ccproxy-api 0.1.0__tar.gz → 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 (275) hide show
  1. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/release.yml +3 -0
  2. ccproxy_api-0.1.2/CHANGELOG.md +90 -0
  3. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/PKG-INFO +62 -7
  4. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/README.md +61 -6
  5. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/_version.py +2 -2
  6. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/app.py +70 -0
  7. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/dependencies.py +3 -0
  8. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/claude.py +0 -6
  9. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/proxy.py +13 -6
  10. ccproxy_api-0.1.2/ccproxy/auth/conditional.py +84 -0
  11. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/claude_sdk/converter.py +1 -1
  12. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/claude_sdk/options.py +23 -1
  13. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/__init__.py +2 -1
  14. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/auth.py +59 -43
  15. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/config/commands.py +2 -2
  16. ccproxy_api-0.1.2/ccproxy/cli/commands/permission.py +128 -0
  17. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/serve.py +322 -82
  18. ccproxy_api-0.1.2/ccproxy/cli/main.py +115 -0
  19. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/claude_options.py +1 -93
  20. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/core_options.py +1 -13
  21. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/security_options.py +1 -9
  22. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/server_options.py +1 -52
  23. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/docker_settings.py +1 -1
  24. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/pricing.py +5 -6
  25. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/scheduler.py +5 -6
  26. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/async_utils.py +1 -1
  27. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/types.py +3 -9
  28. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/messages.py +1 -2
  29. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/models.py +5 -6
  30. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/claude_sdk_service.py +5 -1
  31. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/proxy_service.py +6 -7
  32. ccproxy_api-0.1.2/docs/examples.md +384 -0
  33. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/getting-started/configuration.md +5 -5
  34. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/getting-started/quickstart.md +64 -16
  35. ccproxy_api-0.1.2/docs/index.md +279 -0
  36. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/systemd-setup.md +1 -1
  37. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/authentication.md +5 -5
  38. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/claude-code-options.md +3 -18
  39. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/pyproject.toml +2 -0
  40. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_api.py +7 -28
  41. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_auth.py +0 -34
  42. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_metrics_api.py +0 -11
  43. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_observability.py +0 -7
  44. ccproxy_api-0.1.0/CHANGELOG.md +0 -47
  45. ccproxy_api-0.1.0/ccproxy/cli/main.py +0 -193
  46. ccproxy_api-0.1.0/docs/examples.md +0 -114
  47. ccproxy_api-0.1.0/docs/index.md +0 -175
  48. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.env.example +0 -0
  49. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/backend.yml +0 -0
  50. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/build.yml +0 -0
  51. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/ci.yml +0 -0
  52. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/docs.yml +0 -0
  53. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/frontend.yml.disabled +0 -0
  54. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.gitignore +0 -0
  55. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.pre-commit-config.yaml +0 -0
  56. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.python-version +0 -0
  57. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/CONTRIBUTING.md +0 -0
  58. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/CONVENTIONS.md +0 -0
  59. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/Dockerfile +0 -0
  60. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/LICENSE +0 -0
  61. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/Makefile +0 -0
  62. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/TESTING.md +0 -0
  63. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/__init__.py +0 -0
  64. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/__main__.py +0 -0
  65. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/__init__.py +0 -0
  66. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/base.py +0 -0
  67. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/openai/__init__.py +0 -0
  68. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/openai/adapter.py +0 -0
  69. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/openai/models.py +0 -0
  70. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/openai/streaming.py +0 -0
  71. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/__init__.py +0 -0
  72. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/__init__.py +0 -0
  73. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/auth.py +0 -0
  74. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/cors.py +0 -0
  75. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/errors.py +0 -0
  76. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/headers.py +0 -0
  77. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/logging.py +0 -0
  78. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/request_id.py +0 -0
  79. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/server_header.py +0 -0
  80. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/responses.py +0 -0
  81. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/__init__.py +0 -0
  82. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/health.py +0 -0
  83. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/metrics.py +0 -0
  84. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/__init__.py +0 -0
  85. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/bearer.py +0 -0
  86. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/credentials_adapter.py +0 -0
  87. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/dependencies.py +0 -0
  88. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/exceptions.py +0 -0
  89. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/manager.py +0 -0
  90. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/models.py +0 -0
  91. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/oauth/__init__.py +0 -0
  92. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/oauth/models.py +0 -0
  93. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/oauth/routes.py +0 -0
  94. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/oauth/storage.py +0 -0
  95. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/storage/__init__.py +0 -0
  96. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/storage/base.py +0 -0
  97. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/storage/json_file.py +0 -0
  98. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/storage/keyring.py +0 -0
  99. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/claude_sdk/__init__.py +0 -0
  100. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/claude_sdk/client.py +0 -0
  101. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/__init__.py +0 -0
  102. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/config/__init__.py +0 -0
  103. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/config/schema_commands.py +0 -0
  104. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/docker/__init__.py +0 -0
  105. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/docker/adapter_factory.py +0 -0
  106. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/docker/params.py +0 -0
  107. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/helpers.py +0 -0
  108. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/__init__.py +0 -0
  109. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/__init__.py +0 -0
  110. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/auth.py +2 -2
  111. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/claude.py +0 -0
  112. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/cors.py +0 -0
  113. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/discovery.py +0 -0
  114. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/loader.py +0 -0
  115. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/observability.py +0 -0
  116. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/reverse_proxy.py +0 -0
  117. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/security.py +0 -0
  118. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/server.py +0 -0
  119. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/settings.py +0 -0
  120. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/validators.py +0 -0
  121. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/__init__.py +0 -0
  122. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/constants.py +0 -0
  123. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/errors.py +0 -0
  124. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/http.py +0 -0
  125. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/http_transformers.py +0 -0
  126. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/interfaces.py +0 -0
  127. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/logging.py +0 -0
  128. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/middleware.py +0 -0
  129. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/proxy.py +0 -0
  130. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/system.py +0 -0
  131. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/transformers.py +0 -0
  132. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/validators.py +0 -0
  133. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/__init__.py +0 -0
  134. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/adapter.py +0 -0
  135. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/docker_path.py +0 -0
  136. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/middleware.py +0 -0
  137. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/models.py +0 -0
  138. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/protocol.py +0 -0
  139. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/stream_process.py +0 -0
  140. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/validators.py +0 -0
  141. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/__init__.py +0 -0
  142. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/errors.py +0 -0
  143. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/requests.py +0 -0
  144. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/responses.py +0 -0
  145. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/types.py +0 -0
  146. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/__init__.py +0 -0
  147. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/access_logger.py +0 -0
  148. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/context.py +0 -0
  149. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/metrics.py +0 -0
  150. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/pushgateway.py +0 -0
  151. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/sse_events.py +0 -0
  152. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/stats_printer.py +0 -0
  153. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/storage/__init__.py +0 -0
  154. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/storage/duckdb_simple.py +0 -0
  155. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/storage/models.py +0 -0
  156. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/__init__.py +0 -0
  157. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/cache.py +0 -0
  158. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/loader.py +0 -0
  159. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/updater.py +0 -0
  160. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/__init__.py +0 -0
  161. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/core.py +0 -0
  162. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/exceptions.py +0 -0
  163. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/manager.py +0 -0
  164. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/registry.py +0 -0
  165. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/tasks.py +0 -0
  166. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/__init__.py +0 -0
  167. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/credentials/__init__.py +0 -0
  168. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/credentials/config.py +0 -0
  169. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/credentials/manager.py +0 -0
  170. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/credentials/oauth_client.py +0 -0
  171. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/static/.keep +0 -0
  172. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/__init__.py +0 -0
  173. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/config.py +0 -0
  174. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/content_generation.py +0 -0
  175. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/mock_responses.py +0 -0
  176. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/response_handlers.py +0 -0
  177. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/scenarios.py +0 -0
  178. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/utils/__init__.py +0 -0
  179. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/utils/cost_calculator.py +0 -0
  180. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/utils/streaming_metrics.py +0 -0
  181. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/data/metrics.db +0 -0
  182. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/devenv.lock +0 -0
  183. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/devenv.nix +0 -0
  184. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docker-compose.monitoring.yml +0 -0
  185. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docker-compose.yml +0 -0
  186. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/README.md +0 -0
  187. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/api-reference.md +0 -0
  188. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/assets/extra.css +0 -0
  189. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/assets/extra.js +0 -0
  190. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/contributing.md +0 -0
  191. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/development/debugging-with-proxy.md +0 -0
  192. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/gen_ref_pages.py +0 -0
  193. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/getting-started/installation.md +0 -0
  194. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/metrics-api.md +0 -0
  195. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/observability.md +0 -0
  196. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/api-usage.md +0 -0
  197. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/claude-sdk-compatibility.md +0 -0
  198. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/mcp-integration.md +0 -0
  199. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/pool-configuration.md +0 -0
  200. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/understanding-pool-logs.md +0 -0
  201. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/README.md +0 -0
  202. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/README_chat_agent.md +0 -0
  203. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/ai_code_discussion_demo.py +0 -0
  204. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/anthropic_streaming_demo.py +0 -0
  205. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/anthropic_tools_demo.py +0 -0
  206. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/client.py +0 -0
  207. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/common_utils.py +0 -0
  208. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/console_utils.py +0 -0
  209. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_anthropic_conversation_demo.py +0 -0
  210. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_claude_code_options_example.sh +0 -0
  211. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_json_object_example.sh +0 -0
  212. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_json_schema_example.sh +0 -0
  213. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_streaming_demo.py +0 -0
  214. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_structured_response_demo.py +0 -0
  215. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_thinking_demo.py +0 -0
  216. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_tools_demo.py +0 -0
  217. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/project_code_access_demo.py +0 -0
  218. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/simple_thinking_demo.py +0 -0
  219. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/textual_chat_agent.py +0 -0
  220. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/mkdocs.yml +0 -0
  221. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/monitoring/grafana/dashboards/ccproxy-dashboard.json +0 -0
  222. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/monitoring/grafana/provisioning/dashboards/dashboard.yml +0 -0
  223. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/monitoring/grafana/provisioning/datasources/victoria-metrics.yml +0 -0
  224. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/act-run.sh +0 -0
  225. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/benchmark_startup_node.py +0 -0
  226. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/build-docs.sh +0 -0
  227. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/entrypoint.sh +0 -0
  228. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/format_version.py +0 -0
  229. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/serve-docs.sh +0 -0
  230. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/setup-systemd.sh +0 -0
  231. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/setup.sh +0 -0
  232. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/traffic_generator.py +0 -0
  233. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/systemd/ccproxy.service.template +0 -0
  234. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/test_config/test-config.toml +0 -0
  235. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/__init__.py +0 -0
  236. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/conftest.py +0 -0
  237. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/factories/MIGRATION_GUIDE.md +0 -0
  238. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/factories/README.md +0 -0
  239. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/factories/__init__.py +0 -0
  240. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/factories/fastapi_factory.py +0 -0
  241. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/MIGRATION_GUIDE.md +0 -0
  242. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/README.md +0 -0
  243. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/auth/MIGRATION_GUIDE.md +0 -0
  244. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/auth/__init__.py +0 -0
  245. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/auth/example_usage.py +0 -0
  246. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/claude_sdk/__init__.py +0 -0
  247. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/claude_sdk/internal_mocks.py +0 -0
  248. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/claude_sdk/responses.py +0 -0
  249. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/credentials.json +0 -0
  250. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/external_apis/__init__.py +0 -0
  251. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/external_apis/anthropic_api.py +0 -0
  252. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/proxy_service/__init__.py +0 -0
  253. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/proxy_service/oauth_mocks.py +0 -0
  254. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/responses.json +0 -0
  255. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_access_logger_integration.py +0 -0
  256. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_adapters.py +0 -0
  257. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_cli_auth_commands.py +0 -0
  258. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_cli_config.py +0 -0
  259. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_cli_serve.py +0 -0
  260. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_docker.py +0 -0
  261. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_duckdb_lifecycle.py +0 -0
  262. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_duckdb_settings_integration.py +0 -0
  263. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_fastapi_factory.py +0 -0
  264. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_http_transformers.py +0 -0
  265. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_pricing.py +0 -0
  266. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_pushgateway_error_handling.py +0 -0
  267. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_queue_duckdb_storage.py +0 -0
  268. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_reset_endpoint.py +0 -0
  269. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_scheduler.py +0 -0
  270. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_scheduler_tasks.py +0 -0
  271. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_sse_events.py +0 -0
  272. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_sse_stream_filtering.py +0 -0
  273. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_stats_printer.py +0 -0
  274. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_streaming.py +0 -0
  275. {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/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
 
@@ -0,0 +1,90 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.2] - 2025-07-22
9
+
10
+ ### Added
11
+ - **Permission Mode Support**: Restored `--permission-mode` option supporting default, acceptEdits, and bypassPermissions modes
12
+ - **Custom Permission Tool**: Restored `--permission-prompt-tool-name` option to specify custom permission tool names
13
+ - **Permission Response Models**: Added `PermissionToolAllowResponse` and `PermissionToolDenyResponse` models with proper JSON serialization
14
+
15
+ ### Changed
16
+ - **Message Formatting**: Modified `MessageConverter.combine_text_parts()` to join text parts with newlines instead of spaces, preserving formatting in multi-line content
17
+ - **Settings Integration**: Enhanced OptionsHandler to apply default Claude options from settings before API parameters
18
+ - **Configuration**: Extended settings to persist permission_mode and permission_prompt_tool_name
19
+
20
+ ### Fixed
21
+ - **Claude SDK Options**: Integrated Settings dependency into ClaudeSDKService to support configuration-based options
22
+
23
+ ## [0.1.1] - 2025-07-22
24
+
25
+ ### Added
26
+ - **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.
27
+ - **Startup Validation**: Added comprehensive validation checks during application startup:
28
+ - Validates OAuth credentials and warns about expired tokens
29
+ - Checks for Claude CLI binary availability with installation instructions
30
+ - Logs token expiration time and subscription type when valid
31
+ - **Default Command**: The `serve` command is now the default - running `ccproxy` without subcommands automatically starts the server.
32
+ - **Alternative Entry Point**: Added `ccproxy-api` as an alternative command-line entry point.
33
+
34
+ ### Changed
35
+ - **Authentication Variable**: Renamed environment variable from `AUTH_TOKEN` to `SECURITY__AUTH_TOKEN` for better namespace organization and clarity.
36
+ - **Credential Priority**: Reordered credential search paths to prioritize ccproxy-specific credentials before Claude CLI paths.
37
+ - **CLI Syntax**: Migrated all CLI parameters to modern Annotated syntax for better type safety and IDE support.
38
+ - **Pydantic v2**: Updated all models to use Pydantic v2 configuration syntax (`model_config` instead of `Config` class).
39
+ - **Documentation**: Improved Aider integration docs with correct API endpoint URLs and added installation options (uv, pipx).
40
+
41
+ ### Fixed
42
+ - **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.
43
+ - **URL Paths**: Fixed documentation to use `/api` endpoints for Aider compatibility instead of SDK mode paths.
44
+ - **Default Values**: Fixed default values for list parameters in CLI (docker_env, docker_volume, docker_arg).
45
+
46
+ ### Removed
47
+ - **Status Endpoints**: Removed redundant `/status` endpoints from both Claude SDK and proxy routes.
48
+ - **Permission Tool**: Removed Claude permission tool functionality and related CLI options (`--permission-mode`, `--permission-prompt-tool-name`) that are no longer needed.
49
+ - **Deprecated Options**: Removed references to deprecated permission_mode and permission_prompt_tool_name from documentation.
50
+
51
+ ## [0.1.0] - 2025-07-21
52
+
53
+ This is the initial public release of the CCProxy API.
54
+
55
+ ### Added
56
+
57
+ #### Core Functionality
58
+ - **Personal Claude Access**: Enables using a personal Claude Pro, Team, or Enterprise subscription as an API endpoint, without needing separate API keys.
59
+ - **OAuth2 Authentication**: Implements the official Claude OAuth2 flow for secure, local authentication.
60
+ - **Local Proxy Server**: Runs a lightweight FastAPI server on your local machine.
61
+ - **HTTP/HTTPS Proxy Support**: Full support for routing requests through an upstream HTTP or HTTPS proxy.
62
+
63
+ #### API & Compatibility
64
+ - **Dual API Support**: Provides full compatibility with both Anthropic and OpenAI API specifications.
65
+ - **Anthropic Messages API**: Native support for the Anthropic Messages API at `/v1/chat/completions`.
66
+ - **OpenAI Chat Completions API**: Compatibility layer for the OpenAI Chat Completions API at `/openai/v1/chat/completions`.
67
+ - **Request/Response Translation**: Seamlessly translates requests and responses between OpenAI and Anthropic formats.
68
+ - **Streaming Support**: Real-time streaming for both Anthropic and OpenAI-compatible endpoints.
69
+ - **Model Endpoints**: Lists available models via `/v1/models` and `/openai/v1/models`.
70
+ - **Health Check**: A `/health` endpoint for monitoring the proxy's status.
71
+
72
+ #### Configuration & CLI
73
+ - **Unified `ccproxy` CLI**: A single, user-friendly command-line interface for managing the proxy.
74
+ - **TOML Configuration**: Configure the server using a `config.toml` file with JSON Schema validation.
75
+ - **Keyring Integration**: Securely stores and manages OAuth credentials in the system's native keyring.
76
+ - **`generate-token` Command**: A CLI command to manually generate and manage API tokens.
77
+ - **Systemd Integration**: Includes a setup script and service template for running the proxy as a systemd service in production environments.
78
+ - **Docker Support**: A `Dockerfile` and `docker-compose.yml` for running the proxy in an isolated containerized environment.
79
+
80
+ #### Security
81
+ - **Local-First Design**: All processing and authentication happens locally; no conversation data is stored or transmitted to third parties.
82
+ - **Credential Security**: OAuth tokens are stored securely in the system keyring, not in plaintext files.
83
+ - **Header Stripping**: Automatically removes client-side `Authorization` headers to prevent accidental key leakage.
84
+
85
+ #### Developer Experience
86
+ - **Comprehensive Documentation**: Includes a quick start guide, API reference, and setup instructions.
87
+ - **Pre-commit Hooks**: Configured for automated code formatting and linting to ensure code quality.
88
+ - **Modern Tooling**: Uses `uv` for package management and `devenv` for a reproducible development environment.
89
+ - **Extensive Test Suite**: Includes unit, integration, and benchmark tests to ensure reliability.
90
+ - **Rich Logging**: Structured and colorized logging for improved readability during development and debugging.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccproxy-api
3
- Version: 0.1.0
3
+ Version: 0.1.2
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.2'
21
+ __version_tuple__ = version_tuple = (0, 1, 2)
@@ -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)
@@ -22,11 +22,13 @@ SettingsDep = Annotated[Settings, Depends(get_settings)]
22
22
 
23
23
 
24
24
  def get_claude_service(
25
+ settings: SettingsDep,
25
26
  auth_manager: AuthManagerDep,
26
27
  ) -> ClaudeSDKService:
27
28
  """Get Claude SDK service instance.
28
29
 
29
30
  Args:
31
+ settings: Application settings dependency
30
32
  auth_manager: Authentication manager dependency
31
33
 
32
34
  Returns:
@@ -39,6 +41,7 @@ def get_claude_service(
39
41
  return ClaudeSDKService(
40
42
  auth_manager=auth_manager,
41
43
  metrics=metrics,
44
+ settings=settings,
42
45
  )
43
46
 
44
47
 
@@ -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
+ ]
@@ -109,7 +109,7 @@ class MessageConverter:
109
109
  for block in contents:
110
110
  text_parts.append(MessageConverter.extract_text_from_content(block))
111
111
 
112
- return " ".join(text_parts)
112
+ return "\n".join(text_parts)
113
113
 
114
114
  @staticmethod
115
115
  def convert_to_anthropic_response(
@@ -2,6 +2,7 @@
2
2
 
3
3
  from typing import Any
4
4
 
5
+ from ccproxy.config.settings import Settings
5
6
  from ccproxy.core.async_utils import patched_typing
6
7
 
7
8
 
@@ -14,8 +15,17 @@ class OptionsHandler:
14
15
  Handles creation and management of Claude SDK options.
15
16
  """
16
17
 
17
- @staticmethod
18
+ def __init__(self, settings: Settings | None = None) -> None:
19
+ """
20
+ Initialize options handler.
21
+
22
+ Args:
23
+ settings: Application settings containing default Claude options
24
+ """
25
+ self.settings = settings
26
+
18
27
  def create_options(
28
+ self,
19
29
  model: str,
20
30
  temperature: float | None = None,
21
31
  max_tokens: int | None = None,
@@ -37,6 +47,18 @@ class OptionsHandler:
37
47
  """
38
48
  options = ClaudeCodeOptions(model=model)
39
49
 
50
+ # First apply settings from configuration if available
51
+ if self.settings and self.settings.claude.code_options:
52
+ code_opts = self.settings.claude.code_options
53
+
54
+ # Apply settings from configuration
55
+ for attr_name in dir(code_opts):
56
+ if not attr_name.startswith("_"):
57
+ value = getattr(code_opts, attr_name, None)
58
+ if value is not None and hasattr(options, attr_name):
59
+ setattr(options, attr_name, value)
60
+
61
+ # Then apply API parameters (these override settings)
40
62
  if temperature is not None:
41
63
  options.temperature = temperature # type: ignore[attr-defined]
42
64