ccproxy-api 0.1.6__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 +439 -212
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +145 -176
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +402 -530
- 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 +558 -0
- ccproxy/data/codex_headers_fallback.json +121 -0
- 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 +63 -107
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +346 -314
- 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 +95 -342
- ccproxy/utils/version_checker.py +279 -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.6.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 -1231
- 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 -269
- ccproxy/services/codex_detection_service.py +0 -263
- 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.6.dist-info/METADATA +0 -615
- ccproxy_api-0.1.6.dist-info/RECORD +0 -189
- ccproxy_api-0.1.6.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.6.dist-info → ccproxy_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""Claude OAuth client implementation."""
|
|
2
|
+
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ccproxy.services.cli_detection import CLIDetectionService
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
from pydantic import SecretStr
|
|
12
|
+
|
|
13
|
+
from ccproxy.auth.exceptions import OAuthError
|
|
14
|
+
from ccproxy.auth.oauth.base import BaseOAuthClient
|
|
15
|
+
from ccproxy.auth.storage.base import TokenStorage
|
|
16
|
+
from ccproxy.config.settings import Settings
|
|
17
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
18
|
+
|
|
19
|
+
from .config import ClaudeOAuthConfig
|
|
20
|
+
from .models import (
|
|
21
|
+
ClaudeCredentials,
|
|
22
|
+
ClaudeOAuthToken,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
logger = get_plugin_logger()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ClaudeOAuthClient(BaseOAuthClient[ClaudeCredentials]):
|
|
30
|
+
"""Claude OAuth implementation for the OAuth Claude plugin."""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
config: ClaudeOAuthConfig,
|
|
35
|
+
storage: TokenStorage[ClaudeCredentials] | None = None,
|
|
36
|
+
http_client: httpx.AsyncClient | None = None,
|
|
37
|
+
hook_manager: Any | None = None,
|
|
38
|
+
detection_service: "CLIDetectionService | None" = None,
|
|
39
|
+
settings: Settings | None = None,
|
|
40
|
+
):
|
|
41
|
+
"""Initialize Claude OAuth client.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
config: OAuth configuration
|
|
45
|
+
storage: Token storage backend
|
|
46
|
+
http_client: Optional HTTP client (for request tracing support)
|
|
47
|
+
hook_manager: Optional hook manager for emitting events
|
|
48
|
+
detection_service: Optional CLI detection service for headers
|
|
49
|
+
settings: Optional settings for HTTP client configuration
|
|
50
|
+
"""
|
|
51
|
+
self.oauth_config = config
|
|
52
|
+
self.detection_service = detection_service
|
|
53
|
+
|
|
54
|
+
# Resolve effective redirect URI from config
|
|
55
|
+
redirect_uri = config.get_redirect_uri()
|
|
56
|
+
|
|
57
|
+
# Debug logging for CLI tracing
|
|
58
|
+
logger.debug(
|
|
59
|
+
"claude_oauth_client_init",
|
|
60
|
+
has_http_client=http_client is not None,
|
|
61
|
+
has_hook_manager=hook_manager is not None,
|
|
62
|
+
http_client_id=id(http_client) if http_client else None,
|
|
63
|
+
hook_manager_id=id(hook_manager) if hook_manager else None,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Initialize base class
|
|
67
|
+
super().__init__(
|
|
68
|
+
client_id=config.client_id,
|
|
69
|
+
redirect_uri=redirect_uri,
|
|
70
|
+
base_url=config.base_url,
|
|
71
|
+
scopes=config.scopes,
|
|
72
|
+
storage=storage,
|
|
73
|
+
http_client=http_client,
|
|
74
|
+
hook_manager=hook_manager,
|
|
75
|
+
settings=settings,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def _get_auth_endpoint(self) -> str:
|
|
79
|
+
"""Get Claude OAuth authorization endpoint.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Full authorization endpoint URL
|
|
83
|
+
"""
|
|
84
|
+
return self.oauth_config.authorize_url
|
|
85
|
+
|
|
86
|
+
def _get_token_endpoint(self) -> str:
|
|
87
|
+
"""Get Claude OAuth token exchange endpoint.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Full token endpoint URL
|
|
91
|
+
"""
|
|
92
|
+
return self.oauth_config.token_url
|
|
93
|
+
|
|
94
|
+
def get_custom_headers(self) -> dict[str, str]:
|
|
95
|
+
"""Get Claude-specific HTTP headers.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Dictionary of custom headers
|
|
99
|
+
"""
|
|
100
|
+
# Start with headers from config
|
|
101
|
+
headers = dict(self.oauth_config.headers)
|
|
102
|
+
|
|
103
|
+
# Use injected detection service if available
|
|
104
|
+
if self.detection_service:
|
|
105
|
+
try:
|
|
106
|
+
get_headers = getattr(
|
|
107
|
+
self.detection_service, "get_cached_headers", None
|
|
108
|
+
)
|
|
109
|
+
detected_headers = get_headers() if callable(get_headers) else None
|
|
110
|
+
if detected_headers and "user-agent" in detected_headers:
|
|
111
|
+
headers["User-Agent"] = detected_headers["user-agent"]
|
|
112
|
+
except Exception:
|
|
113
|
+
# Keep the User-Agent from config if detection service not available
|
|
114
|
+
pass
|
|
115
|
+
# No fallback - if detection service is not injected, use config headers only
|
|
116
|
+
|
|
117
|
+
return headers
|
|
118
|
+
|
|
119
|
+
def _use_json_for_token_exchange(self) -> bool:
|
|
120
|
+
"""Claude uses JSON for token exchange.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
True to use JSON body
|
|
124
|
+
"""
|
|
125
|
+
return True
|
|
126
|
+
|
|
127
|
+
def _get_token_exchange_data(
|
|
128
|
+
self, code: str, code_verifier: str, state: str | None = None
|
|
129
|
+
) -> dict[str, str]:
|
|
130
|
+
"""Get token exchange request data for Claude.
|
|
131
|
+
|
|
132
|
+
Claude has a non-standard OAuth implementation that requires the
|
|
133
|
+
state parameter in token exchange requests, unlike RFC 6749 Section 4.1.3.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
code: Authorization code
|
|
137
|
+
code_verifier: PKCE code verifier
|
|
138
|
+
state: OAuth state parameter (required by Claude)
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Dictionary of token exchange parameters
|
|
142
|
+
"""
|
|
143
|
+
base_data = {
|
|
144
|
+
"grant_type": "authorization_code",
|
|
145
|
+
"code": code,
|
|
146
|
+
"redirect_uri": self.redirect_uri,
|
|
147
|
+
"client_id": self.client_id,
|
|
148
|
+
"code_verifier": code_verifier,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Claude requires the state parameter in token exchange (non-standard)
|
|
152
|
+
if state:
|
|
153
|
+
base_data["state"] = state
|
|
154
|
+
|
|
155
|
+
# Allow for custom parameters
|
|
156
|
+
custom_data = self.get_custom_token_params()
|
|
157
|
+
base_data.update(custom_data)
|
|
158
|
+
|
|
159
|
+
return base_data
|
|
160
|
+
|
|
161
|
+
async def parse_token_response(self, data: dict[str, Any]) -> ClaudeCredentials:
|
|
162
|
+
"""Parse Claude-specific token response.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
data: Raw token response from Claude
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Claude credentials object
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
OAuthError: If response parsing fails
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
# Calculate expiration time
|
|
175
|
+
expires_in = data.get("expires_in")
|
|
176
|
+
expires_at = None
|
|
177
|
+
if expires_in:
|
|
178
|
+
expires_at = int((datetime.now(UTC).timestamp() + expires_in) * 1000)
|
|
179
|
+
|
|
180
|
+
# Parse scope string into list
|
|
181
|
+
scopes: list[str] = []
|
|
182
|
+
if data.get("scope"):
|
|
183
|
+
scopes = (
|
|
184
|
+
data["scope"].split()
|
|
185
|
+
if isinstance(data["scope"], str)
|
|
186
|
+
else data["scope"]
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Create OAuth token
|
|
190
|
+
oauth_token = ClaudeOAuthToken(
|
|
191
|
+
accessToken=SecretStr(data["access_token"]),
|
|
192
|
+
refreshToken=SecretStr(data.get("refresh_token", "")),
|
|
193
|
+
expiresAt=expires_at,
|
|
194
|
+
scopes=scopes or self.oauth_config.scopes,
|
|
195
|
+
subscriptionType=data.get("subscription_type"),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Create credentials (using alias for field name)
|
|
199
|
+
credentials = ClaudeCredentials(claudeAiOauth=oauth_token)
|
|
200
|
+
|
|
201
|
+
logger.info(
|
|
202
|
+
"claude_oauth_credentials_parsed",
|
|
203
|
+
has_refresh_token=bool(data.get("refresh_token")),
|
|
204
|
+
expires_in=expires_in,
|
|
205
|
+
subscription_type=oauth_token.subscription_type,
|
|
206
|
+
scopes=oauth_token.scopes,
|
|
207
|
+
category="auth",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return credentials
|
|
211
|
+
|
|
212
|
+
except KeyError as e:
|
|
213
|
+
logger.error(
|
|
214
|
+
"claude_oauth_token_response_missing_field",
|
|
215
|
+
missing_field=str(e),
|
|
216
|
+
response_keys=list(data.keys()),
|
|
217
|
+
category="auth",
|
|
218
|
+
)
|
|
219
|
+
raise OAuthError(f"Missing required field in token response: {e}") from e
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.error(
|
|
222
|
+
"claude_oauth_token_response_parse_error",
|
|
223
|
+
error=str(e),
|
|
224
|
+
error_type=type(e).__name__,
|
|
225
|
+
category="auth",
|
|
226
|
+
)
|
|
227
|
+
raise OAuthError(f"Failed to parse Claude token response: {e}") from e
|
|
228
|
+
|
|
229
|
+
async def refresh_token(self, refresh_token: str) -> ClaudeCredentials:
|
|
230
|
+
"""Refresh Claude access token.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
refresh_token: Refresh token
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
New Claude credentials
|
|
237
|
+
|
|
238
|
+
Raises:
|
|
239
|
+
OAuthError: If refresh fails
|
|
240
|
+
"""
|
|
241
|
+
token_endpoint = self._get_token_endpoint()
|
|
242
|
+
data = {
|
|
243
|
+
"grant_type": "refresh_token",
|
|
244
|
+
"refresh_token": refresh_token,
|
|
245
|
+
"client_id": self.client_id,
|
|
246
|
+
}
|
|
247
|
+
headers = self.get_custom_headers()
|
|
248
|
+
headers["Content-Type"] = "application/json"
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
# Use the HTTP client directly (always available now)
|
|
252
|
+
response = await self.http_client.post(
|
|
253
|
+
token_endpoint,
|
|
254
|
+
json=data, # Claude uses JSON
|
|
255
|
+
headers=headers,
|
|
256
|
+
timeout=30.0,
|
|
257
|
+
)
|
|
258
|
+
response.raise_for_status()
|
|
259
|
+
|
|
260
|
+
token_response = response.json()
|
|
261
|
+
return await self.parse_token_response(token_response)
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
logger.error(
|
|
265
|
+
"claude_oauth_token_refresh_failed",
|
|
266
|
+
error=str(e),
|
|
267
|
+
exc_info=e,
|
|
268
|
+
category="auth",
|
|
269
|
+
)
|
|
270
|
+
raise OAuthError(f"Failed to refresh Claude token: {e}") from e
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""OAuth configuration for Claude OAuth plugin."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ClaudeOAuthConfig(BaseModel):
|
|
7
|
+
"""OAuth-specific configuration for Claude."""
|
|
8
|
+
|
|
9
|
+
enabled: bool = Field(
|
|
10
|
+
default=True,
|
|
11
|
+
description="Enablded the plugin",
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
base_url: str = Field(
|
|
15
|
+
default="https://console.anthropic.com",
|
|
16
|
+
description="Base URL for OAuth API endpoints",
|
|
17
|
+
)
|
|
18
|
+
token_url: str = Field(
|
|
19
|
+
default="https://console.anthropic.com/v1/oauth/token",
|
|
20
|
+
description="OAuth token endpoint URL",
|
|
21
|
+
)
|
|
22
|
+
authorize_url: str = Field(
|
|
23
|
+
default="https://claude.ai/oauth/authorize",
|
|
24
|
+
description="OAuth authorization endpoint URL",
|
|
25
|
+
)
|
|
26
|
+
profile_url: str = Field(
|
|
27
|
+
default="https://api.anthropic.com/api/oauth/profile",
|
|
28
|
+
description="OAuth profile endpoint URL",
|
|
29
|
+
)
|
|
30
|
+
client_id: str = Field(
|
|
31
|
+
default="9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
32
|
+
description="OAuth client ID",
|
|
33
|
+
)
|
|
34
|
+
redirect_uri: str | None = Field(
|
|
35
|
+
# default="https://console.anthropic.com/oauth/code/callback",
|
|
36
|
+
default=None,
|
|
37
|
+
# default="http://localhost:54545/callback",
|
|
38
|
+
description="OAuth redirect URI",
|
|
39
|
+
)
|
|
40
|
+
scopes: list[str] = Field(
|
|
41
|
+
default_factory=lambda: [
|
|
42
|
+
"org:create_api_key",
|
|
43
|
+
"user:profile",
|
|
44
|
+
"user:inference",
|
|
45
|
+
],
|
|
46
|
+
description="OAuth scopes to request",
|
|
47
|
+
)
|
|
48
|
+
headers: dict[str, str] = Field(
|
|
49
|
+
default_factory=lambda: {
|
|
50
|
+
# "anthropic-beta": "oauth-2025-04-20",
|
|
51
|
+
# "User-Agent": "Claude-Code/1.0.43", # Match default user agent in config
|
|
52
|
+
},
|
|
53
|
+
description="Additional headers for OAuth requests",
|
|
54
|
+
)
|
|
55
|
+
request_timeout: int = Field(
|
|
56
|
+
default=30,
|
|
57
|
+
description="Timeout in seconds for OAuth requests",
|
|
58
|
+
)
|
|
59
|
+
callback_timeout: int = Field(
|
|
60
|
+
default=300,
|
|
61
|
+
description="Timeout in seconds for OAuth callback",
|
|
62
|
+
ge=60,
|
|
63
|
+
le=600,
|
|
64
|
+
)
|
|
65
|
+
callback_port: int = Field(
|
|
66
|
+
default=35593,
|
|
67
|
+
# default=54545,
|
|
68
|
+
description="Port for OAuth callback server",
|
|
69
|
+
ge=1024,
|
|
70
|
+
le=65535,
|
|
71
|
+
)
|
|
72
|
+
use_pkce: bool = Field(
|
|
73
|
+
default=True,
|
|
74
|
+
description="Whether to use PKCE flow (required for Claude OAuth)",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def get_redirect_uri(self) -> str:
|
|
78
|
+
"""Return redirect URI, auto-generated from callback_port when unset.
|
|
79
|
+
|
|
80
|
+
Uses the standard plugin callback path: `/callback`.
|
|
81
|
+
"""
|
|
82
|
+
if self.redirect_uri:
|
|
83
|
+
return self.redirect_uri
|
|
84
|
+
return f"http://localhost:{self.callback_port}/callback"
|