ccproxy-api 0.1.0__tar.gz → 0.1.1__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 (272) hide show
  1. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.github/workflows/release.yml +3 -0
  2. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/CHANGELOG.md +28 -0
  3. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/PKG-INFO +62 -7
  4. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/README.md +61 -6
  5. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/_version.py +2 -2
  6. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/app.py +70 -0
  7. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/routes/claude.py +0 -6
  8. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/routes/proxy.py +13 -6
  9. ccproxy_api-0.1.1/ccproxy/auth/conditional.py +84 -0
  10. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/commands/auth.py +59 -43
  11. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/commands/config/commands.py +2 -2
  12. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/commands/serve.py +304 -86
  13. ccproxy_api-0.1.1/ccproxy/cli/main.py +115 -0
  14. ccproxy_api-0.1.1/ccproxy/cli/options/claude_options.py +102 -0
  15. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/options/core_options.py +1 -13
  16. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/options/security_options.py +1 -9
  17. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/options/server_options.py +1 -52
  18. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/docker_settings.py +1 -1
  19. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/pricing.py +5 -6
  20. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/scheduler.py +5 -6
  21. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/settings.py +0 -2
  22. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/async_utils.py +1 -1
  23. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/types.py +3 -9
  24. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/models/messages.py +1 -2
  25. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/models/responses.py +0 -36
  26. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/pricing/models.py +5 -6
  27. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/services/proxy_service.py +6 -7
  28. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/getting-started/configuration.md +5 -5
  29. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/getting-started/quickstart.md +64 -16
  30. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/systemd-setup.md +1 -1
  31. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/user-guide/authentication.md +5 -5
  32. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/user-guide/claude-code-options.md +3 -18
  33. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/pyproject.toml +1 -0
  34. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_api.py +7 -28
  35. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_auth.py +0 -34
  36. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_metrics_api.py +0 -11
  37. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_observability.py +0 -7
  38. ccproxy_api-0.1.0/ccproxy/cli/main.py +0 -193
  39. ccproxy_api-0.1.0/ccproxy/cli/options/claude_options.py +0 -216
  40. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.env.example +0 -0
  41. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.github/workflows/backend.yml +0 -0
  42. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.github/workflows/build.yml +0 -0
  43. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.github/workflows/ci.yml +0 -0
  44. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.github/workflows/docs.yml +0 -0
  45. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.github/workflows/frontend.yml.disabled +0 -0
  46. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.gitignore +0 -0
  47. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.pre-commit-config.yaml +0 -0
  48. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/.python-version +0 -0
  49. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/CONTRIBUTING.md +0 -0
  50. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/CONVENTIONS.md +0 -0
  51. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/Dockerfile +0 -0
  52. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/LICENSE +0 -0
  53. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/Makefile +0 -0
  54. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/TESTING.md +0 -0
  55. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/__init__.py +0 -0
  56. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/__main__.py +0 -0
  57. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/adapters/__init__.py +0 -0
  58. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/adapters/base.py +0 -0
  59. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/adapters/openai/__init__.py +0 -0
  60. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/adapters/openai/adapter.py +0 -0
  61. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/adapters/openai/models.py +0 -0
  62. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/adapters/openai/streaming.py +0 -0
  63. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/__init__.py +0 -0
  64. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/dependencies.py +0 -0
  65. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/middleware/__init__.py +0 -0
  66. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/middleware/auth.py +0 -0
  67. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/middleware/cors.py +0 -0
  68. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/middleware/errors.py +0 -0
  69. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/middleware/headers.py +0 -0
  70. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/middleware/logging.py +0 -0
  71. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/middleware/request_id.py +0 -0
  72. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/middleware/server_header.py +0 -0
  73. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/responses.py +0 -0
  74. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/routes/__init__.py +0 -0
  75. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/routes/health.py +0 -0
  76. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/api/routes/metrics.py +0 -0
  77. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/__init__.py +0 -0
  78. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/bearer.py +0 -0
  79. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/credentials_adapter.py +0 -0
  80. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/dependencies.py +0 -0
  81. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/exceptions.py +0 -0
  82. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/manager.py +0 -0
  83. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/models.py +0 -0
  84. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/oauth/__init__.py +0 -0
  85. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/oauth/models.py +0 -0
  86. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/oauth/routes.py +0 -0
  87. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/oauth/storage.py +0 -0
  88. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/storage/__init__.py +0 -0
  89. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/storage/base.py +0 -0
  90. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/storage/json_file.py +0 -0
  91. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/auth/storage/keyring.py +0 -0
  92. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/claude_sdk/__init__.py +0 -0
  93. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/claude_sdk/client.py +0 -0
  94. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/claude_sdk/converter.py +0 -0
  95. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/claude_sdk/options.py +0 -0
  96. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/__init__.py +0 -0
  97. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/commands/__init__.py +0 -0
  98. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/commands/config/__init__.py +0 -0
  99. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/commands/config/schema_commands.py +0 -0
  100. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/docker/__init__.py +0 -0
  101. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/docker/adapter_factory.py +0 -0
  102. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/docker/params.py +0 -0
  103. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/helpers.py +0 -0
  104. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/cli/options/__init__.py +0 -0
  105. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/__init__.py +0 -0
  106. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/auth.py +2 -2
  107. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/claude.py +0 -0
  108. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/cors.py +0 -0
  109. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/discovery.py +0 -0
  110. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/loader.py +0 -0
  111. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/observability.py +0 -0
  112. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/reverse_proxy.py +0 -0
  113. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/security.py +0 -0
  114. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/server.py +0 -0
  115. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/config/validators.py +0 -0
  116. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/__init__.py +0 -0
  117. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/constants.py +0 -0
  118. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/errors.py +0 -0
  119. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/http.py +0 -0
  120. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/http_transformers.py +0 -0
  121. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/interfaces.py +0 -0
  122. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/logging.py +0 -0
  123. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/middleware.py +0 -0
  124. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/proxy.py +0 -0
  125. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/system.py +0 -0
  126. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/transformers.py +0 -0
  127. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/core/validators.py +0 -0
  128. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/docker/__init__.py +0 -0
  129. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/docker/adapter.py +0 -0
  130. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/docker/docker_path.py +0 -0
  131. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/docker/middleware.py +0 -0
  132. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/docker/models.py +0 -0
  133. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/docker/protocol.py +0 -0
  134. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/docker/stream_process.py +0 -0
  135. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/docker/validators.py +0 -0
  136. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/models/__init__.py +0 -0
  137. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/models/errors.py +0 -0
  138. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/models/requests.py +0 -0
  139. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/models/types.py +0 -0
  140. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/__init__.py +0 -0
  141. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/access_logger.py +0 -0
  142. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/context.py +0 -0
  143. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/metrics.py +0 -0
  144. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/pushgateway.py +0 -0
  145. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/sse_events.py +0 -0
  146. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/stats_printer.py +0 -0
  147. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/storage/__init__.py +0 -0
  148. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/storage/duckdb_simple.py +0 -0
  149. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/observability/storage/models.py +0 -0
  150. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/pricing/__init__.py +0 -0
  151. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/pricing/cache.py +0 -0
  152. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/pricing/loader.py +0 -0
  153. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/pricing/updater.py +0 -0
  154. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/scheduler/__init__.py +0 -0
  155. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/scheduler/core.py +0 -0
  156. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/scheduler/exceptions.py +0 -0
  157. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/scheduler/manager.py +0 -0
  158. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/scheduler/registry.py +0 -0
  159. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/scheduler/tasks.py +0 -0
  160. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/services/__init__.py +0 -0
  161. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/services/claude_sdk_service.py +0 -0
  162. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/services/credentials/__init__.py +0 -0
  163. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/services/credentials/config.py +0 -0
  164. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/services/credentials/manager.py +0 -0
  165. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/services/credentials/oauth_client.py +0 -0
  166. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/static/.keep +0 -0
  167. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/testing/__init__.py +0 -0
  168. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/testing/config.py +0 -0
  169. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/testing/content_generation.py +0 -0
  170. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/testing/mock_responses.py +0 -0
  171. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/testing/response_handlers.py +0 -0
  172. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/testing/scenarios.py +0 -0
  173. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/utils/__init__.py +0 -0
  174. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/utils/cost_calculator.py +0 -0
  175. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/ccproxy/utils/streaming_metrics.py +0 -0
  176. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/data/metrics.db +0 -0
  177. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/devenv.lock +0 -0
  178. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/devenv.nix +0 -0
  179. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docker-compose.monitoring.yml +0 -0
  180. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docker-compose.yml +0 -0
  181. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/README.md +0 -0
  182. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/api-reference.md +0 -0
  183. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/assets/extra.css +0 -0
  184. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/assets/extra.js +0 -0
  185. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/contributing.md +0 -0
  186. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/development/debugging-with-proxy.md +0 -0
  187. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/examples.md +0 -0
  188. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/gen_ref_pages.py +0 -0
  189. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/getting-started/installation.md +0 -0
  190. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/index.md +0 -0
  191. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/metrics-api.md +0 -0
  192. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/observability.md +0 -0
  193. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/user-guide/api-usage.md +0 -0
  194. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/user-guide/claude-sdk-compatibility.md +0 -0
  195. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/user-guide/mcp-integration.md +0 -0
  196. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/user-guide/pool-configuration.md +0 -0
  197. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/docs/user-guide/understanding-pool-logs.md +0 -0
  198. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/README.md +0 -0
  199. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/README_chat_agent.md +0 -0
  200. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/ai_code_discussion_demo.py +0 -0
  201. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/anthropic_streaming_demo.py +0 -0
  202. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/anthropic_tools_demo.py +0 -0
  203. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/client.py +0 -0
  204. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/common_utils.py +0 -0
  205. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/console_utils.py +0 -0
  206. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/openai_anthropic_conversation_demo.py +0 -0
  207. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/openai_claude_code_options_example.sh +0 -0
  208. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/openai_json_object_example.sh +0 -0
  209. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/openai_json_schema_example.sh +0 -0
  210. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/openai_streaming_demo.py +0 -0
  211. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/openai_structured_response_demo.py +0 -0
  212. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/openai_thinking_demo.py +0 -0
  213. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/openai_tools_demo.py +0 -0
  214. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/project_code_access_demo.py +0 -0
  215. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/simple_thinking_demo.py +0 -0
  216. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/examples/textual_chat_agent.py +0 -0
  217. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/mkdocs.yml +0 -0
  218. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/monitoring/grafana/dashboards/ccproxy-dashboard.json +0 -0
  219. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/monitoring/grafana/provisioning/dashboards/dashboard.yml +0 -0
  220. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/monitoring/grafana/provisioning/datasources/victoria-metrics.yml +0 -0
  221. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/act-run.sh +0 -0
  222. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/benchmark_startup_node.py +0 -0
  223. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/build-docs.sh +0 -0
  224. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/entrypoint.sh +0 -0
  225. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/format_version.py +0 -0
  226. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/serve-docs.sh +0 -0
  227. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/setup-systemd.sh +0 -0
  228. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/setup.sh +0 -0
  229. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/scripts/traffic_generator.py +0 -0
  230. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/systemd/ccproxy.service.template +0 -0
  231. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/test_config/test-config.toml +0 -0
  232. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/__init__.py +0 -0
  233. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/conftest.py +0 -0
  234. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/factories/MIGRATION_GUIDE.md +0 -0
  235. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/factories/README.md +0 -0
  236. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/factories/__init__.py +0 -0
  237. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/factories/fastapi_factory.py +0 -0
  238. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/MIGRATION_GUIDE.md +0 -0
  239. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/README.md +0 -0
  240. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/auth/MIGRATION_GUIDE.md +0 -0
  241. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/auth/__init__.py +0 -0
  242. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/auth/example_usage.py +0 -0
  243. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/claude_sdk/__init__.py +0 -0
  244. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/claude_sdk/internal_mocks.py +0 -0
  245. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/claude_sdk/responses.py +0 -0
  246. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/credentials.json +0 -0
  247. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/external_apis/__init__.py +0 -0
  248. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/external_apis/anthropic_api.py +0 -0
  249. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/proxy_service/__init__.py +0 -0
  250. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/proxy_service/oauth_mocks.py +0 -0
  251. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/fixtures/responses.json +0 -0
  252. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_access_logger_integration.py +0 -0
  253. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_adapters.py +0 -0
  254. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_cli_auth_commands.py +0 -0
  255. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_cli_config.py +0 -0
  256. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_cli_serve.py +0 -0
  257. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_docker.py +0 -0
  258. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_duckdb_lifecycle.py +0 -0
  259. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_duckdb_settings_integration.py +0 -0
  260. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_fastapi_factory.py +0 -0
  261. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_http_transformers.py +0 -0
  262. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_pricing.py +0 -0
  263. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_pushgateway_error_handling.py +0 -0
  264. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_queue_duckdb_storage.py +0 -0
  265. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_reset_endpoint.py +0 -0
  266. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_scheduler.py +0 -0
  267. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_scheduler_tasks.py +0 -0
  268. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_sse_events.py +0 -0
  269. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_sse_stream_filtering.py +0 -0
  270. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_stats_printer.py +0 -0
  271. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/tests/test_streaming.py +0 -0
  272. {ccproxy_api-0.1.0 → ccproxy_api-0.1.1}/uv.lock +0 -0
@@ -130,6 +130,9 @@ jobs:
130
130
  needs: [build-package, build-release-docker]
131
131
  runs-on: ubuntu-latest
132
132
 
133
+ permissions:
134
+ id-token: write
135
+ contents: write
133
136
  steps:
134
137
  - uses: actions/checkout@v4
135
138
 
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.1] - 2025-07-22
9
+
10
+ ### Added
11
+ - **Conditional Authentication**: API endpoints now support optional authentication - when `SECURITY__AUTH_TOKEN` is configured, authentication is enforced; when not configured, the proxy runs in open mode.
12
+ - **Startup Validation**: Added comprehensive validation checks during application startup:
13
+ - Validates OAuth credentials and warns about expired tokens
14
+ - Checks for Claude CLI binary availability with installation instructions
15
+ - Logs token expiration time and subscription type when valid
16
+ - **Default Command**: The `serve` command is now the default - running `ccproxy` without subcommands automatically starts the server.
17
+ - **Alternative Entry Point**: Added `ccproxy-api` as an alternative command-line entry point.
18
+
19
+ ### Changed
20
+ - **Authentication Variable**: Renamed environment variable from `AUTH_TOKEN` to `SECURITY__AUTH_TOKEN` for better namespace organization and clarity.
21
+ - **Credential Priority**: Reordered credential search paths to prioritize ccproxy-specific credentials before Claude CLI paths.
22
+ - **CLI Syntax**: Migrated all CLI parameters to modern Annotated syntax for better type safety and IDE support.
23
+ - **Pydantic v2**: Updated all models to use Pydantic v2 configuration syntax (`model_config` instead of `Config` class).
24
+ - **Documentation**: Improved Aider integration docs with correct API endpoint URLs and added installation options (uv, pipx).
25
+
26
+ ### Fixed
27
+ - **Authentication Separation**: Fixed critical issue where auth token was incorrectly used for both client and upstream authentication - now client auth token is separate from OAuth credentials.
28
+ - **URL Paths**: Fixed documentation to use `/api` endpoints for Aider compatibility instead of SDK mode paths.
29
+ - **Default Values**: Fixed default values for list parameters in CLI (docker_env, docker_volume, docker_arg).
30
+
31
+ ### Removed
32
+ - **Status Endpoints**: Removed redundant `/status` endpoints from both Claude SDK and proxy routes.
33
+ - **Permission Tool**: Removed Claude permission tool functionality and related CLI options (`--permission-mode`, `--permission-prompt-tool-name`) that are no longer needed.
34
+ - **Deprecated Options**: Removed references to deprecated permission_mode and permission_prompt_tool_name from documentation.
35
+
8
36
  ## [0.1.0] - 2025-07-21
9
37
 
10
38
  This is the initial public release of the CCProxy API.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccproxy-api
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: API server that provides an Anthropic and OpenAI compatible interface over Claude Code, allowing to use your Claude OAuth account or over the API.
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -43,14 +43,26 @@ It includes a translation layer to support both Anthropic and OpenAI-compatible
43
43
  # The official claude-code CLI is required for SDK mode
44
44
  npm install -g @anthropic-ai/claude-code
45
45
 
46
- # Install ccproxy
47
- pipx install git+https://github.com/caddyglow/ccproxy-api.git@dev
46
+ # run it with uv
47
+ uvx ccproxy-api
48
+
49
+ # run it with pipx
50
+ pipx run ccproxy-api
51
+
52
+ # install with uv
53
+ uv tool install ccproxy-api
54
+
55
+ # Install ccproxy with pip
56
+ pipx install ccproxy-api
48
57
 
49
58
  # Optional: Enable shell completion
50
59
  eval "$(ccproxy --show-completion zsh)" # For zsh
51
60
  eval "$(ccproxy --show-completion bash)" # For bash
52
61
  ```
53
62
 
63
+
64
+ For dev version replace `ccproxy-api` with `git+https://github.com/caddyglow/ccproxy-api.git@dev`
65
+
54
66
  ## Authentication
55
67
 
56
68
  The proxy uses two different authentication mechanisms depending on the mode.
@@ -61,12 +73,23 @@ The proxy uses two different authentication mechanisms depending on the mode.
61
73
  claude /login
62
74
  ```
63
75
 
76
+ It's also possible now to get a long live token to avoid renewing issues
77
+ using
78
+ ```sh
79
+ ```bash
80
+ claude setup-token`
81
+
64
82
  2. **ccproxy (`api` mode):**
65
83
  This mode uses its own OAuth2 flow to obtain credentials for direct API access.
66
84
  ```bash
67
85
  ccproxy auth login
68
86
  ```
69
- You can check the status of these credentials with `ccproxy auth validate` and `ccproxy auth info`.
87
+
88
+ If you are already connected with Claude CLI the credentials should be found automatically
89
+
90
+ You can check the status of these credentials with `ccproxy auth validate` and `ccproxy auth info`.
91
+
92
+ Warning is show on start up if no credentials are setup.
70
93
 
71
94
  ## Usage
72
95
 
@@ -102,6 +125,38 @@ export ANTHROPIC_BASE_URL="http://localhost:8000/api"
102
125
  export ANTHROPIC_API_KEY="dummy-key"
103
126
  ```
104
127
 
128
+
129
+ ## Using with Aider
130
+
131
+ CCProxy works seamlessly with Aider and other AI coding assistants:
132
+
133
+ ### Anthropic Mode
134
+ ```bash
135
+ export ANTHROPIC_API_KEY=dummy
136
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:8000/api
137
+ aider --model claude-sonnet-4-20250514
138
+ ```
139
+
140
+ ### OpenAI Mode with Model Mapping
141
+
142
+ If your tool only supports OpenAI settings, ccproxy automatically maps OpenAI models to Claude:
143
+
144
+ ```bash
145
+ export OPENAI_API_KEY=dummy
146
+ export OPENAI_BASE_URL=http://127.0.0.1:8000/api/v1
147
+ aider --model o3-mini
148
+ ```
149
+
150
+ ### API Mode (Direct Proxy)
151
+
152
+ For minimal interference and direct API access:
153
+
154
+ ```bash
155
+ export OPENAI_API_KEY=dummy
156
+ export OPENAI_BASE_URL=http://127.0.0.1:8000/api/v1
157
+ aider --model o3-mini
158
+ ```
159
+
105
160
  ### `curl` Example
106
161
 
107
162
  ```bash
@@ -185,16 +240,16 @@ You can enable token authentication for the proxy. This supports multiple header
185
240
  **1. Generate a Token:**
186
241
  ```bash
187
242
  ccproxy generate-token
188
- # Output: AUTH_TOKEN=abc123xyz789...
243
+ # Output: SECURITY__AUTH_TOKEN=abc123xyz789...
189
244
  ```
190
245
 
191
246
  **2. Configure the Token:**
192
247
  ```bash
193
248
  # Set environment variable
194
- export AUTH_TOKEN=abc123xyz789...
249
+ export SECURITY__AUTH_TOKEN=abc123xyz789...
195
250
 
196
251
  # Or add to .env file
197
- echo "AUTH_TOKEN=abc123xyz789..." >> .env
252
+ echo "SECURITY__AUTH_TOKEN=abc123xyz789..." >> .env
198
253
  ```
199
254
 
200
255
  **3. Use in Requests:**
@@ -14,14 +14,26 @@ It includes a translation layer to support both Anthropic and OpenAI-compatible
14
14
  # The official claude-code CLI is required for SDK mode
15
15
  npm install -g @anthropic-ai/claude-code
16
16
 
17
- # Install ccproxy
18
- pipx install git+https://github.com/caddyglow/ccproxy-api.git@dev
17
+ # run it with uv
18
+ uvx ccproxy-api
19
+
20
+ # run it with pipx
21
+ pipx run ccproxy-api
22
+
23
+ # install with uv
24
+ uv tool install ccproxy-api
25
+
26
+ # Install ccproxy with pip
27
+ pipx install ccproxy-api
19
28
 
20
29
  # Optional: Enable shell completion
21
30
  eval "$(ccproxy --show-completion zsh)" # For zsh
22
31
  eval "$(ccproxy --show-completion bash)" # For bash
23
32
  ```
24
33
 
34
+
35
+ For dev version replace `ccproxy-api` with `git+https://github.com/caddyglow/ccproxy-api.git@dev`
36
+
25
37
  ## Authentication
26
38
 
27
39
  The proxy uses two different authentication mechanisms depending on the mode.
@@ -32,12 +44,23 @@ The proxy uses two different authentication mechanisms depending on the mode.
32
44
  claude /login
33
45
  ```
34
46
 
47
+ It's also possible now to get a long live token to avoid renewing issues
48
+ using
49
+ ```sh
50
+ ```bash
51
+ claude setup-token`
52
+
35
53
  2. **ccproxy (`api` mode):**
36
54
  This mode uses its own OAuth2 flow to obtain credentials for direct API access.
37
55
  ```bash
38
56
  ccproxy auth login
39
57
  ```
40
- You can check the status of these credentials with `ccproxy auth validate` and `ccproxy auth info`.
58
+
59
+ If you are already connected with Claude CLI the credentials should be found automatically
60
+
61
+ You can check the status of these credentials with `ccproxy auth validate` and `ccproxy auth info`.
62
+
63
+ Warning is show on start up if no credentials are setup.
41
64
 
42
65
  ## Usage
43
66
 
@@ -73,6 +96,38 @@ export ANTHROPIC_BASE_URL="http://localhost:8000/api"
73
96
  export ANTHROPIC_API_KEY="dummy-key"
74
97
  ```
75
98
 
99
+
100
+ ## Using with Aider
101
+
102
+ CCProxy works seamlessly with Aider and other AI coding assistants:
103
+
104
+ ### Anthropic Mode
105
+ ```bash
106
+ export ANTHROPIC_API_KEY=dummy
107
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:8000/api
108
+ aider --model claude-sonnet-4-20250514
109
+ ```
110
+
111
+ ### OpenAI Mode with Model Mapping
112
+
113
+ If your tool only supports OpenAI settings, ccproxy automatically maps OpenAI models to Claude:
114
+
115
+ ```bash
116
+ export OPENAI_API_KEY=dummy
117
+ export OPENAI_BASE_URL=http://127.0.0.1:8000/api/v1
118
+ aider --model o3-mini
119
+ ```
120
+
121
+ ### API Mode (Direct Proxy)
122
+
123
+ For minimal interference and direct API access:
124
+
125
+ ```bash
126
+ export OPENAI_API_KEY=dummy
127
+ export OPENAI_BASE_URL=http://127.0.0.1:8000/api/v1
128
+ aider --model o3-mini
129
+ ```
130
+
76
131
  ### `curl` Example
77
132
 
78
133
  ```bash
@@ -156,16 +211,16 @@ You can enable token authentication for the proxy. This supports multiple header
156
211
  **1. Generate a Token:**
157
212
  ```bash
158
213
  ccproxy generate-token
159
- # Output: AUTH_TOKEN=abc123xyz789...
214
+ # Output: SECURITY__AUTH_TOKEN=abc123xyz789...
160
215
  ```
161
216
 
162
217
  **2. Configure the Token:**
163
218
  ```bash
164
219
  # Set environment variable
165
- export AUTH_TOKEN=abc123xyz789...
220
+ export SECURITY__AUTH_TOKEN=abc123xyz789...
166
221
 
167
222
  # Or add to .env file
168
- echo "AUTH_TOKEN=abc123xyz789..." >> .env
223
+ echo "SECURITY__AUTH_TOKEN=abc123xyz789..." >> .env
169
224
  ```
170
225
 
171
226
  **3. Use in Requests:**
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1.0'
21
- __version_tuple__ = version_tuple = (0, 1, 0)
20
+ __version__ = version = '0.1.1'
21
+ __version_tuple__ = version_tuple = (0, 1, 1)
@@ -2,6 +2,7 @@
2
2
 
3
3
  from collections.abc import AsyncGenerator
4
4
  from contextlib import asynccontextmanager
5
+ from datetime import UTC, datetime
5
6
  from typing import Any
6
7
 
7
8
  from fastapi import FastAPI, HTTPException
@@ -23,11 +24,13 @@ from ccproxy.api.routes.metrics import (
23
24
  prometheus_router,
24
25
  )
25
26
  from ccproxy.api.routes.proxy import router as proxy_router
27
+ from ccproxy.auth.exceptions import CredentialsNotFoundError
26
28
  from ccproxy.auth.oauth.routes import router as oauth_router
27
29
  from ccproxy.config.settings import Settings, get_settings
28
30
  from ccproxy.core.logging import setup_logging
29
31
  from ccproxy.observability.storage.duckdb_simple import SimpleDuckDBStorage
30
32
  from ccproxy.scheduler.manager import start_scheduler, stop_scheduler
33
+ from ccproxy.services.credentials import CredentialsManager
31
34
 
32
35
 
33
36
  logger = get_logger(__name__)
@@ -58,6 +61,73 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
58
61
  "claude_cli_search_paths", paths=settings.claude.get_searched_paths()
59
62
  )
60
63
 
64
+ # Validate authentication token at startup
65
+ try:
66
+ credentials_manager = CredentialsManager()
67
+ validation = await credentials_manager.validate()
68
+
69
+ if validation.valid and not validation.expired:
70
+ credentials = validation.credentials
71
+ oauth_token = credentials.claude_ai_oauth if credentials else None
72
+
73
+ if oauth_token and oauth_token.expires_at_datetime:
74
+ hours_until_expiry = int(
75
+ (
76
+ oauth_token.expires_at_datetime - datetime.now(UTC)
77
+ ).total_seconds()
78
+ / 3600
79
+ )
80
+ logger.info(
81
+ "auth_token_valid",
82
+ expires_in_hours=hours_until_expiry,
83
+ subscription_type=oauth_token.subscription_type,
84
+ credentials_path=str(validation.path) if validation.path else None,
85
+ )
86
+ else:
87
+ logger.info("auth_token_valid", credentials_path=str(validation.path))
88
+ elif validation.expired:
89
+ logger.warning(
90
+ "auth_token_expired",
91
+ message="Authentication token has expired. Please run 'ccproxy auth login' to refresh.",
92
+ credentials_path=str(validation.path) if validation.path else None,
93
+ )
94
+ else:
95
+ logger.warning(
96
+ "auth_token_invalid",
97
+ message="Authentication token is invalid. Please run 'ccproxy auth login'.",
98
+ credentials_path=str(validation.path) if validation.path else None,
99
+ )
100
+ except CredentialsNotFoundError:
101
+ logger.warning(
102
+ "auth_token_not_found",
103
+ message="No authentication credentials found. Please run 'ccproxy auth login' to authenticate.",
104
+ searched_paths=settings.auth.storage.storage_paths,
105
+ )
106
+ except Exception as e:
107
+ logger.error(
108
+ "auth_token_validation_error",
109
+ error=str(e),
110
+ message="Failed to validate authentication token. The server will continue without authentication.",
111
+ )
112
+
113
+ # Validate Claude binary at startup
114
+ claude_path, found_in_path = settings.claude.find_claude_cli()
115
+ if claude_path:
116
+ logger.info(
117
+ "claude_binary_found",
118
+ path=claude_path,
119
+ found_in_path=found_in_path,
120
+ message=f"Claude CLI binary found at: {claude_path}",
121
+ )
122
+ else:
123
+ searched_paths = settings.claude.get_searched_paths()
124
+ logger.warning(
125
+ "claude_binary_not_found",
126
+ message="Claude CLI binary not found. Please install Claude CLI to use SDK features.",
127
+ searched_paths=searched_paths,
128
+ install_command="npm install -g @anthropic-ai/claude-code",
129
+ )
130
+
61
131
  # Start scheduler system
62
132
  try:
63
133
  scheduler = await start_scheduler(settings)
@@ -173,9 +173,3 @@ async def list_models(
173
173
  raise HTTPException(
174
174
  status_code=500, detail=f"Internal server error: {str(e)}"
175
175
  ) from e
176
-
177
-
178
- @router.get("/status")
179
- async def claude_sdk_status() -> dict[str, str]:
180
- """Get Claude SDK status."""
181
- return {"status": "claude sdk endpoint available", "service": "direct"}
@@ -11,6 +11,7 @@ from starlette.background import BackgroundTask
11
11
  from ccproxy.adapters.openai.adapter import OpenAIAdapter
12
12
  from ccproxy.api.dependencies import ProxyServiceDep
13
13
  from ccproxy.api.responses import ProxyResponse
14
+ from ccproxy.auth.conditional import ConditionalAuthDep
14
15
  from ccproxy.core.errors import ProxyHTTPException
15
16
 
16
17
 
@@ -22,6 +23,7 @@ router = APIRouter(tags=["proxy"])
22
23
  async def create_openai_chat_completion(
23
24
  request: Request,
24
25
  proxy_service: ProxyServiceDep,
26
+ auth: ConditionalAuthDep,
25
27
  ) -> StreamingResponse | Response:
26
28
  """Create a chat completion using Claude AI with OpenAI-compatible format.
27
29
 
@@ -98,6 +100,9 @@ async def create_openai_chat_completion(
98
100
  media_type=response_headers.get("content-type", "application/json"),
99
101
  )
100
102
 
103
+ except HTTPException:
104
+ # Re-raise HTTPException as-is (including 401 auth errors)
105
+ raise
101
106
  except Exception as e:
102
107
  raise HTTPException(
103
108
  status_code=500, detail=f"Internal server error: {str(e)}"
@@ -108,6 +113,7 @@ async def create_openai_chat_completion(
108
113
  async def create_anthropic_message(
109
114
  request: Request,
110
115
  proxy_service: ProxyServiceDep,
116
+ auth: ConditionalAuthDep,
111
117
  ) -> StreamingResponse | Response:
112
118
  """Create a message using Claude AI with Anthropic format.
113
119
 
@@ -180,6 +186,9 @@ async def create_anthropic_message(
180
186
  media_type=response_headers.get("content-type", "application/json"),
181
187
  )
182
188
 
189
+ except HTTPException:
190
+ # Re-raise HTTPException as-is (including 401 auth errors)
191
+ raise
183
192
  except Exception as e:
184
193
  raise HTTPException(
185
194
  status_code=500, detail=f"Internal server error: {str(e)}"
@@ -190,6 +199,7 @@ async def create_anthropic_message(
190
199
  async def list_models(
191
200
  request: Request,
192
201
  proxy_service: ProxyServiceDep,
202
+ auth: ConditionalAuthDep,
193
203
  ) -> Response:
194
204
  """List available models using the proxy service.
195
205
 
@@ -226,13 +236,10 @@ async def list_models(
226
236
  media_type=response_headers.get("content-type", "application/json"),
227
237
  )
228
238
 
239
+ except HTTPException:
240
+ # Re-raise HTTPException as-is (including 401 auth errors)
241
+ raise
229
242
  except Exception as e:
230
243
  raise HTTPException(
231
244
  status_code=500, detail=f"Internal server error: {str(e)}"
232
245
  ) from e
233
-
234
-
235
- @router.get("/status")
236
- async def proxy_status() -> dict[str, str]:
237
- """Get proxy status."""
238
- return {"status": "proxy API available", "version": "1.0.0"}
@@ -0,0 +1,84 @@
1
+ """Conditional authentication dependencies."""
2
+
3
+ from typing import Annotated
4
+
5
+ from fastapi import Depends, HTTPException, Request, status
6
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
7
+
8
+ from ccproxy.auth.bearer import BearerTokenAuthManager
9
+ from ccproxy.auth.exceptions import AuthenticationError
10
+ from ccproxy.auth.manager import AuthManager
11
+ from ccproxy.config.settings import Settings, get_settings
12
+
13
+
14
+ # FastAPI security scheme for bearer tokens
15
+ bearer_scheme = HTTPBearer(auto_error=False)
16
+
17
+
18
+ async def get_conditional_auth_manager(
19
+ request: Request,
20
+ credentials: Annotated[
21
+ HTTPAuthorizationCredentials | None, Depends(bearer_scheme)
22
+ ] = None,
23
+ settings: Annotated[Settings | None, Depends(get_settings)] = None,
24
+ ) -> AuthManager | None:
25
+ """Get authentication manager only if auth is required.
26
+
27
+ This dependency checks if authentication is configured and validates
28
+ the token if required. If no auth is configured, returns None.
29
+
30
+ Args:
31
+ request: The FastAPI request object
32
+ credentials: HTTP authorization credentials
33
+ settings: Application settings
34
+
35
+ Returns:
36
+ AuthManager instance if authenticated, None if no auth required
37
+
38
+ Raises:
39
+ HTTPException: If auth is required but credentials are invalid
40
+ """
41
+ # Check if auth is required for this configuration
42
+ if settings is None or not settings.security.auth_token:
43
+ # No auth configured, return None
44
+ return None
45
+
46
+ # Auth is required, validate credentials
47
+ if not credentials or not credentials.credentials:
48
+ raise HTTPException(
49
+ status_code=status.HTTP_401_UNAUTHORIZED,
50
+ detail="Authentication required",
51
+ headers={"WWW-Authenticate": "Bearer"},
52
+ )
53
+
54
+ # Validate the token
55
+ if credentials.credentials != settings.security.auth_token:
56
+ raise HTTPException(
57
+ status_code=status.HTTP_401_UNAUTHORIZED,
58
+ detail="Invalid authentication credentials",
59
+ headers={"WWW-Authenticate": "Bearer"},
60
+ )
61
+
62
+ # Create and return auth manager
63
+ try:
64
+ bearer_auth = BearerTokenAuthManager(credentials.credentials)
65
+ if await bearer_auth.is_authenticated():
66
+ return bearer_auth
67
+ else:
68
+ raise HTTPException(
69
+ status_code=status.HTTP_401_UNAUTHORIZED,
70
+ detail="Authentication failed",
71
+ headers={"WWW-Authenticate": "Bearer"},
72
+ )
73
+ except (AuthenticationError, ValueError) as e:
74
+ raise HTTPException(
75
+ status_code=status.HTTP_401_UNAUTHORIZED,
76
+ detail=str(e),
77
+ headers={"WWW-Authenticate": "Bearer"},
78
+ ) from e
79
+
80
+
81
+ # Type alias for conditional auth dependency
82
+ ConditionalAuthDep = Annotated[
83
+ AuthManager | None, Depends(get_conditional_auth_manager)
84
+ ]
@@ -4,7 +4,7 @@ import asyncio
4
4
  import json
5
5
  from datetime import UTC, datetime, timezone
6
6
  from pathlib import Path
7
- from typing import Optional
7
+ from typing import Annotated, Optional
8
8
 
9
9
  import typer
10
10
  from rich import box
@@ -52,16 +52,20 @@ def get_docker_credential_paths() -> list[Path]:
52
52
 
53
53
  @app.command(name="validate")
54
54
  def validate_credentials(
55
- docker: bool = typer.Option(
56
- False,
57
- "--docker",
58
- help="Use Docker credential paths (from get_claude_docker_home_dir())",
59
- ),
60
- credential_file: str | None = typer.Option(
61
- None,
62
- "--credential-file",
63
- help="Path to specific credential file to validate",
64
- ),
55
+ docker: Annotated[
56
+ bool,
57
+ typer.Option(
58
+ "--docker",
59
+ help="Use Docker credential paths (from get_claude_docker_home_dir())",
60
+ ),
61
+ ] = False,
62
+ credential_file: Annotated[
63
+ str | None,
64
+ typer.Option(
65
+ "--credential-file",
66
+ help="Path to specific credential file to validate",
67
+ ),
68
+ ] = None,
65
69
  ) -> None:
66
70
  """Validate Claude CLI credentials.
67
71
 
@@ -176,16 +180,20 @@ def validate_credentials(
176
180
 
177
181
  @app.command(name="info")
178
182
  def credential_info(
179
- docker: bool = typer.Option(
180
- False,
181
- "--docker",
182
- help="Use Docker credential paths (from get_claude_docker_home_dir())",
183
- ),
184
- credential_file: str | None = typer.Option(
185
- None,
186
- "--credential-file",
187
- help="Path to specific credential file to display info for",
188
- ),
183
+ docker: Annotated[
184
+ bool,
185
+ typer.Option(
186
+ "--docker",
187
+ help="Use Docker credential paths (from get_claude_docker_home_dir())",
188
+ ),
189
+ ] = False,
190
+ credential_file: Annotated[
191
+ str | None,
192
+ typer.Option(
193
+ "--credential-file",
194
+ help="Path to specific credential file to display info for",
195
+ ),
196
+ ] = None,
189
197
  ) -> None:
190
198
  """Display detailed credential information.
191
199
 
@@ -376,16 +384,20 @@ def credential_info(
376
384
 
377
385
  @app.command(name="login")
378
386
  def login_command(
379
- docker: bool = typer.Option(
380
- False,
381
- "--docker",
382
- help="Use Docker credential paths (from get_claude_docker_home_dir())",
383
- ),
384
- credential_file: str | None = typer.Option(
385
- None,
386
- "--credential-file",
387
- help="Path to specific credential file to save to",
388
- ),
387
+ docker: Annotated[
388
+ bool,
389
+ typer.Option(
390
+ "--docker",
391
+ help="Use Docker credential paths (from get_claude_docker_home_dir())",
392
+ ),
393
+ ] = False,
394
+ credential_file: Annotated[
395
+ str | None,
396
+ typer.Option(
397
+ "--credential-file",
398
+ help="Path to specific credential file to save to",
399
+ ),
400
+ ] = None,
389
401
  ) -> None:
390
402
  """Login to Claude using OAuth authentication.
391
403
 
@@ -470,18 +482,22 @@ def login_command(
470
482
 
471
483
  @app.command()
472
484
  def renew(
473
- docker: bool = typer.Option(
474
- False,
475
- "--docker",
476
- "-d",
477
- help="Renew credentials for Docker environment",
478
- ),
479
- credential_file: Path | None = typer.Option(
480
- None,
481
- "--credential-file",
482
- "-f",
483
- help="Path to custom credential file",
484
- ),
485
+ docker: Annotated[
486
+ bool,
487
+ typer.Option(
488
+ "--docker",
489
+ "-d",
490
+ help="Renew credentials for Docker environment",
491
+ ),
492
+ ] = False,
493
+ credential_file: Annotated[
494
+ Path | None,
495
+ typer.Option(
496
+ "--credential-file",
497
+ "-f",
498
+ help="Path to custom credential file",
499
+ ),
500
+ ] = None,
485
501
  ) -> None:
486
502
  """Force renew Claude credentials without checking expiration.
487
503