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
ccproxy/config/claude.py
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
"""Claude-specific configuration settings."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import shutil
|
|
5
|
-
from enum import Enum
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
8
|
-
|
|
9
|
-
import structlog
|
|
10
|
-
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
11
|
-
|
|
12
|
-
from ccproxy.core.async_utils import get_package_dir, patched_typing
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# For further information visit https://errors.pydantic.dev/2.11/u/typed-dict-version
|
|
16
|
-
with patched_typing():
|
|
17
|
-
from claude_code_sdk import ClaudeCodeOptions # noqa: E402
|
|
18
|
-
|
|
19
|
-
logger = structlog.get_logger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _create_default_claude_code_options(
|
|
23
|
-
builtin_permissions: bool = True,
|
|
24
|
-
continue_conversation: bool = False,
|
|
25
|
-
) -> ClaudeCodeOptions:
|
|
26
|
-
"""Create ClaudeCodeOptions with default values.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
builtin_permissions: Whether to include built-in permission handling defaults
|
|
30
|
-
"""
|
|
31
|
-
if builtin_permissions:
|
|
32
|
-
return ClaudeCodeOptions(
|
|
33
|
-
continue_conversation=continue_conversation,
|
|
34
|
-
mcp_servers={
|
|
35
|
-
"confirmation": {"type": "sse", "url": "http://127.0.0.1:8000/mcp"}
|
|
36
|
-
},
|
|
37
|
-
permission_prompt_tool_name="mcp__confirmation__check_permission",
|
|
38
|
-
)
|
|
39
|
-
else:
|
|
40
|
-
return ClaudeCodeOptions(
|
|
41
|
-
mcp_servers={},
|
|
42
|
-
permission_prompt_tool_name=None,
|
|
43
|
-
continue_conversation=continue_conversation,
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class SDKMessageMode(str, Enum):
|
|
48
|
-
"""Modes for handling SDK messages from Claude SDK.
|
|
49
|
-
|
|
50
|
-
- forward: Forward SDK content blocks directly with original types and metadata
|
|
51
|
-
- ignore: Skip SDK messages and blocks completely
|
|
52
|
-
- formatted: Format as XML tags with JSON data in text deltas
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
FORWARD = "forward"
|
|
56
|
-
IGNORE = "ignore"
|
|
57
|
-
FORMATTED = "formatted"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class SystemPromptInjectionMode(str, Enum):
|
|
61
|
-
"""Modes for system prompt injection.
|
|
62
|
-
|
|
63
|
-
- minimal: Only inject Claude Code identification prompt
|
|
64
|
-
- full: Inject all detected system messages from Claude CLI
|
|
65
|
-
"""
|
|
66
|
-
|
|
67
|
-
MINIMAL = "minimal"
|
|
68
|
-
FULL = "full"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class SessionPoolSettings(BaseModel):
|
|
72
|
-
"""Session pool configuration settings."""
|
|
73
|
-
|
|
74
|
-
enabled: bool = Field(
|
|
75
|
-
default=True, description="Enable session-aware persistent pooling"
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
session_ttl: int = Field(
|
|
79
|
-
default=3600,
|
|
80
|
-
ge=60,
|
|
81
|
-
le=86400,
|
|
82
|
-
description="Session time-to-live in seconds (1 minute to 24 hours)",
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
max_sessions: int = Field(
|
|
86
|
-
default=1000,
|
|
87
|
-
ge=1,
|
|
88
|
-
le=10000,
|
|
89
|
-
description="Maximum number of concurrent sessions",
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
cleanup_interval: int = Field(
|
|
93
|
-
default=300,
|
|
94
|
-
ge=30,
|
|
95
|
-
le=3600,
|
|
96
|
-
description="Session cleanup interval in seconds (30 seconds to 1 hour)",
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
idle_threshold: int = Field(
|
|
100
|
-
default=600,
|
|
101
|
-
ge=60,
|
|
102
|
-
le=7200,
|
|
103
|
-
description="Session idle threshold in seconds (1 minute to 2 hours)",
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
connection_recovery: bool = Field(
|
|
107
|
-
default=True,
|
|
108
|
-
description="Enable automatic connection recovery for unhealthy sessions",
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
stream_first_chunk_timeout: int = Field(
|
|
112
|
-
default=3,
|
|
113
|
-
ge=1,
|
|
114
|
-
le=30,
|
|
115
|
-
description="Stream first chunk timeout in seconds (1-30 seconds)",
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
stream_ongoing_timeout: int = Field(
|
|
119
|
-
default=60,
|
|
120
|
-
ge=10,
|
|
121
|
-
le=600,
|
|
122
|
-
description="Stream ongoing timeout in seconds after first chunk (10 seconds to 10 minutes)",
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
stream_interrupt_timeout: int = Field(
|
|
126
|
-
default=10,
|
|
127
|
-
ge=2,
|
|
128
|
-
le=60,
|
|
129
|
-
description="Stream interrupt timeout in seconds for SDK and worker operations (2-60 seconds)",
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
@model_validator(mode="after")
|
|
133
|
-
def validate_timeout_hierarchy(self) -> "SessionPoolSettings":
|
|
134
|
-
"""Ensure stream timeouts are less than session TTL."""
|
|
135
|
-
if self.stream_ongoing_timeout >= self.session_ttl:
|
|
136
|
-
raise ValueError(
|
|
137
|
-
f"stream_ongoing_timeout ({self.stream_ongoing_timeout}s) must be less than session_ttl ({self.session_ttl}s)"
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
if self.stream_first_chunk_timeout >= self.stream_ongoing_timeout:
|
|
141
|
-
raise ValueError(
|
|
142
|
-
f"stream_first_chunk_timeout ({self.stream_first_chunk_timeout}s) must be less than stream_ongoing_timeout ({self.stream_ongoing_timeout}s)"
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
return self
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
class ClaudeSettings(BaseModel):
|
|
149
|
-
"""Claude-specific configuration settings."""
|
|
150
|
-
|
|
151
|
-
cli_path: str | None = Field(
|
|
152
|
-
default=None,
|
|
153
|
-
description="Path to Claude CLI executable",
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
builtin_permissions: bool = Field(
|
|
157
|
-
default=True,
|
|
158
|
-
description="Whether to enable built-in permission handling infrastructure (MCP server and SSE endpoints). When disabled, users can still configure custom MCP servers and permission tools.",
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
code_options: ClaudeCodeOptions | None = Field(
|
|
162
|
-
default=None,
|
|
163
|
-
description="Claude Code SDK options configuration",
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
sdk_message_mode: SDKMessageMode = Field(
|
|
167
|
-
default=SDKMessageMode.FORWARD,
|
|
168
|
-
description="Mode for handling SDK messages from Claude SDK. Options: forward (direct SDK blocks), ignore (skip blocks), formatted (XML tags with JSON data)",
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
system_prompt_injection_mode: SystemPromptInjectionMode = Field(
|
|
172
|
-
default=SystemPromptInjectionMode.MINIMAL,
|
|
173
|
-
description="Mode for system prompt injection. Options: minimal (Claude Code ID only), full (all detected system messages)",
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
pretty_format: bool = Field(
|
|
177
|
-
default=True,
|
|
178
|
-
description="Whether to use pretty formatting (indented JSON, newlines after XML tags, unescaped content). When false: compact JSON, no newlines, escaped content between XML tags",
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
sdk_session_pool: SessionPoolSettings = Field(
|
|
182
|
-
default_factory=SessionPoolSettings,
|
|
183
|
-
description="Configuration settings for session-aware SDK client pooling",
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
@field_validator("cli_path")
|
|
187
|
-
@classmethod
|
|
188
|
-
def validate_claude_cli_path(cls, v: str | None) -> str | None:
|
|
189
|
-
"""Validate Claude CLI path if provided."""
|
|
190
|
-
if v is not None:
|
|
191
|
-
path = Path(v)
|
|
192
|
-
if not path.exists():
|
|
193
|
-
raise ValueError(f"Claude CLI path does not exist: {v}")
|
|
194
|
-
if not path.is_file():
|
|
195
|
-
raise ValueError(f"Claude CLI path is not a file: {v}")
|
|
196
|
-
if not os.access(path, os.X_OK):
|
|
197
|
-
raise ValueError(f"Claude CLI path is not executable: {v}")
|
|
198
|
-
return v
|
|
199
|
-
|
|
200
|
-
@field_validator("code_options", mode="before")
|
|
201
|
-
@classmethod
|
|
202
|
-
def validate_claude_code_options(cls, v: Any, info: Any) -> Any:
|
|
203
|
-
"""Validate and convert Claude Code options."""
|
|
204
|
-
# Get builtin_permissions setting from the model data
|
|
205
|
-
builtin_permissions = True # default
|
|
206
|
-
if info.data and "builtin_permissions" in info.data:
|
|
207
|
-
builtin_permissions = info.data["builtin_permissions"]
|
|
208
|
-
|
|
209
|
-
if v is None:
|
|
210
|
-
# Create instance with default values based on builtin_permissions
|
|
211
|
-
return _create_default_claude_code_options(builtin_permissions)
|
|
212
|
-
|
|
213
|
-
# If it's already a ClaudeCodeOptions instance, return as-is
|
|
214
|
-
if isinstance(v, ClaudeCodeOptions):
|
|
215
|
-
return v
|
|
216
|
-
|
|
217
|
-
# If it's an empty dict, treat it like None and use defaults
|
|
218
|
-
if isinstance(v, dict) and not v:
|
|
219
|
-
return _create_default_claude_code_options(builtin_permissions)
|
|
220
|
-
|
|
221
|
-
# For non-empty dicts, merge with defaults instead of replacing them
|
|
222
|
-
if isinstance(v, dict):
|
|
223
|
-
# Start with default values based on builtin_permissions
|
|
224
|
-
defaults = _create_default_claude_code_options(builtin_permissions)
|
|
225
|
-
|
|
226
|
-
# Extract default values as a dict for merging
|
|
227
|
-
default_values = {
|
|
228
|
-
"mcp_servers": dict(defaults.mcp_servers)
|
|
229
|
-
if isinstance(defaults.mcp_servers, dict)
|
|
230
|
-
else {},
|
|
231
|
-
"permission_prompt_tool_name": defaults.permission_prompt_tool_name,
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
# Add other default attributes if they exist
|
|
235
|
-
for attr in [
|
|
236
|
-
"max_thinking_tokens",
|
|
237
|
-
"allowed_tools",
|
|
238
|
-
"disallowed_tools",
|
|
239
|
-
"cwd",
|
|
240
|
-
"append_system_prompt",
|
|
241
|
-
"max_turns",
|
|
242
|
-
"continue_conversation",
|
|
243
|
-
"permission_mode",
|
|
244
|
-
"model",
|
|
245
|
-
"system_prompt",
|
|
246
|
-
]:
|
|
247
|
-
if hasattr(defaults, attr):
|
|
248
|
-
default_value = getattr(defaults, attr, None)
|
|
249
|
-
if default_value is not None:
|
|
250
|
-
default_values[attr] = default_value
|
|
251
|
-
|
|
252
|
-
# Handle MCP server merging when builtin_permissions is enabled
|
|
253
|
-
if builtin_permissions and "mcp_servers" in v:
|
|
254
|
-
user_mcp_servers = v["mcp_servers"]
|
|
255
|
-
if isinstance(user_mcp_servers, dict):
|
|
256
|
-
# Merge user MCP servers with built-in ones (user takes precedence)
|
|
257
|
-
default_mcp = default_values["mcp_servers"]
|
|
258
|
-
if isinstance(default_mcp, dict):
|
|
259
|
-
merged_mcp_servers = {
|
|
260
|
-
**default_mcp,
|
|
261
|
-
**user_mcp_servers,
|
|
262
|
-
}
|
|
263
|
-
v = {**v, "mcp_servers": merged_mcp_servers}
|
|
264
|
-
|
|
265
|
-
# Merge CLI overrides with defaults (CLI overrides take precedence)
|
|
266
|
-
merged_values = {**default_values, **v}
|
|
267
|
-
|
|
268
|
-
return ClaudeCodeOptions(**merged_values)
|
|
269
|
-
|
|
270
|
-
# Try to convert to ClaudeCodeOptions if possible
|
|
271
|
-
if hasattr(v, "model_dump"):
|
|
272
|
-
return ClaudeCodeOptions(**v.model_dump())
|
|
273
|
-
elif hasattr(v, "__dict__"):
|
|
274
|
-
return ClaudeCodeOptions(**v.__dict__)
|
|
275
|
-
|
|
276
|
-
# Fallback: use default values
|
|
277
|
-
return _create_default_claude_code_options(builtin_permissions)
|
|
278
|
-
|
|
279
|
-
@model_validator(mode="after")
|
|
280
|
-
def validate_code_options_after(self) -> "ClaudeSettings":
|
|
281
|
-
"""Ensure code_options is properly initialized after field validation."""
|
|
282
|
-
if self.code_options is None:
|
|
283
|
-
self.code_options = _create_default_claude_code_options(
|
|
284
|
-
self.builtin_permissions
|
|
285
|
-
)
|
|
286
|
-
return self
|
|
287
|
-
|
|
288
|
-
def find_claude_cli(self) -> tuple[str | None, bool]:
|
|
289
|
-
"""Find Claude CLI executable in PATH or specified location.
|
|
290
|
-
|
|
291
|
-
Returns:
|
|
292
|
-
tuple: (path_to_claude, found_in_path)
|
|
293
|
-
"""
|
|
294
|
-
if self.cli_path:
|
|
295
|
-
return self.cli_path, False
|
|
296
|
-
|
|
297
|
-
# Try to find claude in PATH
|
|
298
|
-
claude_path = shutil.which("claude")
|
|
299
|
-
if claude_path:
|
|
300
|
-
return claude_path, True
|
|
301
|
-
|
|
302
|
-
# Common installation paths (in order of preference)
|
|
303
|
-
common_paths = [
|
|
304
|
-
# User-specific Claude installation
|
|
305
|
-
Path.home() / ".claude" / "local" / "claude",
|
|
306
|
-
# User's global node_modules (npm install -g)
|
|
307
|
-
Path.home() / "node_modules" / ".bin" / "claude",
|
|
308
|
-
# Package installation directory node_modules
|
|
309
|
-
get_package_dir() / "node_modules" / ".bin" / "claude",
|
|
310
|
-
# Current working directory node_modules
|
|
311
|
-
Path.cwd() / "node_modules" / ".bin" / "claude",
|
|
312
|
-
# System-wide installations
|
|
313
|
-
Path("/usr/local/bin/claude"),
|
|
314
|
-
Path("/opt/homebrew/bin/claude"),
|
|
315
|
-
]
|
|
316
|
-
|
|
317
|
-
for path in common_paths:
|
|
318
|
-
if path.exists() and path.is_file() and os.access(path, os.X_OK):
|
|
319
|
-
return str(path), False
|
|
320
|
-
|
|
321
|
-
return None, False
|
|
322
|
-
|
|
323
|
-
def get_searched_paths(self) -> list[str]:
|
|
324
|
-
"""Get list of paths that would be searched for Claude CLI auto-detection."""
|
|
325
|
-
paths = []
|
|
326
|
-
|
|
327
|
-
# PATH search
|
|
328
|
-
paths.append("PATH environment variable")
|
|
329
|
-
|
|
330
|
-
# Common installation paths (in order of preference)
|
|
331
|
-
common_paths = [
|
|
332
|
-
# User-specific Claude installation
|
|
333
|
-
Path.home() / ".claude" / "local" / "claude",
|
|
334
|
-
# User's global node_modules (npm install -g)
|
|
335
|
-
Path.home() / "node_modules" / ".bin" / "claude",
|
|
336
|
-
# Package installation directory node_modules
|
|
337
|
-
get_package_dir() / "node_modules" / ".bin" / "claude",
|
|
338
|
-
# Current working directory node_modules
|
|
339
|
-
Path.cwd() / "node_modules" / ".bin" / "claude",
|
|
340
|
-
# System-wide installations
|
|
341
|
-
Path("/usr/local/bin/claude"),
|
|
342
|
-
Path("/opt/homebrew/bin/claude"),
|
|
343
|
-
]
|
|
344
|
-
|
|
345
|
-
for path in common_paths:
|
|
346
|
-
paths.append(str(path))
|
|
347
|
-
|
|
348
|
-
return paths
|
ccproxy/config/cors.py
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"""CORS configuration settings."""
|
|
2
|
-
|
|
3
|
-
from pydantic import BaseModel, Field, field_validator
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class CORSSettings(BaseModel):
|
|
7
|
-
"""CORS-specific configuration settings."""
|
|
8
|
-
|
|
9
|
-
origins: list[str] = Field(
|
|
10
|
-
default_factory=lambda: ["*"],
|
|
11
|
-
description="CORS allowed origins",
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
credentials: bool = Field(
|
|
15
|
-
default=True,
|
|
16
|
-
description="CORS allow credentials",
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
methods: list[str] = Field(
|
|
20
|
-
default_factory=lambda: ["*"],
|
|
21
|
-
description="CORS allowed methods",
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
headers: list[str] = Field(
|
|
25
|
-
default_factory=lambda: ["*"],
|
|
26
|
-
description="CORS allowed headers",
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
origin_regex: str | None = Field(
|
|
30
|
-
default=None,
|
|
31
|
-
description="CORS origin regex pattern",
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
expose_headers: list[str] = Field(
|
|
35
|
-
default_factory=list,
|
|
36
|
-
description="CORS exposed headers",
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
max_age: int = Field(
|
|
40
|
-
default=600,
|
|
41
|
-
description="CORS preflight max age in seconds",
|
|
42
|
-
ge=0,
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
@field_validator("origins", mode="before")
|
|
46
|
-
@classmethod
|
|
47
|
-
def validate_cors_origins(cls, v: str | list[str]) -> list[str]:
|
|
48
|
-
"""Parse CORS origins from string or list."""
|
|
49
|
-
if isinstance(v, str):
|
|
50
|
-
# Split comma-separated string
|
|
51
|
-
return [origin.strip() for origin in v.split(",") if origin.strip()]
|
|
52
|
-
return v
|
|
53
|
-
|
|
54
|
-
@field_validator("methods", mode="before")
|
|
55
|
-
@classmethod
|
|
56
|
-
def validate_cors_methods(cls, v: str | list[str]) -> list[str]:
|
|
57
|
-
"""Parse CORS methods from string or list."""
|
|
58
|
-
if isinstance(v, str):
|
|
59
|
-
# Split comma-separated string
|
|
60
|
-
return [method.strip().upper() for method in v.split(",") if method.strip()]
|
|
61
|
-
return [method.upper() for method in v]
|
|
62
|
-
|
|
63
|
-
@field_validator("headers", mode="before")
|
|
64
|
-
@classmethod
|
|
65
|
-
def validate_cors_headers(cls, v: str | list[str]) -> list[str]:
|
|
66
|
-
"""Parse CORS headers from string or list."""
|
|
67
|
-
if isinstance(v, str):
|
|
68
|
-
# Split comma-separated string
|
|
69
|
-
return [header.strip() for header in v.split(",") if header.strip()]
|
|
70
|
-
return v
|
|
71
|
-
|
|
72
|
-
@field_validator("expose_headers", mode="before")
|
|
73
|
-
@classmethod
|
|
74
|
-
def validate_cors_expose_headers(cls, v: str | list[str]) -> list[str]:
|
|
75
|
-
"""Parse CORS expose headers from string or list."""
|
|
76
|
-
if isinstance(v, str):
|
|
77
|
-
# Split comma-separated string
|
|
78
|
-
return [header.strip() for header in v.split(",") if header.strip()]
|
|
79
|
-
return v
|
ccproxy/config/discovery.py
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
from ccproxy.core.system import get_xdg_cache_home, get_xdg_config_home
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def find_toml_config_file() -> Path | None:
|
|
7
|
-
"""Find the TOML configuration file for ccproxy.
|
|
8
|
-
|
|
9
|
-
Searches in the following order:
|
|
10
|
-
1. .ccproxy.toml in current directory
|
|
11
|
-
2. ccproxy.toml in git repository root (if in a git repo)
|
|
12
|
-
3. config.toml in XDG_CONFIG_HOME/ccproxy/
|
|
13
|
-
"""
|
|
14
|
-
# Check current directory first
|
|
15
|
-
candidates = [
|
|
16
|
-
Path(".ccproxy.toml").resolve(),
|
|
17
|
-
Path("ccproxy.toml").resolve(),
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
# Check git repo root
|
|
21
|
-
git_root = find_git_root()
|
|
22
|
-
if git_root:
|
|
23
|
-
candidates.extend(
|
|
24
|
-
[
|
|
25
|
-
git_root / ".ccproxy.toml",
|
|
26
|
-
git_root / "ccproxy.toml",
|
|
27
|
-
]
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
# Check XDG config directory
|
|
31
|
-
config_dir = get_ccproxy_config_dir()
|
|
32
|
-
candidates.append(config_dir / "config.toml")
|
|
33
|
-
|
|
34
|
-
# Return first existing file
|
|
35
|
-
for candidate in candidates:
|
|
36
|
-
if candidate.exists() and candidate.is_file():
|
|
37
|
-
return candidate
|
|
38
|
-
|
|
39
|
-
return None
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def find_git_root(path: Path | None = None) -> Path | None:
|
|
43
|
-
"""Find the root directory of a git repository."""
|
|
44
|
-
import subprocess
|
|
45
|
-
|
|
46
|
-
if path is None:
|
|
47
|
-
path = Path.cwd()
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
result = subprocess.run(
|
|
51
|
-
["git", "rev-parse", "--show-toplevel"],
|
|
52
|
-
cwd=path,
|
|
53
|
-
capture_output=True,
|
|
54
|
-
text=True,
|
|
55
|
-
check=True,
|
|
56
|
-
)
|
|
57
|
-
return Path(result.stdout.strip())
|
|
58
|
-
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
59
|
-
return None
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def get_ccproxy_config_dir() -> Path:
|
|
63
|
-
"""Get the ccproxy configuration directory.
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
Path to the ccproxy configuration directory within XDG_CONFIG_HOME.
|
|
67
|
-
"""
|
|
68
|
-
return get_xdg_config_home() / "ccproxy"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def get_claude_cli_config_dir() -> Path:
|
|
72
|
-
"""Get the Claude CLI configuration directory.
|
|
73
|
-
|
|
74
|
-
Returns:
|
|
75
|
-
Path to the Claude CLI configuration directory within XDG_CONFIG_HOME.
|
|
76
|
-
"""
|
|
77
|
-
return get_xdg_config_home() / "claude"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def get_claude_docker_home_dir() -> Path:
|
|
81
|
-
"""Get the Claude Docker home directory.
|
|
82
|
-
|
|
83
|
-
Returns:
|
|
84
|
-
Path to the Claude Docker home directory within XDG_DATA_HOME.
|
|
85
|
-
"""
|
|
86
|
-
return get_ccproxy_config_dir() / "home"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def get_ccproxy_cache_dir() -> Path:
|
|
90
|
-
"""Get the ccproxy cache directory.
|
|
91
|
-
|
|
92
|
-
Returns:
|
|
93
|
-
Path to the ccproxy cache directory within XDG_CACHE_HOME.
|
|
94
|
-
"""
|
|
95
|
-
return get_xdg_cache_home() / "ccproxy"
|