ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0__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.0.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0.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.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"""Central OAuth router that delegates to plugin providers.
|
|
2
|
+
|
|
3
|
+
This module provides unified OAuth endpoints that dynamically route
|
|
4
|
+
to the appropriate plugin-based OAuth provider.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import base64
|
|
8
|
+
import secrets
|
|
9
|
+
|
|
10
|
+
import structlog
|
|
11
|
+
from fastapi import APIRouter, HTTPException, Query, Request, Response
|
|
12
|
+
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
|
13
|
+
from pydantic import BaseModel
|
|
14
|
+
|
|
15
|
+
from ccproxy.auth.oauth.registry import OAuthProviderInfo
|
|
16
|
+
from ccproxy.auth.oauth.session import get_oauth_session_manager
|
|
17
|
+
from ccproxy.auth.oauth.templates import OAuthTemplates
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logger = structlog.get_logger(__name__)
|
|
21
|
+
|
|
22
|
+
# Create the OAuth router
|
|
23
|
+
oauth_router = APIRouter()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OAuthProvidersResponse(BaseModel):
|
|
27
|
+
"""Response for listing OAuth providers."""
|
|
28
|
+
|
|
29
|
+
providers: dict[str, OAuthProviderInfo]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class OAuthLoginResponse(BaseModel):
|
|
33
|
+
"""Response for OAuth login initiation."""
|
|
34
|
+
|
|
35
|
+
auth_url: str
|
|
36
|
+
state: str
|
|
37
|
+
provider: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class OAuthErrorResponse(BaseModel):
|
|
41
|
+
"""Response for OAuth errors."""
|
|
42
|
+
|
|
43
|
+
error: str
|
|
44
|
+
error_description: str | None = None
|
|
45
|
+
provider: str | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@oauth_router.get("/providers", response_model=OAuthProvidersResponse)
|
|
49
|
+
async def list_oauth_providers(request: Request) -> OAuthProvidersResponse:
|
|
50
|
+
"""List all available OAuth providers.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Dictionary of available OAuth providers with their information
|
|
54
|
+
"""
|
|
55
|
+
# Get registry from app state (app-scoped)
|
|
56
|
+
registry = getattr(request.app.state, "oauth_registry", None)
|
|
57
|
+
if registry is None:
|
|
58
|
+
raise HTTPException(status_code=503, detail="OAuth registry not initialized")
|
|
59
|
+
providers = registry.list()
|
|
60
|
+
|
|
61
|
+
logger.info("oauth_providers_listed", count=len(providers), category="auth")
|
|
62
|
+
|
|
63
|
+
return OAuthProvidersResponse(providers=providers)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@oauth_router.get("/{provider}/login")
|
|
67
|
+
async def initiate_oauth_login(
|
|
68
|
+
request: Request,
|
|
69
|
+
provider: str,
|
|
70
|
+
redirect_uri: str | None = Query(
|
|
71
|
+
None, description="Optional redirect URI override"
|
|
72
|
+
),
|
|
73
|
+
scopes: str | None = Query(
|
|
74
|
+
None, description="Optional scope override (comma-separated)"
|
|
75
|
+
),
|
|
76
|
+
) -> RedirectResponse:
|
|
77
|
+
"""Initiate OAuth login flow for a specific provider.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
provider: Provider name (e.g., 'claude-api', 'codex')
|
|
81
|
+
redirect_uri: Optional redirect URI override
|
|
82
|
+
scopes: Optional scope override
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Redirect to provider's authorization URL
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
HTTPException: If provider not found or error generating auth URL
|
|
89
|
+
"""
|
|
90
|
+
registry = getattr(request.app.state, "oauth_registry", None)
|
|
91
|
+
if registry is None:
|
|
92
|
+
raise HTTPException(status_code=503, detail="OAuth registry not initialized")
|
|
93
|
+
oauth_provider = registry.get(provider)
|
|
94
|
+
|
|
95
|
+
if not oauth_provider:
|
|
96
|
+
logger.error("oauth_provider_not_found", provider=provider, category="auth")
|
|
97
|
+
raise HTTPException(
|
|
98
|
+
status_code=404,
|
|
99
|
+
detail=f"OAuth provider '{provider}' not found",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Generate OAuth state for CSRF protection
|
|
103
|
+
state = secrets.token_urlsafe(32)
|
|
104
|
+
|
|
105
|
+
# Generate PKCE code verifier if provider supports it
|
|
106
|
+
code_verifier = None
|
|
107
|
+
if oauth_provider.supports_pkce:
|
|
108
|
+
# Generate PKCE pair
|
|
109
|
+
code_verifier = (
|
|
110
|
+
base64.urlsafe_b64encode(secrets.token_bytes(32))
|
|
111
|
+
.decode("utf-8")
|
|
112
|
+
.rstrip("=")
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Store OAuth session data
|
|
116
|
+
session_manager = get_oauth_session_manager()
|
|
117
|
+
session_data = {
|
|
118
|
+
"provider": provider,
|
|
119
|
+
"state": state,
|
|
120
|
+
"redirect_uri": redirect_uri,
|
|
121
|
+
"scopes": scopes.split(",") if scopes else None,
|
|
122
|
+
}
|
|
123
|
+
if code_verifier:
|
|
124
|
+
session_data["code_verifier"] = code_verifier
|
|
125
|
+
|
|
126
|
+
await session_manager.create_session(state, session_data)
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
# Get authorization URL from provider
|
|
130
|
+
auth_url = await oauth_provider.get_authorization_url(state, code_verifier)
|
|
131
|
+
|
|
132
|
+
logger.info(
|
|
133
|
+
"oauth_login_initiated",
|
|
134
|
+
provider=provider,
|
|
135
|
+
state=state,
|
|
136
|
+
has_pkce=bool(code_verifier),
|
|
137
|
+
category="auth",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Redirect to provider's authorization page
|
|
141
|
+
return RedirectResponse(url=auth_url, status_code=302)
|
|
142
|
+
|
|
143
|
+
except Exception as e:
|
|
144
|
+
logger.error(
|
|
145
|
+
"oauth_login_error",
|
|
146
|
+
provider=provider,
|
|
147
|
+
error=str(e),
|
|
148
|
+
exc_info=e,
|
|
149
|
+
category="auth",
|
|
150
|
+
)
|
|
151
|
+
await session_manager.delete_session(state)
|
|
152
|
+
raise HTTPException(
|
|
153
|
+
status_code=500,
|
|
154
|
+
detail=f"Failed to initiate OAuth login: {str(e)}",
|
|
155
|
+
) from e
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@oauth_router.get("/{provider}/callback")
|
|
159
|
+
async def handle_oauth_callback(
|
|
160
|
+
provider: str,
|
|
161
|
+
request: Request,
|
|
162
|
+
code: str | None = Query(None, description="Authorization code"),
|
|
163
|
+
state: str | None = Query(None, description="OAuth state"),
|
|
164
|
+
error: str | None = Query(None, description="OAuth error"),
|
|
165
|
+
error_description: str | None = Query(None, description="Error description"),
|
|
166
|
+
) -> HTMLResponse:
|
|
167
|
+
"""Handle OAuth callback from provider.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
provider: Provider name
|
|
171
|
+
request: FastAPI request
|
|
172
|
+
code: Authorization code from provider
|
|
173
|
+
state: OAuth state for validation
|
|
174
|
+
error: OAuth error code
|
|
175
|
+
error_description: OAuth error description
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
HTML response with success or error message
|
|
179
|
+
|
|
180
|
+
Raises:
|
|
181
|
+
HTTPException: If provider not found or callback handling fails
|
|
182
|
+
"""
|
|
183
|
+
# Handle OAuth errors
|
|
184
|
+
if error:
|
|
185
|
+
logger.error(
|
|
186
|
+
"oauth_callback_error",
|
|
187
|
+
provider=provider,
|
|
188
|
+
error=error,
|
|
189
|
+
error_description=error_description,
|
|
190
|
+
category="auth",
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return OAuthTemplates.callback_error(
|
|
194
|
+
error=error,
|
|
195
|
+
error_description=error_description,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Validate required parameters
|
|
199
|
+
if not code or not state:
|
|
200
|
+
logger.error(
|
|
201
|
+
"oauth_callback_missing_params",
|
|
202
|
+
provider=provider,
|
|
203
|
+
has_code=bool(code),
|
|
204
|
+
has_state=bool(state),
|
|
205
|
+
category="auth",
|
|
206
|
+
)
|
|
207
|
+
return OAuthTemplates.error(
|
|
208
|
+
error_message="No authorization code was received.",
|
|
209
|
+
title="Missing Authorization Code",
|
|
210
|
+
error_detail="The OAuth server did not provide an authorization code. Please try again.",
|
|
211
|
+
status_code=400,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Get OAuth session
|
|
215
|
+
session_manager = get_oauth_session_manager()
|
|
216
|
+
session_data = await session_manager.get_session(state)
|
|
217
|
+
|
|
218
|
+
if not session_data:
|
|
219
|
+
logger.error(
|
|
220
|
+
"oauth_callback_invalid_state",
|
|
221
|
+
provider=provider,
|
|
222
|
+
state=state,
|
|
223
|
+
category="auth",
|
|
224
|
+
)
|
|
225
|
+
return OAuthTemplates.error(
|
|
226
|
+
error_message="The authentication state is invalid or has expired.",
|
|
227
|
+
title="Invalid State",
|
|
228
|
+
error_detail="This may indicate a CSRF attack or an expired authentication session. Please start the authentication process again.",
|
|
229
|
+
status_code=400,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Validate provider matches
|
|
233
|
+
if session_data.get("provider") != provider:
|
|
234
|
+
logger.error(
|
|
235
|
+
"oauth_callback_provider_mismatch",
|
|
236
|
+
expected=session_data.get("provider"),
|
|
237
|
+
actual=provider,
|
|
238
|
+
category="auth",
|
|
239
|
+
)
|
|
240
|
+
await session_manager.delete_session(state)
|
|
241
|
+
return OAuthTemplates.error(
|
|
242
|
+
error_message="Provider mismatch in OAuth callback",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Get provider instance
|
|
246
|
+
registry = getattr(request.app.state, "oauth_registry", None)
|
|
247
|
+
if registry is None:
|
|
248
|
+
raise HTTPException(status_code=503, detail="OAuth registry not initialized")
|
|
249
|
+
oauth_provider = registry.get(provider)
|
|
250
|
+
|
|
251
|
+
if not oauth_provider:
|
|
252
|
+
logger.error("oauth_provider_not_found", provider=provider, category="auth")
|
|
253
|
+
await session_manager.delete_session(state)
|
|
254
|
+
raise HTTPException(
|
|
255
|
+
status_code=404,
|
|
256
|
+
detail=f"OAuth provider '{provider}' not found",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
# Exchange code for tokens
|
|
261
|
+
code_verifier = session_data.get("code_verifier")
|
|
262
|
+
credentials = await oauth_provider.handle_callback(code, state, code_verifier)
|
|
263
|
+
|
|
264
|
+
# Clean up session
|
|
265
|
+
await session_manager.delete_session(state)
|
|
266
|
+
|
|
267
|
+
logger.info(
|
|
268
|
+
"oauth_callback_success",
|
|
269
|
+
provider=provider,
|
|
270
|
+
has_credentials=bool(credentials),
|
|
271
|
+
category="auth",
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Return success page
|
|
275
|
+
return OAuthTemplates.success(
|
|
276
|
+
message="Authentication successful! You can close this window.",
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
except Exception as e:
|
|
280
|
+
logger.error(
|
|
281
|
+
"oauth_callback_exchange_error",
|
|
282
|
+
provider=provider,
|
|
283
|
+
error=str(e),
|
|
284
|
+
exc_info=e,
|
|
285
|
+
category="auth",
|
|
286
|
+
)
|
|
287
|
+
await session_manager.delete_session(state)
|
|
288
|
+
|
|
289
|
+
return OAuthTemplates.error(
|
|
290
|
+
error_message="Failed to exchange authorization code for tokens.",
|
|
291
|
+
title="Token Exchange Failed",
|
|
292
|
+
error_detail=str(e),
|
|
293
|
+
status_code=500,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@oauth_router.post("/{provider}/refresh")
|
|
298
|
+
async def refresh_oauth_token(
|
|
299
|
+
request: Request,
|
|
300
|
+
provider: str,
|
|
301
|
+
refresh_token: str,
|
|
302
|
+
) -> JSONResponse:
|
|
303
|
+
"""Refresh OAuth access token.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
provider: Provider name
|
|
307
|
+
refresh_token: Refresh token
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
New token response
|
|
311
|
+
|
|
312
|
+
Raises:
|
|
313
|
+
HTTPException: If provider not found or refresh fails
|
|
314
|
+
"""
|
|
315
|
+
registry = getattr(request.app.state, "oauth_registry", None)
|
|
316
|
+
if registry is None:
|
|
317
|
+
raise HTTPException(status_code=503, detail="OAuth registry not initialized")
|
|
318
|
+
oauth_provider = registry.get(provider)
|
|
319
|
+
|
|
320
|
+
if not oauth_provider:
|
|
321
|
+
logger.error("oauth_provider_not_found", provider=provider, category="auth")
|
|
322
|
+
raise HTTPException(
|
|
323
|
+
status_code=404,
|
|
324
|
+
detail=f"OAuth provider '{provider}' not found",
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
try:
|
|
328
|
+
new_tokens = await oauth_provider.refresh_access_token(refresh_token)
|
|
329
|
+
|
|
330
|
+
logger.info("oauth_token_refreshed", provider=provider, category="auth")
|
|
331
|
+
|
|
332
|
+
return JSONResponse(content=new_tokens, status_code=200)
|
|
333
|
+
|
|
334
|
+
except Exception as e:
|
|
335
|
+
logger.error(
|
|
336
|
+
"oauth_refresh_error",
|
|
337
|
+
provider=provider,
|
|
338
|
+
error=str(e),
|
|
339
|
+
exc_info=e,
|
|
340
|
+
category="auth",
|
|
341
|
+
)
|
|
342
|
+
raise HTTPException(
|
|
343
|
+
status_code=500,
|
|
344
|
+
detail=f"Failed to refresh token: {str(e)}",
|
|
345
|
+
) from e
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@oauth_router.post("/{provider}/revoke")
|
|
349
|
+
async def revoke_oauth_token(
|
|
350
|
+
request: Request,
|
|
351
|
+
provider: str,
|
|
352
|
+
token: str,
|
|
353
|
+
) -> Response:
|
|
354
|
+
"""Revoke an OAuth token.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
provider: Provider name
|
|
358
|
+
token: Token to revoke
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Empty response on success
|
|
362
|
+
|
|
363
|
+
Raises:
|
|
364
|
+
HTTPException: If provider not found or revocation fails
|
|
365
|
+
"""
|
|
366
|
+
registry = getattr(request.app.state, "oauth_registry", None)
|
|
367
|
+
if registry is None:
|
|
368
|
+
raise HTTPException(status_code=503, detail="OAuth registry not initialized")
|
|
369
|
+
oauth_provider = registry.get(provider)
|
|
370
|
+
|
|
371
|
+
if not oauth_provider:
|
|
372
|
+
logger.error("oauth_provider_not_found", provider=provider, category="auth")
|
|
373
|
+
raise HTTPException(
|
|
374
|
+
status_code=404,
|
|
375
|
+
detail=f"OAuth provider '{provider}' not found",
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
try:
|
|
379
|
+
await oauth_provider.revoke_token(token)
|
|
380
|
+
|
|
381
|
+
logger.info("oauth_token_revoked", provider=provider, category="auth")
|
|
382
|
+
|
|
383
|
+
return Response(status_code=204)
|
|
384
|
+
|
|
385
|
+
except Exception as e:
|
|
386
|
+
logger.error(
|
|
387
|
+
"oauth_revoke_error",
|
|
388
|
+
provider=provider,
|
|
389
|
+
error=str(e),
|
|
390
|
+
exc_info=e,
|
|
391
|
+
category="auth",
|
|
392
|
+
)
|
|
393
|
+
raise HTTPException(
|
|
394
|
+
status_code=500,
|
|
395
|
+
detail=f"Failed to revoke token: {str(e)}",
|
|
396
|
+
) from e
|