ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0a4__py3-none-any.whl
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.
- ccproxy/api/__init__.py +1 -15
- ccproxy/api/app.py +434 -219
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +144 -168
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +388 -524
- ccproxy/api/middleware/hooks.py +563 -0
- ccproxy/api/middleware/normalize_headers.py +59 -0
- ccproxy/api/middleware/request_id.py +35 -16
- ccproxy/api/middleware/streaming_hooks.py +292 -0
- ccproxy/api/routes/__init__.py +5 -14
- ccproxy/api/routes/health.py +39 -672
- ccproxy/api/routes/plugins.py +277 -0
- ccproxy/auth/__init__.py +2 -19
- ccproxy/auth/bearer.py +25 -15
- ccproxy/auth/dependencies.py +123 -157
- ccproxy/auth/exceptions.py +0 -12
- ccproxy/auth/manager.py +35 -49
- ccproxy/auth/managers/__init__.py +10 -0
- ccproxy/auth/managers/base.py +523 -0
- ccproxy/auth/managers/base_enhanced.py +63 -0
- ccproxy/auth/managers/token_snapshot.py +77 -0
- ccproxy/auth/models/base.py +65 -0
- ccproxy/auth/models/credentials.py +40 -0
- ccproxy/auth/oauth/__init__.py +4 -18
- ccproxy/auth/oauth/base.py +533 -0
- ccproxy/auth/oauth/cli_errors.py +37 -0
- ccproxy/auth/oauth/flows.py +430 -0
- ccproxy/auth/oauth/protocol.py +366 -0
- ccproxy/auth/oauth/registry.py +408 -0
- ccproxy/auth/oauth/router.py +396 -0
- ccproxy/auth/oauth/routes.py +186 -113
- ccproxy/auth/oauth/session.py +151 -0
- ccproxy/auth/oauth/templates.py +342 -0
- ccproxy/auth/storage/__init__.py +2 -5
- ccproxy/auth/storage/base.py +279 -5
- ccproxy/auth/storage/generic.py +134 -0
- ccproxy/cli/__init__.py +1 -2
- ccproxy/cli/_settings_help.py +351 -0
- ccproxy/cli/commands/auth.py +1519 -793
- ccproxy/cli/commands/config/commands.py +209 -276
- ccproxy/cli/commands/plugins.py +669 -0
- ccproxy/cli/commands/serve.py +75 -810
- ccproxy/cli/commands/status.py +254 -0
- ccproxy/cli/decorators.py +83 -0
- ccproxy/cli/helpers.py +22 -60
- ccproxy/cli/main.py +359 -10
- ccproxy/cli/options/claude_options.py +0 -25
- ccproxy/config/__init__.py +7 -11
- ccproxy/config/core.py +227 -0
- ccproxy/config/env_generator.py +232 -0
- ccproxy/config/runtime.py +67 -0
- ccproxy/config/security.py +36 -3
- ccproxy/config/settings.py +382 -441
- ccproxy/config/toml_generator.py +299 -0
- ccproxy/config/utils.py +452 -0
- ccproxy/core/__init__.py +7 -271
- ccproxy/{_version.py → core/_version.py} +16 -3
- ccproxy/core/async_task_manager.py +516 -0
- ccproxy/core/async_utils.py +47 -14
- ccproxy/core/auth/__init__.py +6 -0
- ccproxy/core/constants.py +16 -50
- ccproxy/core/errors.py +53 -0
- ccproxy/core/id_utils.py +20 -0
- ccproxy/core/interfaces.py +16 -123
- ccproxy/core/logging.py +473 -18
- ccproxy/core/plugins/__init__.py +77 -0
- ccproxy/core/plugins/cli_discovery.py +211 -0
- ccproxy/core/plugins/declaration.py +455 -0
- ccproxy/core/plugins/discovery.py +604 -0
- ccproxy/core/plugins/factories.py +967 -0
- ccproxy/core/plugins/hooks/__init__.py +30 -0
- ccproxy/core/plugins/hooks/base.py +58 -0
- ccproxy/core/plugins/hooks/events.py +46 -0
- ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
- ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
- ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
- ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
- ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
- ccproxy/core/plugins/hooks/layers.py +44 -0
- ccproxy/core/plugins/hooks/manager.py +186 -0
- ccproxy/core/plugins/hooks/registry.py +139 -0
- ccproxy/core/plugins/hooks/thread_manager.py +203 -0
- ccproxy/core/plugins/hooks/types.py +22 -0
- ccproxy/core/plugins/interfaces.py +416 -0
- ccproxy/core/plugins/loader.py +166 -0
- ccproxy/core/plugins/middleware.py +233 -0
- ccproxy/core/plugins/models.py +59 -0
- ccproxy/core/plugins/protocol.py +180 -0
- ccproxy/core/plugins/runtime.py +519 -0
- ccproxy/{observability/context.py → core/request_context.py} +137 -94
- ccproxy/core/status_report.py +211 -0
- ccproxy/core/transformers.py +13 -8
- ccproxy/data/claude_headers_fallback.json +540 -19
- ccproxy/data/codex_headers_fallback.json +114 -7
- ccproxy/http/__init__.py +30 -0
- ccproxy/http/base.py +95 -0
- ccproxy/http/client.py +323 -0
- ccproxy/http/hooks.py +642 -0
- ccproxy/http/pool.py +279 -0
- ccproxy/llms/formatters/__init__.py +7 -0
- ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
- ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
- ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
- ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
- ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
- ccproxy/llms/formatters/base.py +140 -0
- ccproxy/llms/formatters/base_model.py +33 -0
- ccproxy/llms/formatters/common/__init__.py +51 -0
- ccproxy/llms/formatters/common/identifiers.py +48 -0
- ccproxy/llms/formatters/common/streams.py +254 -0
- ccproxy/llms/formatters/common/thinking.py +74 -0
- ccproxy/llms/formatters/common/usage.py +135 -0
- ccproxy/llms/formatters/constants.py +55 -0
- ccproxy/llms/formatters/context.py +116 -0
- ccproxy/llms/formatters/mapping.py +33 -0
- ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
- ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
- ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
- ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
- ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
- ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
- ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
- ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
- ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
- ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
- ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
- ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
- ccproxy/llms/formatters/utils.py +306 -0
- ccproxy/llms/models/__init__.py +9 -0
- ccproxy/llms/models/anthropic.py +619 -0
- ccproxy/llms/models/openai.py +844 -0
- ccproxy/llms/streaming/__init__.py +26 -0
- ccproxy/llms/streaming/accumulators.py +1074 -0
- ccproxy/llms/streaming/formatters.py +251 -0
- ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
- ccproxy/models/__init__.py +8 -159
- ccproxy/models/detection.py +92 -193
- ccproxy/models/provider.py +75 -0
- ccproxy/plugins/access_log/README.md +32 -0
- ccproxy/plugins/access_log/__init__.py +20 -0
- ccproxy/plugins/access_log/config.py +33 -0
- ccproxy/plugins/access_log/formatter.py +126 -0
- ccproxy/plugins/access_log/hook.py +763 -0
- ccproxy/plugins/access_log/logger.py +254 -0
- ccproxy/plugins/access_log/plugin.py +137 -0
- ccproxy/plugins/access_log/writer.py +109 -0
- ccproxy/plugins/analytics/README.md +24 -0
- ccproxy/plugins/analytics/__init__.py +1 -0
- ccproxy/plugins/analytics/config.py +5 -0
- ccproxy/plugins/analytics/ingest.py +85 -0
- ccproxy/plugins/analytics/models.py +97 -0
- ccproxy/plugins/analytics/plugin.py +121 -0
- ccproxy/plugins/analytics/routes.py +163 -0
- ccproxy/plugins/analytics/service.py +284 -0
- ccproxy/plugins/claude_api/README.md +29 -0
- ccproxy/plugins/claude_api/__init__.py +10 -0
- ccproxy/plugins/claude_api/adapter.py +829 -0
- ccproxy/plugins/claude_api/config.py +52 -0
- ccproxy/plugins/claude_api/detection_service.py +461 -0
- ccproxy/plugins/claude_api/health.py +175 -0
- ccproxy/plugins/claude_api/hooks.py +284 -0
- ccproxy/plugins/claude_api/models.py +256 -0
- ccproxy/plugins/claude_api/plugin.py +298 -0
- ccproxy/plugins/claude_api/routes.py +118 -0
- ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
- ccproxy/plugins/claude_api/tasks.py +84 -0
- ccproxy/plugins/claude_sdk/README.md +35 -0
- ccproxy/plugins/claude_sdk/__init__.py +80 -0
- ccproxy/plugins/claude_sdk/adapter.py +749 -0
- ccproxy/plugins/claude_sdk/auth.py +57 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
- ccproxy/plugins/claude_sdk/config.py +210 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
- ccproxy/plugins/claude_sdk/detection_service.py +163 -0
- ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
- ccproxy/plugins/claude_sdk/health.py +113 -0
- ccproxy/plugins/claude_sdk/hooks.py +115 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
- ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
- ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
- ccproxy/plugins/claude_sdk/options.py +154 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
- ccproxy/plugins/claude_sdk/plugin.py +269 -0
- ccproxy/plugins/claude_sdk/routes.py +104 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
- ccproxy/plugins/claude_sdk/session_pool.py +700 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
- ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
- ccproxy/plugins/claude_sdk/tasks.py +97 -0
- ccproxy/plugins/claude_shared/README.md +18 -0
- ccproxy/plugins/claude_shared/__init__.py +12 -0
- ccproxy/plugins/claude_shared/model_defaults.py +171 -0
- ccproxy/plugins/codex/README.md +35 -0
- ccproxy/plugins/codex/__init__.py +6 -0
- ccproxy/plugins/codex/adapter.py +635 -0
- ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
- ccproxy/plugins/codex/detection_service.py +544 -0
- ccproxy/plugins/codex/health.py +162 -0
- ccproxy/plugins/codex/hooks.py +263 -0
- ccproxy/plugins/codex/model_defaults.py +39 -0
- ccproxy/plugins/codex/models.py +263 -0
- ccproxy/plugins/codex/plugin.py +275 -0
- ccproxy/plugins/codex/routes.py +129 -0
- ccproxy/plugins/codex/streaming_metrics.py +324 -0
- ccproxy/plugins/codex/tasks.py +106 -0
- ccproxy/plugins/codex/utils/__init__.py +1 -0
- ccproxy/plugins/codex/utils/sse_parser.py +106 -0
- ccproxy/plugins/command_replay/README.md +34 -0
- ccproxy/plugins/command_replay/__init__.py +17 -0
- ccproxy/plugins/command_replay/config.py +133 -0
- ccproxy/plugins/command_replay/formatter.py +432 -0
- ccproxy/plugins/command_replay/hook.py +294 -0
- ccproxy/plugins/command_replay/plugin.py +161 -0
- ccproxy/plugins/copilot/README.md +39 -0
- ccproxy/plugins/copilot/__init__.py +11 -0
- ccproxy/plugins/copilot/adapter.py +465 -0
- ccproxy/plugins/copilot/config.py +155 -0
- ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
- ccproxy/plugins/copilot/detection_service.py +255 -0
- ccproxy/plugins/copilot/manager.py +275 -0
- ccproxy/plugins/copilot/model_defaults.py +284 -0
- ccproxy/plugins/copilot/models.py +148 -0
- ccproxy/plugins/copilot/oauth/__init__.py +16 -0
- ccproxy/plugins/copilot/oauth/client.py +494 -0
- ccproxy/plugins/copilot/oauth/models.py +385 -0
- ccproxy/plugins/copilot/oauth/provider.py +602 -0
- ccproxy/plugins/copilot/oauth/storage.py +170 -0
- ccproxy/plugins/copilot/plugin.py +360 -0
- ccproxy/plugins/copilot/routes.py +294 -0
- ccproxy/plugins/credential_balancer/README.md +124 -0
- ccproxy/plugins/credential_balancer/__init__.py +6 -0
- ccproxy/plugins/credential_balancer/config.py +270 -0
- ccproxy/plugins/credential_balancer/factory.py +415 -0
- ccproxy/plugins/credential_balancer/hook.py +51 -0
- ccproxy/plugins/credential_balancer/manager.py +587 -0
- ccproxy/plugins/credential_balancer/plugin.py +146 -0
- ccproxy/plugins/dashboard/README.md +25 -0
- ccproxy/plugins/dashboard/__init__.py +1 -0
- ccproxy/plugins/dashboard/config.py +8 -0
- ccproxy/plugins/dashboard/plugin.py +71 -0
- ccproxy/plugins/dashboard/routes.py +67 -0
- ccproxy/plugins/docker/README.md +32 -0
- ccproxy/{docker → plugins/docker}/__init__.py +3 -0
- ccproxy/{docker → plugins/docker}/adapter.py +108 -10
- ccproxy/plugins/docker/config.py +82 -0
- ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
- ccproxy/{docker → plugins/docker}/middleware.py +2 -2
- ccproxy/plugins/docker/plugin.py +198 -0
- ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
- ccproxy/plugins/duckdb_storage/README.md +26 -0
- ccproxy/plugins/duckdb_storage/__init__.py +1 -0
- ccproxy/plugins/duckdb_storage/config.py +22 -0
- ccproxy/plugins/duckdb_storage/plugin.py +128 -0
- ccproxy/plugins/duckdb_storage/routes.py +51 -0
- ccproxy/plugins/duckdb_storage/storage.py +633 -0
- ccproxy/plugins/max_tokens/README.md +38 -0
- ccproxy/plugins/max_tokens/__init__.py +12 -0
- ccproxy/plugins/max_tokens/adapter.py +235 -0
- ccproxy/plugins/max_tokens/config.py +86 -0
- ccproxy/plugins/max_tokens/models.py +53 -0
- ccproxy/plugins/max_tokens/plugin.py +200 -0
- ccproxy/plugins/max_tokens/service.py +271 -0
- ccproxy/plugins/max_tokens/token_limits.json +54 -0
- ccproxy/plugins/metrics/README.md +35 -0
- ccproxy/plugins/metrics/__init__.py +10 -0
- ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
- ccproxy/plugins/metrics/config.py +85 -0
- ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
- ccproxy/plugins/metrics/hook.py +403 -0
- ccproxy/plugins/metrics/plugin.py +268 -0
- ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
- ccproxy/plugins/metrics/routes.py +107 -0
- ccproxy/plugins/metrics/tasks.py +117 -0
- ccproxy/plugins/oauth_claude/README.md +35 -0
- ccproxy/plugins/oauth_claude/__init__.py +14 -0
- ccproxy/plugins/oauth_claude/client.py +270 -0
- ccproxy/plugins/oauth_claude/config.py +84 -0
- ccproxy/plugins/oauth_claude/manager.py +482 -0
- ccproxy/plugins/oauth_claude/models.py +266 -0
- ccproxy/plugins/oauth_claude/plugin.py +149 -0
- ccproxy/plugins/oauth_claude/provider.py +571 -0
- ccproxy/plugins/oauth_claude/storage.py +212 -0
- ccproxy/plugins/oauth_codex/README.md +38 -0
- ccproxy/plugins/oauth_codex/__init__.py +14 -0
- ccproxy/plugins/oauth_codex/client.py +224 -0
- ccproxy/plugins/oauth_codex/config.py +95 -0
- ccproxy/plugins/oauth_codex/manager.py +256 -0
- ccproxy/plugins/oauth_codex/models.py +239 -0
- ccproxy/plugins/oauth_codex/plugin.py +146 -0
- ccproxy/plugins/oauth_codex/provider.py +574 -0
- ccproxy/plugins/oauth_codex/storage.py +92 -0
- ccproxy/plugins/permissions/README.md +28 -0
- ccproxy/plugins/permissions/__init__.py +22 -0
- ccproxy/plugins/permissions/config.py +28 -0
- ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
- ccproxy/plugins/permissions/handlers/protocol.py +33 -0
- ccproxy/plugins/permissions/handlers/terminal.py +675 -0
- ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
- ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
- ccproxy/plugins/permissions/plugin.py +153 -0
- ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
- ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
- ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
- ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
- ccproxy/plugins/pricing/README.md +34 -0
- ccproxy/plugins/pricing/__init__.py +6 -0
- ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
- ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
- ccproxy/plugins/pricing/exceptions.py +35 -0
- ccproxy/plugins/pricing/loader.py +440 -0
- ccproxy/{pricing → plugins/pricing}/models.py +13 -23
- ccproxy/plugins/pricing/plugin.py +169 -0
- ccproxy/plugins/pricing/service.py +191 -0
- ccproxy/plugins/pricing/tasks.py +300 -0
- ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
- ccproxy/plugins/pricing/utils.py +99 -0
- ccproxy/plugins/request_tracer/README.md +40 -0
- ccproxy/plugins/request_tracer/__init__.py +7 -0
- ccproxy/plugins/request_tracer/config.py +120 -0
- ccproxy/plugins/request_tracer/hook.py +415 -0
- ccproxy/plugins/request_tracer/plugin.py +255 -0
- ccproxy/scheduler/__init__.py +2 -14
- ccproxy/scheduler/core.py +26 -41
- ccproxy/scheduler/manager.py +61 -105
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +268 -276
- ccproxy/services/__init__.py +0 -1
- ccproxy/services/adapters/__init__.py +11 -0
- ccproxy/services/adapters/base.py +123 -0
- ccproxy/services/adapters/chain_composer.py +88 -0
- ccproxy/services/adapters/chain_validation.py +44 -0
- ccproxy/services/adapters/chat_accumulator.py +200 -0
- ccproxy/services/adapters/delta_utils.py +142 -0
- ccproxy/services/adapters/format_adapter.py +136 -0
- ccproxy/services/adapters/format_context.py +11 -0
- ccproxy/services/adapters/format_registry.py +158 -0
- ccproxy/services/adapters/http_adapter.py +1045 -0
- ccproxy/services/adapters/mock_adapter.py +118 -0
- ccproxy/services/adapters/protocols.py +35 -0
- ccproxy/services/adapters/simple_converters.py +571 -0
- ccproxy/services/auth_registry.py +180 -0
- ccproxy/services/cache/__init__.py +6 -0
- ccproxy/services/cache/response_cache.py +261 -0
- ccproxy/services/cli_detection.py +437 -0
- ccproxy/services/config/__init__.py +6 -0
- ccproxy/services/config/proxy_configuration.py +111 -0
- ccproxy/services/container.py +256 -0
- ccproxy/services/factories.py +380 -0
- ccproxy/services/handler_config.py +76 -0
- ccproxy/services/interfaces.py +298 -0
- ccproxy/services/mocking/__init__.py +6 -0
- ccproxy/services/mocking/mock_handler.py +291 -0
- ccproxy/services/tracing/__init__.py +7 -0
- ccproxy/services/tracing/interfaces.py +61 -0
- ccproxy/services/tracing/null_tracer.py +57 -0
- ccproxy/streaming/__init__.py +23 -0
- ccproxy/streaming/buffer.py +1056 -0
- ccproxy/streaming/deferred.py +897 -0
- ccproxy/streaming/handler.py +117 -0
- ccproxy/streaming/interfaces.py +77 -0
- ccproxy/streaming/simple_adapter.py +39 -0
- ccproxy/streaming/sse.py +109 -0
- ccproxy/streaming/sse_parser.py +127 -0
- ccproxy/templates/__init__.py +6 -0
- ccproxy/templates/plugin_scaffold.py +695 -0
- ccproxy/testing/endpoints/__init__.py +33 -0
- ccproxy/testing/endpoints/cli.py +215 -0
- ccproxy/testing/endpoints/config.py +874 -0
- ccproxy/testing/endpoints/console.py +57 -0
- ccproxy/testing/endpoints/models.py +100 -0
- ccproxy/testing/endpoints/runner.py +1903 -0
- ccproxy/testing/endpoints/tools.py +308 -0
- ccproxy/testing/mock_responses.py +70 -1
- ccproxy/testing/response_handlers.py +20 -0
- ccproxy/utils/__init__.py +0 -6
- ccproxy/utils/binary_resolver.py +476 -0
- ccproxy/utils/caching.py +327 -0
- ccproxy/utils/cli_logging.py +101 -0
- ccproxy/utils/command_line.py +251 -0
- ccproxy/utils/headers.py +228 -0
- ccproxy/utils/model_mapper.py +120 -0
- ccproxy/utils/startup_helpers.py +68 -446
- ccproxy/utils/version_checker.py +273 -6
- ccproxy_api-0.2.0a4.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0a4.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0a4.dist-info/entry_points.txt +24 -0
- ccproxy/__init__.py +0 -4
- ccproxy/adapters/__init__.py +0 -11
- ccproxy/adapters/base.py +0 -80
- ccproxy/adapters/codex/__init__.py +0 -11
- ccproxy/adapters/openai/__init__.py +0 -42
- ccproxy/adapters/openai/adapter.py +0 -953
- ccproxy/adapters/openai/models.py +0 -412
- ccproxy/adapters/openai/response_adapter.py +0 -355
- ccproxy/adapters/openai/response_models.py +0 -178
- ccproxy/api/middleware/headers.py +0 -49
- ccproxy/api/middleware/logging.py +0 -180
- ccproxy/api/middleware/request_content_logging.py +0 -297
- ccproxy/api/middleware/server_header.py +0 -58
- ccproxy/api/responses.py +0 -89
- ccproxy/api/routes/claude.py +0 -371
- ccproxy/api/routes/codex.py +0 -1251
- ccproxy/api/routes/metrics.py +0 -1029
- ccproxy/api/routes/proxy.py +0 -211
- ccproxy/api/services/__init__.py +0 -6
- ccproxy/auth/conditional.py +0 -84
- ccproxy/auth/credentials_adapter.py +0 -93
- ccproxy/auth/models.py +0 -118
- ccproxy/auth/oauth/models.py +0 -48
- ccproxy/auth/openai/__init__.py +0 -13
- ccproxy/auth/openai/credentials.py +0 -166
- ccproxy/auth/openai/oauth_client.py +0 -334
- ccproxy/auth/openai/storage.py +0 -184
- ccproxy/auth/storage/json_file.py +0 -158
- ccproxy/auth/storage/keyring.py +0 -189
- ccproxy/claude_sdk/__init__.py +0 -18
- ccproxy/claude_sdk/options.py +0 -194
- ccproxy/claude_sdk/session_pool.py +0 -550
- ccproxy/cli/docker/__init__.py +0 -34
- ccproxy/cli/docker/adapter_factory.py +0 -157
- ccproxy/cli/docker/params.py +0 -274
- ccproxy/config/auth.py +0 -153
- ccproxy/config/claude.py +0 -348
- ccproxy/config/cors.py +0 -79
- ccproxy/config/discovery.py +0 -95
- ccproxy/config/docker_settings.py +0 -264
- ccproxy/config/observability.py +0 -158
- ccproxy/config/reverse_proxy.py +0 -31
- ccproxy/config/scheduler.py +0 -108
- ccproxy/config/server.py +0 -86
- ccproxy/config/validators.py +0 -231
- ccproxy/core/codex_transformers.py +0 -389
- ccproxy/core/http.py +0 -328
- ccproxy/core/http_transformers.py +0 -812
- ccproxy/core/proxy.py +0 -143
- ccproxy/core/validators.py +0 -288
- ccproxy/models/errors.py +0 -42
- ccproxy/models/messages.py +0 -269
- ccproxy/models/requests.py +0 -107
- ccproxy/models/responses.py +0 -270
- ccproxy/models/types.py +0 -102
- ccproxy/observability/__init__.py +0 -51
- ccproxy/observability/access_logger.py +0 -457
- ccproxy/observability/sse_events.py +0 -303
- ccproxy/observability/stats_printer.py +0 -753
- ccproxy/observability/storage/__init__.py +0 -1
- ccproxy/observability/storage/duckdb_simple.py +0 -677
- ccproxy/observability/storage/models.py +0 -70
- ccproxy/observability/streaming_response.py +0 -107
- ccproxy/pricing/__init__.py +0 -19
- ccproxy/pricing/loader.py +0 -251
- ccproxy/services/claude_detection_service.py +0 -243
- ccproxy/services/codex_detection_service.py +0 -252
- ccproxy/services/credentials/__init__.py +0 -55
- ccproxy/services/credentials/config.py +0 -105
- ccproxy/services/credentials/manager.py +0 -561
- ccproxy/services/credentials/oauth_client.py +0 -481
- ccproxy/services/proxy_service.py +0 -1827
- ccproxy/static/.keep +0 -0
- ccproxy/utils/cost_calculator.py +0 -210
- ccproxy/utils/disconnection_monitor.py +0 -83
- ccproxy/utils/model_mapping.py +0 -199
- ccproxy/utils/models_provider.py +0 -150
- ccproxy/utils/simple_request_logger.py +0 -284
- ccproxy/utils/streaming_metrics.py +0 -199
- ccproxy_api-0.1.7.dist-info/METADATA +0 -615
- ccproxy_api-0.1.7.dist-info/RECORD +0 -191
- ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
- /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
- /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
- /ccproxy/{docker → plugins/docker}/models.py +0 -0
- /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
- /ccproxy/{docker → plugins/docker}/validators.py +0 -0
- /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
- /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/licenses/LICENSE +0 -0
ccproxy/api/routes/proxy.py
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
"""Proxy endpoints for CCProxy API Server."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from collections.abc import AsyncIterator
|
|
5
|
-
|
|
6
|
-
from fastapi import APIRouter, HTTPException, Request, Response
|
|
7
|
-
from fastapi.responses import StreamingResponse
|
|
8
|
-
|
|
9
|
-
from ccproxy.adapters.openai.adapter import OpenAIAdapter
|
|
10
|
-
from ccproxy.api.dependencies import ProxyServiceDep
|
|
11
|
-
from ccproxy.api.responses import ProxyResponse
|
|
12
|
-
from ccproxy.auth.conditional import ConditionalAuthDep
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# Create the router for proxy endpoints
|
|
16
|
-
router = APIRouter(tags=["proxy"])
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@router.post("/v1/chat/completions", response_model=None)
|
|
20
|
-
async def create_openai_chat_completion(
|
|
21
|
-
request: Request,
|
|
22
|
-
proxy_service: ProxyServiceDep,
|
|
23
|
-
auth: ConditionalAuthDep,
|
|
24
|
-
) -> StreamingResponse | Response:
|
|
25
|
-
"""Create a chat completion using Claude AI with OpenAI-compatible format.
|
|
26
|
-
|
|
27
|
-
This endpoint handles OpenAI API format requests and forwards them
|
|
28
|
-
directly to Claude via the proxy service.
|
|
29
|
-
"""
|
|
30
|
-
try:
|
|
31
|
-
# Get request body
|
|
32
|
-
body = await request.body()
|
|
33
|
-
|
|
34
|
-
# Get headers and query params
|
|
35
|
-
headers = dict(request.headers)
|
|
36
|
-
query_params: dict[str, str | list[str]] | None = (
|
|
37
|
-
dict(request.query_params) if request.query_params else None
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
# Handle the request using proxy service directly
|
|
41
|
-
# Strip the /api prefix from the path
|
|
42
|
-
service_path = request.url.path.removeprefix("/api")
|
|
43
|
-
response = await proxy_service.handle_request(
|
|
44
|
-
method=request.method,
|
|
45
|
-
path=service_path,
|
|
46
|
-
headers=headers,
|
|
47
|
-
body=body,
|
|
48
|
-
query_params=query_params,
|
|
49
|
-
request=request, # Pass the request object for context access
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Return appropriate response type
|
|
53
|
-
if isinstance(response, StreamingResponse):
|
|
54
|
-
# Already a streaming response
|
|
55
|
-
return response
|
|
56
|
-
else:
|
|
57
|
-
# Tuple response - handle regular response
|
|
58
|
-
status_code, response_headers, response_body = response
|
|
59
|
-
if status_code >= 400:
|
|
60
|
-
# Store headers for preservation middleware
|
|
61
|
-
request.state.preserve_headers = response_headers
|
|
62
|
-
# Forward error response directly with headers
|
|
63
|
-
return ProxyResponse(
|
|
64
|
-
content=response_body,
|
|
65
|
-
status_code=status_code,
|
|
66
|
-
headers=response_headers,
|
|
67
|
-
media_type=response_headers.get("content-type", "application/json"),
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
# Check if this is a streaming response based on content-type
|
|
71
|
-
content_type = response_headers.get("content-type", "")
|
|
72
|
-
if "text/event-stream" in content_type:
|
|
73
|
-
# Return as streaming response
|
|
74
|
-
async def stream_generator() -> AsyncIterator[bytes]:
|
|
75
|
-
# Split the SSE data into chunks
|
|
76
|
-
for line in response_body.decode().split("\n"):
|
|
77
|
-
if line.strip():
|
|
78
|
-
yield f"{line}\n".encode()
|
|
79
|
-
|
|
80
|
-
return StreamingResponse(
|
|
81
|
-
stream_generator(),
|
|
82
|
-
media_type="text/event-stream",
|
|
83
|
-
headers={
|
|
84
|
-
"Cache-Control": "no-cache",
|
|
85
|
-
"Connection": "keep-alive",
|
|
86
|
-
},
|
|
87
|
-
)
|
|
88
|
-
else:
|
|
89
|
-
# Parse JSON response
|
|
90
|
-
response_data = json.loads(response_body.decode())
|
|
91
|
-
|
|
92
|
-
# Convert Anthropic response back to OpenAI format for /chat/completions
|
|
93
|
-
openai_adapter = OpenAIAdapter()
|
|
94
|
-
openai_response = openai_adapter.adapt_response(response_data)
|
|
95
|
-
|
|
96
|
-
# Return response with headers
|
|
97
|
-
return ProxyResponse(
|
|
98
|
-
content=json.dumps(openai_response),
|
|
99
|
-
status_code=status_code,
|
|
100
|
-
headers=response_headers,
|
|
101
|
-
media_type=response_headers.get("content-type", "application/json"),
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
except HTTPException:
|
|
105
|
-
# Re-raise HTTPException as-is (including 401 auth errors)
|
|
106
|
-
raise
|
|
107
|
-
except Exception as e:
|
|
108
|
-
raise HTTPException(
|
|
109
|
-
status_code=500, detail=f"Internal server error: {str(e)}"
|
|
110
|
-
) from e
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@router.post("/v1/messages", response_model=None)
|
|
114
|
-
async def create_anthropic_message(
|
|
115
|
-
request: Request,
|
|
116
|
-
proxy_service: ProxyServiceDep,
|
|
117
|
-
auth: ConditionalAuthDep,
|
|
118
|
-
) -> StreamingResponse | Response:
|
|
119
|
-
"""Create a message using Claude AI with Anthropic format.
|
|
120
|
-
|
|
121
|
-
This endpoint handles Anthropic API format requests and forwards them
|
|
122
|
-
directly to Claude via the proxy service.
|
|
123
|
-
"""
|
|
124
|
-
try:
|
|
125
|
-
# Get request body
|
|
126
|
-
body = await request.body()
|
|
127
|
-
|
|
128
|
-
# Get headers and query params
|
|
129
|
-
headers = dict(request.headers)
|
|
130
|
-
query_params: dict[str, str | list[str]] | None = (
|
|
131
|
-
dict(request.query_params) if request.query_params else None
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
# Handle the request using proxy service directly
|
|
135
|
-
# Strip the /api prefix from the path
|
|
136
|
-
service_path = request.url.path.removeprefix("/api")
|
|
137
|
-
response = await proxy_service.handle_request(
|
|
138
|
-
method=request.method,
|
|
139
|
-
path=service_path,
|
|
140
|
-
headers=headers,
|
|
141
|
-
body=body,
|
|
142
|
-
query_params=query_params,
|
|
143
|
-
request=request, # Pass the request object for context access
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
# Return appropriate response type
|
|
147
|
-
if isinstance(response, StreamingResponse):
|
|
148
|
-
# Already a streaming response
|
|
149
|
-
return response
|
|
150
|
-
else:
|
|
151
|
-
# Tuple response - handle regular response
|
|
152
|
-
status_code, response_headers, response_body = response
|
|
153
|
-
if status_code >= 400:
|
|
154
|
-
# Store headers for preservation middleware
|
|
155
|
-
request.state.preserve_headers = response_headers
|
|
156
|
-
# Forward error response directly with headers
|
|
157
|
-
return ProxyResponse(
|
|
158
|
-
content=response_body,
|
|
159
|
-
status_code=status_code,
|
|
160
|
-
headers=response_headers,
|
|
161
|
-
media_type=response_headers.get("content-type", "application/json"),
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
# Check if this is a streaming response based on content-type
|
|
165
|
-
content_type = response_headers.get("content-type", "")
|
|
166
|
-
if "text/event-stream" in content_type:
|
|
167
|
-
# Return as streaming response
|
|
168
|
-
async def stream_generator() -> AsyncIterator[bytes]:
|
|
169
|
-
# Split the SSE data into chunks
|
|
170
|
-
for line in response_body.decode().split("\n"):
|
|
171
|
-
if line.strip():
|
|
172
|
-
yield f"{line}\n".encode()
|
|
173
|
-
|
|
174
|
-
# Start with the response headers from proxy service
|
|
175
|
-
streaming_headers = response_headers.copy()
|
|
176
|
-
|
|
177
|
-
# Ensure critical headers for streaming
|
|
178
|
-
streaming_headers["Cache-Control"] = "no-cache"
|
|
179
|
-
streaming_headers["Connection"] = "keep-alive"
|
|
180
|
-
|
|
181
|
-
# Set content-type if not already set by upstream
|
|
182
|
-
if "content-type" not in streaming_headers:
|
|
183
|
-
streaming_headers["content-type"] = "text/event-stream"
|
|
184
|
-
|
|
185
|
-
return StreamingResponse(
|
|
186
|
-
stream_generator(),
|
|
187
|
-
media_type="text/event-stream",
|
|
188
|
-
headers=streaming_headers,
|
|
189
|
-
)
|
|
190
|
-
else:
|
|
191
|
-
# Store headers for preservation middleware
|
|
192
|
-
request.state.preserve_headers = response_headers
|
|
193
|
-
|
|
194
|
-
# Parse JSON response
|
|
195
|
-
response_data = json.loads(response_body.decode())
|
|
196
|
-
|
|
197
|
-
# Return response with headers
|
|
198
|
-
return ProxyResponse(
|
|
199
|
-
content=response_body, # Use original body to preserve exact format
|
|
200
|
-
status_code=status_code,
|
|
201
|
-
headers=response_headers,
|
|
202
|
-
media_type=response_headers.get("content-type", "application/json"),
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
except HTTPException:
|
|
206
|
-
# Re-raise HTTPException as-is (including 401 auth errors)
|
|
207
|
-
raise
|
|
208
|
-
except Exception as e:
|
|
209
|
-
raise HTTPException(
|
|
210
|
-
status_code=500, detail=f"Internal server error: {str(e)}"
|
|
211
|
-
) from e
|
ccproxy/api/services/__init__.py
DELETED
ccproxy/auth/conditional.py
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
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.api.dependencies import SettingsDep
|
|
9
|
-
from ccproxy.auth.bearer import BearerTokenAuthManager
|
|
10
|
-
from ccproxy.auth.exceptions import AuthenticationError
|
|
11
|
-
from ccproxy.auth.manager import AuthManager
|
|
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
|
-
settings: SettingsDep,
|
|
21
|
-
credentials: Annotated[
|
|
22
|
-
HTTPAuthorizationCredentials | None, Depends(bearer_scheme)
|
|
23
|
-
] = 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
|
-
]
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"""Adapter to make CredentialsManager compatible with AuthManager interface."""
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from ccproxy.auth.exceptions import (
|
|
6
|
-
AuthenticationError,
|
|
7
|
-
CredentialsError,
|
|
8
|
-
CredentialsExpiredError,
|
|
9
|
-
CredentialsNotFoundError,
|
|
10
|
-
)
|
|
11
|
-
from ccproxy.auth.manager import BaseAuthManager
|
|
12
|
-
from ccproxy.auth.models import ClaudeCredentials, UserProfile
|
|
13
|
-
from ccproxy.services.credentials.manager import CredentialsManager
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class CredentialsAuthManager(BaseAuthManager):
|
|
17
|
-
"""Adapter to make CredentialsManager compatible with AuthManager interface."""
|
|
18
|
-
|
|
19
|
-
def __init__(self, credentials_manager: CredentialsManager | None = None) -> None:
|
|
20
|
-
"""Initialize with credentials manager.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
credentials_manager: CredentialsManager instance, creates new if None
|
|
24
|
-
"""
|
|
25
|
-
self._credentials_manager = credentials_manager or CredentialsManager()
|
|
26
|
-
|
|
27
|
-
async def get_access_token(self) -> str:
|
|
28
|
-
"""Get valid access token from credentials manager.
|
|
29
|
-
|
|
30
|
-
Returns:
|
|
31
|
-
Access token string
|
|
32
|
-
|
|
33
|
-
Raises:
|
|
34
|
-
AuthenticationError: If authentication fails
|
|
35
|
-
"""
|
|
36
|
-
try:
|
|
37
|
-
return await self._credentials_manager.get_access_token()
|
|
38
|
-
except CredentialsNotFoundError as e:
|
|
39
|
-
raise AuthenticationError("No credentials found") from e
|
|
40
|
-
except CredentialsExpiredError as e:
|
|
41
|
-
raise AuthenticationError("Credentials expired") from e
|
|
42
|
-
except CredentialsError as e:
|
|
43
|
-
raise AuthenticationError(f"Credentials error: {e}") from e
|
|
44
|
-
|
|
45
|
-
async def get_credentials(self) -> ClaudeCredentials:
|
|
46
|
-
"""Get valid credentials from credentials manager.
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
Valid credentials
|
|
50
|
-
|
|
51
|
-
Raises:
|
|
52
|
-
AuthenticationError: If authentication fails
|
|
53
|
-
"""
|
|
54
|
-
try:
|
|
55
|
-
return await self._credentials_manager.get_valid_credentials()
|
|
56
|
-
except CredentialsNotFoundError as e:
|
|
57
|
-
raise AuthenticationError("No credentials found") from e
|
|
58
|
-
except CredentialsExpiredError as e:
|
|
59
|
-
raise AuthenticationError("Credentials expired") from e
|
|
60
|
-
except CredentialsError as e:
|
|
61
|
-
raise AuthenticationError(f"Credentials error: {e}") from e
|
|
62
|
-
|
|
63
|
-
async def is_authenticated(self) -> bool:
|
|
64
|
-
"""Check if current authentication is valid.
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
True if authenticated, False otherwise
|
|
68
|
-
"""
|
|
69
|
-
try:
|
|
70
|
-
await self._credentials_manager.get_valid_credentials()
|
|
71
|
-
return True
|
|
72
|
-
except CredentialsError:
|
|
73
|
-
return False
|
|
74
|
-
|
|
75
|
-
async def get_user_profile(self) -> UserProfile | None:
|
|
76
|
-
"""Get user profile information.
|
|
77
|
-
|
|
78
|
-
Returns:
|
|
79
|
-
UserProfile if available, None otherwise
|
|
80
|
-
"""
|
|
81
|
-
try:
|
|
82
|
-
return await self._credentials_manager.fetch_user_profile()
|
|
83
|
-
except CredentialsError:
|
|
84
|
-
return None
|
|
85
|
-
|
|
86
|
-
async def __aenter__(self) -> "CredentialsAuthManager":
|
|
87
|
-
"""Async context manager entry."""
|
|
88
|
-
await self._credentials_manager.__aenter__()
|
|
89
|
-
return self
|
|
90
|
-
|
|
91
|
-
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
92
|
-
"""Async context manager exit."""
|
|
93
|
-
await self._credentials_manager.__aexit__(exc_type, exc_val, exc_tb)
|
ccproxy/auth/models.py
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
"""Data models for authentication."""
|
|
2
|
-
|
|
3
|
-
from datetime import UTC, datetime
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class OAuthToken(BaseModel):
|
|
9
|
-
"""OAuth token information from Claude credentials."""
|
|
10
|
-
|
|
11
|
-
access_token: str = Field(..., alias="accessToken")
|
|
12
|
-
refresh_token: str = Field(..., alias="refreshToken")
|
|
13
|
-
expires_at: int | None = Field(None, alias="expiresAt")
|
|
14
|
-
scopes: list[str] = Field(default_factory=list)
|
|
15
|
-
subscription_type: str | None = Field(None, alias="subscriptionType")
|
|
16
|
-
token_type: str = Field(default="Bearer", alias="tokenType")
|
|
17
|
-
|
|
18
|
-
def __repr__(self) -> str:
|
|
19
|
-
"""Safe string representation that masks sensitive tokens."""
|
|
20
|
-
access_preview = (
|
|
21
|
-
f"{self.access_token[:8]}...{self.access_token[-8:]}"
|
|
22
|
-
if len(self.access_token) > 16
|
|
23
|
-
else "***"
|
|
24
|
-
)
|
|
25
|
-
refresh_preview = (
|
|
26
|
-
f"{self.refresh_token[:8]}...{self.refresh_token[-8:]}"
|
|
27
|
-
if len(self.refresh_token) > 16
|
|
28
|
-
else "***"
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
expires_at = (
|
|
32
|
-
datetime.fromtimestamp(self.expires_at / 1000, tz=UTC).isoformat()
|
|
33
|
-
if self.expires_at is not None
|
|
34
|
-
else "None"
|
|
35
|
-
)
|
|
36
|
-
return (
|
|
37
|
-
f"OAuthToken(access_token='{access_preview}', "
|
|
38
|
-
f"refresh_token='{refresh_preview}', "
|
|
39
|
-
f"expires_at={expires_at}, "
|
|
40
|
-
f"scopes={self.scopes}, "
|
|
41
|
-
f"subscription_type='{self.subscription_type}', "
|
|
42
|
-
f"token_type='{self.token_type}')"
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
@property
|
|
46
|
-
def is_expired(self) -> bool:
|
|
47
|
-
"""Check if the token is expired."""
|
|
48
|
-
if self.expires_at is None:
|
|
49
|
-
# If no expiration info, assume not expired for backward compatibility
|
|
50
|
-
return False
|
|
51
|
-
now = datetime.now(UTC).timestamp() * 1000 # Convert to milliseconds
|
|
52
|
-
return now >= self.expires_at
|
|
53
|
-
|
|
54
|
-
@property
|
|
55
|
-
def expires_at_datetime(self) -> datetime:
|
|
56
|
-
"""Get expiration as datetime object."""
|
|
57
|
-
if self.expires_at is None:
|
|
58
|
-
# Return a far future date if no expiration info
|
|
59
|
-
return datetime.fromtimestamp(2147483647, tz=UTC) # Year 2038
|
|
60
|
-
return datetime.fromtimestamp(self.expires_at / 1000, tz=UTC)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class OrganizationInfo(BaseModel):
|
|
64
|
-
"""Organization information from OAuth API."""
|
|
65
|
-
|
|
66
|
-
uuid: str
|
|
67
|
-
name: str
|
|
68
|
-
organization_type: str | None = None
|
|
69
|
-
billing_type: str | None = None
|
|
70
|
-
rate_limit_tier: str | None = None
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class AccountInfo(BaseModel):
|
|
74
|
-
"""Account information from OAuth API."""
|
|
75
|
-
|
|
76
|
-
uuid: str
|
|
77
|
-
email: str
|
|
78
|
-
full_name: str | None = None
|
|
79
|
-
display_name: str | None = None
|
|
80
|
-
has_claude_max: bool | None = None
|
|
81
|
-
has_claude_pro: bool | None = None
|
|
82
|
-
|
|
83
|
-
@property
|
|
84
|
-
def email_address(self) -> str:
|
|
85
|
-
"""Compatibility property for email_address."""
|
|
86
|
-
return self.email
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class UserProfile(BaseModel):
|
|
90
|
-
"""User profile information from Anthropic OAuth API."""
|
|
91
|
-
|
|
92
|
-
organization: OrganizationInfo | None = None
|
|
93
|
-
account: AccountInfo | None = None
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
class ClaudeCredentials(BaseModel):
|
|
97
|
-
"""Claude credentials from the credentials file."""
|
|
98
|
-
|
|
99
|
-
claude_ai_oauth: OAuthToken = Field(..., alias="claudeAiOauth")
|
|
100
|
-
|
|
101
|
-
def __repr__(self) -> str:
|
|
102
|
-
"""Safe string representation that masks sensitive tokens."""
|
|
103
|
-
return f"ClaudeCredentials(claude_ai_oauth={repr(self.claude_ai_oauth)})"
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
class ValidationResult(BaseModel):
|
|
107
|
-
"""Result of credentials validation."""
|
|
108
|
-
|
|
109
|
-
valid: bool
|
|
110
|
-
expired: bool | None = None
|
|
111
|
-
credentials: ClaudeCredentials | None = None
|
|
112
|
-
path: str | None = None
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# Backwards compatibility - provide common aliases
|
|
116
|
-
User = UserProfile
|
|
117
|
-
Credentials = ClaudeCredentials
|
|
118
|
-
Profile = UserProfile
|
ccproxy/auth/oauth/models.py
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"""OAuth-specific models for authentication."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class OAuthState(BaseModel):
|
|
9
|
-
"""OAuth state information for pending flows."""
|
|
10
|
-
|
|
11
|
-
code_verifier: str = Field(..., description="PKCE code verifier")
|
|
12
|
-
custom_paths: list[str] | None = Field(None, description="Custom credential paths")
|
|
13
|
-
completed: bool = Field(default=False, description="Whether the flow is completed")
|
|
14
|
-
success: bool = Field(default=False, description="Whether the flow was successful")
|
|
15
|
-
error: str | None = Field(None, description="Error message if failed")
|
|
16
|
-
created_at: datetime = Field(
|
|
17
|
-
default_factory=datetime.utcnow, description="Creation timestamp"
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class OAuthCallbackRequest(BaseModel):
|
|
22
|
-
"""OAuth callback request parameters."""
|
|
23
|
-
|
|
24
|
-
code: str | None = Field(None, description="Authorization code")
|
|
25
|
-
state: str | None = Field(None, description="State parameter")
|
|
26
|
-
error: str | None = Field(None, description="OAuth error")
|
|
27
|
-
error_description: str | None = Field(None, description="OAuth error description")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class OAuthTokenRequest(BaseModel):
|
|
31
|
-
"""OAuth token exchange request."""
|
|
32
|
-
|
|
33
|
-
grant_type: str = Field(default="authorization_code")
|
|
34
|
-
code: str = Field(..., description="Authorization code")
|
|
35
|
-
redirect_uri: str = Field(..., description="Redirect URI")
|
|
36
|
-
client_id: str = Field(..., description="Client ID")
|
|
37
|
-
code_verifier: str = Field(..., description="PKCE code verifier")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class OAuthTokenResponse(BaseModel):
|
|
41
|
-
"""OAuth token exchange response."""
|
|
42
|
-
|
|
43
|
-
access_token: str = Field(..., description="Access token")
|
|
44
|
-
refresh_token: str | None = Field(None, description="Refresh token")
|
|
45
|
-
expires_in: int | None = Field(None, description="Token expiration in seconds")
|
|
46
|
-
scope: str | None = Field(None, description="Granted scopes")
|
|
47
|
-
subscription_type: str | None = Field(None, description="Subscription type")
|
|
48
|
-
token_type: str = Field(default="Bearer", description="Token type")
|
ccproxy/auth/openai/__init__.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"""OpenAI authentication components for Codex integration."""
|
|
2
|
-
|
|
3
|
-
from .credentials import OpenAICredentials, OpenAITokenManager
|
|
4
|
-
from .oauth_client import OpenAIOAuthClient
|
|
5
|
-
from .storage import OpenAITokenStorage
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
__all__ = [
|
|
9
|
-
"OpenAICredentials",
|
|
10
|
-
"OpenAITokenManager",
|
|
11
|
-
"OpenAIOAuthClient",
|
|
12
|
-
"OpenAITokenStorage",
|
|
13
|
-
]
|