ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0a4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccproxy/api/__init__.py +1 -15
- ccproxy/api/app.py +434 -219
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +144 -168
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +388 -524
- ccproxy/api/middleware/hooks.py +563 -0
- ccproxy/api/middleware/normalize_headers.py +59 -0
- ccproxy/api/middleware/request_id.py +35 -16
- ccproxy/api/middleware/streaming_hooks.py +292 -0
- ccproxy/api/routes/__init__.py +5 -14
- ccproxy/api/routes/health.py +39 -672
- ccproxy/api/routes/plugins.py +277 -0
- ccproxy/auth/__init__.py +2 -19
- ccproxy/auth/bearer.py +25 -15
- ccproxy/auth/dependencies.py +123 -157
- ccproxy/auth/exceptions.py +0 -12
- ccproxy/auth/manager.py +35 -49
- ccproxy/auth/managers/__init__.py +10 -0
- ccproxy/auth/managers/base.py +523 -0
- ccproxy/auth/managers/base_enhanced.py +63 -0
- ccproxy/auth/managers/token_snapshot.py +77 -0
- ccproxy/auth/models/base.py +65 -0
- ccproxy/auth/models/credentials.py +40 -0
- ccproxy/auth/oauth/__init__.py +4 -18
- ccproxy/auth/oauth/base.py +533 -0
- ccproxy/auth/oauth/cli_errors.py +37 -0
- ccproxy/auth/oauth/flows.py +430 -0
- ccproxy/auth/oauth/protocol.py +366 -0
- ccproxy/auth/oauth/registry.py +408 -0
- ccproxy/auth/oauth/router.py +396 -0
- ccproxy/auth/oauth/routes.py +186 -113
- ccproxy/auth/oauth/session.py +151 -0
- ccproxy/auth/oauth/templates.py +342 -0
- ccproxy/auth/storage/__init__.py +2 -5
- ccproxy/auth/storage/base.py +279 -5
- ccproxy/auth/storage/generic.py +134 -0
- ccproxy/cli/__init__.py +1 -2
- ccproxy/cli/_settings_help.py +351 -0
- ccproxy/cli/commands/auth.py +1519 -793
- ccproxy/cli/commands/config/commands.py +209 -276
- ccproxy/cli/commands/plugins.py +669 -0
- ccproxy/cli/commands/serve.py +75 -810
- ccproxy/cli/commands/status.py +254 -0
- ccproxy/cli/decorators.py +83 -0
- ccproxy/cli/helpers.py +22 -60
- ccproxy/cli/main.py +359 -10
- ccproxy/cli/options/claude_options.py +0 -25
- ccproxy/config/__init__.py +7 -11
- ccproxy/config/core.py +227 -0
- ccproxy/config/env_generator.py +232 -0
- ccproxy/config/runtime.py +67 -0
- ccproxy/config/security.py +36 -3
- ccproxy/config/settings.py +382 -441
- ccproxy/config/toml_generator.py +299 -0
- ccproxy/config/utils.py +452 -0
- ccproxy/core/__init__.py +7 -271
- ccproxy/{_version.py → core/_version.py} +16 -3
- ccproxy/core/async_task_manager.py +516 -0
- ccproxy/core/async_utils.py +47 -14
- ccproxy/core/auth/__init__.py +6 -0
- ccproxy/core/constants.py +16 -50
- ccproxy/core/errors.py +53 -0
- ccproxy/core/id_utils.py +20 -0
- ccproxy/core/interfaces.py +16 -123
- ccproxy/core/logging.py +473 -18
- ccproxy/core/plugins/__init__.py +77 -0
- ccproxy/core/plugins/cli_discovery.py +211 -0
- ccproxy/core/plugins/declaration.py +455 -0
- ccproxy/core/plugins/discovery.py +604 -0
- ccproxy/core/plugins/factories.py +967 -0
- ccproxy/core/plugins/hooks/__init__.py +30 -0
- ccproxy/core/plugins/hooks/base.py +58 -0
- ccproxy/core/plugins/hooks/events.py +46 -0
- ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
- ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
- ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
- ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
- ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
- ccproxy/core/plugins/hooks/layers.py +44 -0
- ccproxy/core/plugins/hooks/manager.py +186 -0
- ccproxy/core/plugins/hooks/registry.py +139 -0
- ccproxy/core/plugins/hooks/thread_manager.py +203 -0
- ccproxy/core/plugins/hooks/types.py +22 -0
- ccproxy/core/plugins/interfaces.py +416 -0
- ccproxy/core/plugins/loader.py +166 -0
- ccproxy/core/plugins/middleware.py +233 -0
- ccproxy/core/plugins/models.py +59 -0
- ccproxy/core/plugins/protocol.py +180 -0
- ccproxy/core/plugins/runtime.py +519 -0
- ccproxy/{observability/context.py → core/request_context.py} +137 -94
- ccproxy/core/status_report.py +211 -0
- ccproxy/core/transformers.py +13 -8
- ccproxy/data/claude_headers_fallback.json +540 -19
- ccproxy/data/codex_headers_fallback.json +114 -7
- ccproxy/http/__init__.py +30 -0
- ccproxy/http/base.py +95 -0
- ccproxy/http/client.py +323 -0
- ccproxy/http/hooks.py +642 -0
- ccproxy/http/pool.py +279 -0
- ccproxy/llms/formatters/__init__.py +7 -0
- ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
- ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
- ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
- ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
- ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
- ccproxy/llms/formatters/base.py +140 -0
- ccproxy/llms/formatters/base_model.py +33 -0
- ccproxy/llms/formatters/common/__init__.py +51 -0
- ccproxy/llms/formatters/common/identifiers.py +48 -0
- ccproxy/llms/formatters/common/streams.py +254 -0
- ccproxy/llms/formatters/common/thinking.py +74 -0
- ccproxy/llms/formatters/common/usage.py +135 -0
- ccproxy/llms/formatters/constants.py +55 -0
- ccproxy/llms/formatters/context.py +116 -0
- ccproxy/llms/formatters/mapping.py +33 -0
- ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
- ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
- ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
- ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
- ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
- ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
- ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
- ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
- ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
- ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
- ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
- ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
- ccproxy/llms/formatters/utils.py +306 -0
- ccproxy/llms/models/__init__.py +9 -0
- ccproxy/llms/models/anthropic.py +619 -0
- ccproxy/llms/models/openai.py +844 -0
- ccproxy/llms/streaming/__init__.py +26 -0
- ccproxy/llms/streaming/accumulators.py +1074 -0
- ccproxy/llms/streaming/formatters.py +251 -0
- ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
- ccproxy/models/__init__.py +8 -159
- ccproxy/models/detection.py +92 -193
- ccproxy/models/provider.py +75 -0
- ccproxy/plugins/access_log/README.md +32 -0
- ccproxy/plugins/access_log/__init__.py +20 -0
- ccproxy/plugins/access_log/config.py +33 -0
- ccproxy/plugins/access_log/formatter.py +126 -0
- ccproxy/plugins/access_log/hook.py +763 -0
- ccproxy/plugins/access_log/logger.py +254 -0
- ccproxy/plugins/access_log/plugin.py +137 -0
- ccproxy/plugins/access_log/writer.py +109 -0
- ccproxy/plugins/analytics/README.md +24 -0
- ccproxy/plugins/analytics/__init__.py +1 -0
- ccproxy/plugins/analytics/config.py +5 -0
- ccproxy/plugins/analytics/ingest.py +85 -0
- ccproxy/plugins/analytics/models.py +97 -0
- ccproxy/plugins/analytics/plugin.py +121 -0
- ccproxy/plugins/analytics/routes.py +163 -0
- ccproxy/plugins/analytics/service.py +284 -0
- ccproxy/plugins/claude_api/README.md +29 -0
- ccproxy/plugins/claude_api/__init__.py +10 -0
- ccproxy/plugins/claude_api/adapter.py +829 -0
- ccproxy/plugins/claude_api/config.py +52 -0
- ccproxy/plugins/claude_api/detection_service.py +461 -0
- ccproxy/plugins/claude_api/health.py +175 -0
- ccproxy/plugins/claude_api/hooks.py +284 -0
- ccproxy/plugins/claude_api/models.py +256 -0
- ccproxy/plugins/claude_api/plugin.py +298 -0
- ccproxy/plugins/claude_api/routes.py +118 -0
- ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
- ccproxy/plugins/claude_api/tasks.py +84 -0
- ccproxy/plugins/claude_sdk/README.md +35 -0
- ccproxy/plugins/claude_sdk/__init__.py +80 -0
- ccproxy/plugins/claude_sdk/adapter.py +749 -0
- ccproxy/plugins/claude_sdk/auth.py +57 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
- ccproxy/plugins/claude_sdk/config.py +210 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
- ccproxy/plugins/claude_sdk/detection_service.py +163 -0
- ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
- ccproxy/plugins/claude_sdk/health.py +113 -0
- ccproxy/plugins/claude_sdk/hooks.py +115 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
- ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
- ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
- ccproxy/plugins/claude_sdk/options.py +154 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
- ccproxy/plugins/claude_sdk/plugin.py +269 -0
- ccproxy/plugins/claude_sdk/routes.py +104 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
- ccproxy/plugins/claude_sdk/session_pool.py +700 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
- ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
- ccproxy/plugins/claude_sdk/tasks.py +97 -0
- ccproxy/plugins/claude_shared/README.md +18 -0
- ccproxy/plugins/claude_shared/__init__.py +12 -0
- ccproxy/plugins/claude_shared/model_defaults.py +171 -0
- ccproxy/plugins/codex/README.md +35 -0
- ccproxy/plugins/codex/__init__.py +6 -0
- ccproxy/plugins/codex/adapter.py +635 -0
- ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
- ccproxy/plugins/codex/detection_service.py +544 -0
- ccproxy/plugins/codex/health.py +162 -0
- ccproxy/plugins/codex/hooks.py +263 -0
- ccproxy/plugins/codex/model_defaults.py +39 -0
- ccproxy/plugins/codex/models.py +263 -0
- ccproxy/plugins/codex/plugin.py +275 -0
- ccproxy/plugins/codex/routes.py +129 -0
- ccproxy/plugins/codex/streaming_metrics.py +324 -0
- ccproxy/plugins/codex/tasks.py +106 -0
- ccproxy/plugins/codex/utils/__init__.py +1 -0
- ccproxy/plugins/codex/utils/sse_parser.py +106 -0
- ccproxy/plugins/command_replay/README.md +34 -0
- ccproxy/plugins/command_replay/__init__.py +17 -0
- ccproxy/plugins/command_replay/config.py +133 -0
- ccproxy/plugins/command_replay/formatter.py +432 -0
- ccproxy/plugins/command_replay/hook.py +294 -0
- ccproxy/plugins/command_replay/plugin.py +161 -0
- ccproxy/plugins/copilot/README.md +39 -0
- ccproxy/plugins/copilot/__init__.py +11 -0
- ccproxy/plugins/copilot/adapter.py +465 -0
- ccproxy/plugins/copilot/config.py +155 -0
- ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
- ccproxy/plugins/copilot/detection_service.py +255 -0
- ccproxy/plugins/copilot/manager.py +275 -0
- ccproxy/plugins/copilot/model_defaults.py +284 -0
- ccproxy/plugins/copilot/models.py +148 -0
- ccproxy/plugins/copilot/oauth/__init__.py +16 -0
- ccproxy/plugins/copilot/oauth/client.py +494 -0
- ccproxy/plugins/copilot/oauth/models.py +385 -0
- ccproxy/plugins/copilot/oauth/provider.py +602 -0
- ccproxy/plugins/copilot/oauth/storage.py +170 -0
- ccproxy/plugins/copilot/plugin.py +360 -0
- ccproxy/plugins/copilot/routes.py +294 -0
- ccproxy/plugins/credential_balancer/README.md +124 -0
- ccproxy/plugins/credential_balancer/__init__.py +6 -0
- ccproxy/plugins/credential_balancer/config.py +270 -0
- ccproxy/plugins/credential_balancer/factory.py +415 -0
- ccproxy/plugins/credential_balancer/hook.py +51 -0
- ccproxy/plugins/credential_balancer/manager.py +587 -0
- ccproxy/plugins/credential_balancer/plugin.py +146 -0
- ccproxy/plugins/dashboard/README.md +25 -0
- ccproxy/plugins/dashboard/__init__.py +1 -0
- ccproxy/plugins/dashboard/config.py +8 -0
- ccproxy/plugins/dashboard/plugin.py +71 -0
- ccproxy/plugins/dashboard/routes.py +67 -0
- ccproxy/plugins/docker/README.md +32 -0
- ccproxy/{docker → plugins/docker}/__init__.py +3 -0
- ccproxy/{docker → plugins/docker}/adapter.py +108 -10
- ccproxy/plugins/docker/config.py +82 -0
- ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
- ccproxy/{docker → plugins/docker}/middleware.py +2 -2
- ccproxy/plugins/docker/plugin.py +198 -0
- ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
- ccproxy/plugins/duckdb_storage/README.md +26 -0
- ccproxy/plugins/duckdb_storage/__init__.py +1 -0
- ccproxy/plugins/duckdb_storage/config.py +22 -0
- ccproxy/plugins/duckdb_storage/plugin.py +128 -0
- ccproxy/plugins/duckdb_storage/routes.py +51 -0
- ccproxy/plugins/duckdb_storage/storage.py +633 -0
- ccproxy/plugins/max_tokens/README.md +38 -0
- ccproxy/plugins/max_tokens/__init__.py +12 -0
- ccproxy/plugins/max_tokens/adapter.py +235 -0
- ccproxy/plugins/max_tokens/config.py +86 -0
- ccproxy/plugins/max_tokens/models.py +53 -0
- ccproxy/plugins/max_tokens/plugin.py +200 -0
- ccproxy/plugins/max_tokens/service.py +271 -0
- ccproxy/plugins/max_tokens/token_limits.json +54 -0
- ccproxy/plugins/metrics/README.md +35 -0
- ccproxy/plugins/metrics/__init__.py +10 -0
- ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
- ccproxy/plugins/metrics/config.py +85 -0
- ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
- ccproxy/plugins/metrics/hook.py +403 -0
- ccproxy/plugins/metrics/plugin.py +268 -0
- ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
- ccproxy/plugins/metrics/routes.py +107 -0
- ccproxy/plugins/metrics/tasks.py +117 -0
- ccproxy/plugins/oauth_claude/README.md +35 -0
- ccproxy/plugins/oauth_claude/__init__.py +14 -0
- ccproxy/plugins/oauth_claude/client.py +270 -0
- ccproxy/plugins/oauth_claude/config.py +84 -0
- ccproxy/plugins/oauth_claude/manager.py +482 -0
- ccproxy/plugins/oauth_claude/models.py +266 -0
- ccproxy/plugins/oauth_claude/plugin.py +149 -0
- ccproxy/plugins/oauth_claude/provider.py +571 -0
- ccproxy/plugins/oauth_claude/storage.py +212 -0
- ccproxy/plugins/oauth_codex/README.md +38 -0
- ccproxy/plugins/oauth_codex/__init__.py +14 -0
- ccproxy/plugins/oauth_codex/client.py +224 -0
- ccproxy/plugins/oauth_codex/config.py +95 -0
- ccproxy/plugins/oauth_codex/manager.py +256 -0
- ccproxy/plugins/oauth_codex/models.py +239 -0
- ccproxy/plugins/oauth_codex/plugin.py +146 -0
- ccproxy/plugins/oauth_codex/provider.py +574 -0
- ccproxy/plugins/oauth_codex/storage.py +92 -0
- ccproxy/plugins/permissions/README.md +28 -0
- ccproxy/plugins/permissions/__init__.py +22 -0
- ccproxy/plugins/permissions/config.py +28 -0
- ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
- ccproxy/plugins/permissions/handlers/protocol.py +33 -0
- ccproxy/plugins/permissions/handlers/terminal.py +675 -0
- ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
- ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
- ccproxy/plugins/permissions/plugin.py +153 -0
- ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
- ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
- ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
- ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
- ccproxy/plugins/pricing/README.md +34 -0
- ccproxy/plugins/pricing/__init__.py +6 -0
- ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
- ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
- ccproxy/plugins/pricing/exceptions.py +35 -0
- ccproxy/plugins/pricing/loader.py +440 -0
- ccproxy/{pricing → plugins/pricing}/models.py +13 -23
- ccproxy/plugins/pricing/plugin.py +169 -0
- ccproxy/plugins/pricing/service.py +191 -0
- ccproxy/plugins/pricing/tasks.py +300 -0
- ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
- ccproxy/plugins/pricing/utils.py +99 -0
- ccproxy/plugins/request_tracer/README.md +40 -0
- ccproxy/plugins/request_tracer/__init__.py +7 -0
- ccproxy/plugins/request_tracer/config.py +120 -0
- ccproxy/plugins/request_tracer/hook.py +415 -0
- ccproxy/plugins/request_tracer/plugin.py +255 -0
- ccproxy/scheduler/__init__.py +2 -14
- ccproxy/scheduler/core.py +26 -41
- ccproxy/scheduler/manager.py +61 -105
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +268 -276
- ccproxy/services/__init__.py +0 -1
- ccproxy/services/adapters/__init__.py +11 -0
- ccproxy/services/adapters/base.py +123 -0
- ccproxy/services/adapters/chain_composer.py +88 -0
- ccproxy/services/adapters/chain_validation.py +44 -0
- ccproxy/services/adapters/chat_accumulator.py +200 -0
- ccproxy/services/adapters/delta_utils.py +142 -0
- ccproxy/services/adapters/format_adapter.py +136 -0
- ccproxy/services/adapters/format_context.py +11 -0
- ccproxy/services/adapters/format_registry.py +158 -0
- ccproxy/services/adapters/http_adapter.py +1045 -0
- ccproxy/services/adapters/mock_adapter.py +118 -0
- ccproxy/services/adapters/protocols.py +35 -0
- ccproxy/services/adapters/simple_converters.py +571 -0
- ccproxy/services/auth_registry.py +180 -0
- ccproxy/services/cache/__init__.py +6 -0
- ccproxy/services/cache/response_cache.py +261 -0
- ccproxy/services/cli_detection.py +437 -0
- ccproxy/services/config/__init__.py +6 -0
- ccproxy/services/config/proxy_configuration.py +111 -0
- ccproxy/services/container.py +256 -0
- ccproxy/services/factories.py +380 -0
- ccproxy/services/handler_config.py +76 -0
- ccproxy/services/interfaces.py +298 -0
- ccproxy/services/mocking/__init__.py +6 -0
- ccproxy/services/mocking/mock_handler.py +291 -0
- ccproxy/services/tracing/__init__.py +7 -0
- ccproxy/services/tracing/interfaces.py +61 -0
- ccproxy/services/tracing/null_tracer.py +57 -0
- ccproxy/streaming/__init__.py +23 -0
- ccproxy/streaming/buffer.py +1056 -0
- ccproxy/streaming/deferred.py +897 -0
- ccproxy/streaming/handler.py +117 -0
- ccproxy/streaming/interfaces.py +77 -0
- ccproxy/streaming/simple_adapter.py +39 -0
- ccproxy/streaming/sse.py +109 -0
- ccproxy/streaming/sse_parser.py +127 -0
- ccproxy/templates/__init__.py +6 -0
- ccproxy/templates/plugin_scaffold.py +695 -0
- ccproxy/testing/endpoints/__init__.py +33 -0
- ccproxy/testing/endpoints/cli.py +215 -0
- ccproxy/testing/endpoints/config.py +874 -0
- ccproxy/testing/endpoints/console.py +57 -0
- ccproxy/testing/endpoints/models.py +100 -0
- ccproxy/testing/endpoints/runner.py +1903 -0
- ccproxy/testing/endpoints/tools.py +308 -0
- ccproxy/testing/mock_responses.py +70 -1
- ccproxy/testing/response_handlers.py +20 -0
- ccproxy/utils/__init__.py +0 -6
- ccproxy/utils/binary_resolver.py +476 -0
- ccproxy/utils/caching.py +327 -0
- ccproxy/utils/cli_logging.py +101 -0
- ccproxy/utils/command_line.py +251 -0
- ccproxy/utils/headers.py +228 -0
- ccproxy/utils/model_mapper.py +120 -0
- ccproxy/utils/startup_helpers.py +68 -446
- ccproxy/utils/version_checker.py +273 -6
- ccproxy_api-0.2.0a4.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0a4.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0a4.dist-info/entry_points.txt +24 -0
- ccproxy/__init__.py +0 -4
- ccproxy/adapters/__init__.py +0 -11
- ccproxy/adapters/base.py +0 -80
- ccproxy/adapters/codex/__init__.py +0 -11
- ccproxy/adapters/openai/__init__.py +0 -42
- ccproxy/adapters/openai/adapter.py +0 -953
- ccproxy/adapters/openai/models.py +0 -412
- ccproxy/adapters/openai/response_adapter.py +0 -355
- ccproxy/adapters/openai/response_models.py +0 -178
- ccproxy/api/middleware/headers.py +0 -49
- ccproxy/api/middleware/logging.py +0 -180
- ccproxy/api/middleware/request_content_logging.py +0 -297
- ccproxy/api/middleware/server_header.py +0 -58
- ccproxy/api/responses.py +0 -89
- ccproxy/api/routes/claude.py +0 -371
- ccproxy/api/routes/codex.py +0 -1251
- ccproxy/api/routes/metrics.py +0 -1029
- ccproxy/api/routes/proxy.py +0 -211
- ccproxy/api/services/__init__.py +0 -6
- ccproxy/auth/conditional.py +0 -84
- ccproxy/auth/credentials_adapter.py +0 -93
- ccproxy/auth/models.py +0 -118
- ccproxy/auth/oauth/models.py +0 -48
- ccproxy/auth/openai/__init__.py +0 -13
- ccproxy/auth/openai/credentials.py +0 -166
- ccproxy/auth/openai/oauth_client.py +0 -334
- ccproxy/auth/openai/storage.py +0 -184
- ccproxy/auth/storage/json_file.py +0 -158
- ccproxy/auth/storage/keyring.py +0 -189
- ccproxy/claude_sdk/__init__.py +0 -18
- ccproxy/claude_sdk/options.py +0 -194
- ccproxy/claude_sdk/session_pool.py +0 -550
- ccproxy/cli/docker/__init__.py +0 -34
- ccproxy/cli/docker/adapter_factory.py +0 -157
- ccproxy/cli/docker/params.py +0 -274
- ccproxy/config/auth.py +0 -153
- ccproxy/config/claude.py +0 -348
- ccproxy/config/cors.py +0 -79
- ccproxy/config/discovery.py +0 -95
- ccproxy/config/docker_settings.py +0 -264
- ccproxy/config/observability.py +0 -158
- ccproxy/config/reverse_proxy.py +0 -31
- ccproxy/config/scheduler.py +0 -108
- ccproxy/config/server.py +0 -86
- ccproxy/config/validators.py +0 -231
- ccproxy/core/codex_transformers.py +0 -389
- ccproxy/core/http.py +0 -328
- ccproxy/core/http_transformers.py +0 -812
- ccproxy/core/proxy.py +0 -143
- ccproxy/core/validators.py +0 -288
- ccproxy/models/errors.py +0 -42
- ccproxy/models/messages.py +0 -269
- ccproxy/models/requests.py +0 -107
- ccproxy/models/responses.py +0 -270
- ccproxy/models/types.py +0 -102
- ccproxy/observability/__init__.py +0 -51
- ccproxy/observability/access_logger.py +0 -457
- ccproxy/observability/sse_events.py +0 -303
- ccproxy/observability/stats_printer.py +0 -753
- ccproxy/observability/storage/__init__.py +0 -1
- ccproxy/observability/storage/duckdb_simple.py +0 -677
- ccproxy/observability/storage/models.py +0 -70
- ccproxy/observability/streaming_response.py +0 -107
- ccproxy/pricing/__init__.py +0 -19
- ccproxy/pricing/loader.py +0 -251
- ccproxy/services/claude_detection_service.py +0 -243
- ccproxy/services/codex_detection_service.py +0 -252
- ccproxy/services/credentials/__init__.py +0 -55
- ccproxy/services/credentials/config.py +0 -105
- ccproxy/services/credentials/manager.py +0 -561
- ccproxy/services/credentials/oauth_client.py +0 -481
- ccproxy/services/proxy_service.py +0 -1827
- ccproxy/static/.keep +0 -0
- ccproxy/utils/cost_calculator.py +0 -210
- ccproxy/utils/disconnection_monitor.py +0 -83
- ccproxy/utils/model_mapping.py +0 -199
- ccproxy/utils/models_provider.py +0 -150
- ccproxy/utils/simple_request_logger.py +0 -284
- ccproxy/utils/streaming_metrics.py +0 -199
- ccproxy_api-0.1.7.dist-info/METADATA +0 -615
- ccproxy_api-0.1.7.dist-info/RECORD +0 -191
- ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
- /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
- /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
- /ccproxy/{docker → plugins/docker}/models.py +0 -0
- /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
- /ccproxy/{docker → plugins/docker}/validators.py +0 -0
- /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
- /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Claude-specific authentication models."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Literal
|
|
7
|
+
|
|
8
|
+
from pydantic import (
|
|
9
|
+
BaseModel,
|
|
10
|
+
ConfigDict,
|
|
11
|
+
Field,
|
|
12
|
+
SecretStr,
|
|
13
|
+
computed_field,
|
|
14
|
+
field_serializer,
|
|
15
|
+
field_validator,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from ccproxy.auth.models.base import BaseProfileInfo, BaseTokenInfo
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ClaudeOAuthToken(BaseModel):
|
|
22
|
+
"""OAuth token information from Claude credentials."""
|
|
23
|
+
|
|
24
|
+
model_config = ConfigDict(
|
|
25
|
+
populate_by_name=True, use_enum_values=True, arbitrary_types_allowed=True
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
access_token: SecretStr = Field(..., alias="accessToken")
|
|
29
|
+
refresh_token: SecretStr = Field(..., alias="refreshToken")
|
|
30
|
+
expires_at: int | None = Field(None, alias="expiresAt")
|
|
31
|
+
scopes: list[str] = Field(default_factory=list)
|
|
32
|
+
subscription_type: str | None = Field(None, alias="subscriptionType")
|
|
33
|
+
|
|
34
|
+
@field_serializer("access_token", "refresh_token")
|
|
35
|
+
def serialize_secret(self, value: SecretStr) -> str:
|
|
36
|
+
"""Serialize SecretStr to plain string for JSON output."""
|
|
37
|
+
return value.get_secret_value() if value else ""
|
|
38
|
+
|
|
39
|
+
@field_validator("access_token", "refresh_token", mode="before")
|
|
40
|
+
@classmethod
|
|
41
|
+
def validate_tokens(cls, v: str | SecretStr | None) -> SecretStr | None:
|
|
42
|
+
"""Convert string values to SecretStr."""
|
|
43
|
+
if v is None:
|
|
44
|
+
return None
|
|
45
|
+
if isinstance(v, str):
|
|
46
|
+
return SecretStr(v)
|
|
47
|
+
return v
|
|
48
|
+
|
|
49
|
+
def __repr__(self) -> str:
|
|
50
|
+
"""Safe string representation that masks sensitive tokens."""
|
|
51
|
+
access_token_str = self.access_token.get_secret_value()
|
|
52
|
+
refresh_token_str = self.refresh_token.get_secret_value()
|
|
53
|
+
|
|
54
|
+
access_preview = (
|
|
55
|
+
f"{access_token_str[:8]}...{access_token_str[-8:]}"
|
|
56
|
+
if len(access_token_str) > 16
|
|
57
|
+
else "***"
|
|
58
|
+
)
|
|
59
|
+
refresh_preview = (
|
|
60
|
+
f"{refresh_token_str[:8]}...{refresh_token_str[-8:]}"
|
|
61
|
+
if len(refresh_token_str) > 16
|
|
62
|
+
else "***"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
expires_at = (
|
|
66
|
+
datetime.fromtimestamp(self.expires_at / 1000, tz=UTC).isoformat()
|
|
67
|
+
if self.expires_at is not None
|
|
68
|
+
else "None"
|
|
69
|
+
)
|
|
70
|
+
return (
|
|
71
|
+
f"OAuthToken(access_token='{access_preview}', "
|
|
72
|
+
f"refresh_token='{refresh_preview}', "
|
|
73
|
+
f"expires_at={expires_at}, "
|
|
74
|
+
f"scopes={self.scopes}, "
|
|
75
|
+
f"subscription_type='{self.subscription_type}')"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def is_expired(self) -> bool:
|
|
80
|
+
"""Check if the token is expired."""
|
|
81
|
+
if self.expires_at is None:
|
|
82
|
+
return False
|
|
83
|
+
now = datetime.now(UTC).timestamp() * 1000 # Convert to milliseconds
|
|
84
|
+
return now >= self.expires_at
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def expires_at_datetime(self) -> datetime:
|
|
88
|
+
"""Get expiration as datetime object."""
|
|
89
|
+
if self.expires_at is None:
|
|
90
|
+
# Return a far future date if no expiration info
|
|
91
|
+
return datetime.fromtimestamp(2147483647, tz=UTC) # Year 2038
|
|
92
|
+
return datetime.fromtimestamp(self.expires_at / 1000, tz=UTC)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ClaudeCredentials(BaseModel):
|
|
96
|
+
"""Claude credentials from the credentials file."""
|
|
97
|
+
|
|
98
|
+
model_config = ConfigDict(
|
|
99
|
+
populate_by_name=True, use_enum_values=True, arbitrary_types_allowed=True
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
claude_ai_oauth: ClaudeOAuthToken = Field(..., alias="claudeAiOauth")
|
|
103
|
+
|
|
104
|
+
def __repr__(self) -> str:
|
|
105
|
+
"""Safe string representation that masks sensitive tokens."""
|
|
106
|
+
return f"ClaudeCredentials(claude_ai_oauth={repr(self.claude_ai_oauth)})"
|
|
107
|
+
|
|
108
|
+
def is_expired(self) -> bool:
|
|
109
|
+
"""Check if the credentials are expired.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
True if expired, False otherwise
|
|
113
|
+
"""
|
|
114
|
+
return self.claude_ai_oauth.is_expired
|
|
115
|
+
|
|
116
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
117
|
+
"""Override model_dump to use by_alias=True by default."""
|
|
118
|
+
kwargs.setdefault("by_alias", True)
|
|
119
|
+
return super().model_dump(**kwargs)
|
|
120
|
+
|
|
121
|
+
def to_dict(self) -> dict[str, Any]:
|
|
122
|
+
"""Convert to dictionary for storage.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Dictionary representation
|
|
126
|
+
"""
|
|
127
|
+
return self.model_dump(mode="json", exclude_none=True)
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def from_dict(cls, data: dict[str, Any]) -> "ClaudeCredentials":
|
|
131
|
+
"""Create from dictionary.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
data: Dictionary containing credential data
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
ClaudeCredentials instance
|
|
138
|
+
"""
|
|
139
|
+
return cls.model_validate(data)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class ClaudeTokenWrapper(BaseTokenInfo):
|
|
143
|
+
"""Wrapper for Claude credentials that adds computed properties.
|
|
144
|
+
|
|
145
|
+
This wrapper maintains the original ClaudeCredentials structure
|
|
146
|
+
while providing a unified interface through BaseTokenInfo.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
# Embed the original credentials to preserve JSON schema
|
|
150
|
+
credentials: ClaudeCredentials
|
|
151
|
+
|
|
152
|
+
@computed_field
|
|
153
|
+
def access_token_value(self) -> str:
|
|
154
|
+
"""Extract access token from Claude OAuth structure."""
|
|
155
|
+
return self.credentials.claude_ai_oauth.access_token.get_secret_value()
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def refresh_token_value(self) -> str | None:
|
|
159
|
+
"""Extract refresh token from Claude OAuth structure."""
|
|
160
|
+
token = self.credentials.claude_ai_oauth.refresh_token
|
|
161
|
+
return token.get_secret_value() if token else None
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def expires_at_datetime(self) -> datetime:
|
|
165
|
+
"""Convert Claude's millisecond timestamp to datetime."""
|
|
166
|
+
expires_at = self.credentials.claude_ai_oauth.expires_at
|
|
167
|
+
if not expires_at:
|
|
168
|
+
# No expiration means token doesn't expire
|
|
169
|
+
return datetime.max.replace(tzinfo=UTC)
|
|
170
|
+
# Claude stores expires_at in milliseconds
|
|
171
|
+
return datetime.fromtimestamp(expires_at / 1000, tz=UTC)
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def subscription_type(self) -> str | None:
|
|
175
|
+
"""Compute subscription type from stored profile info.
|
|
176
|
+
|
|
177
|
+
Attempts to read the Claude profile file ("~/.claude/.account.json")
|
|
178
|
+
and derive the subscription from account flags:
|
|
179
|
+
- "max" if has_claude_max is true
|
|
180
|
+
- "pro" if has_claude_pro is true
|
|
181
|
+
- "free" otherwise
|
|
182
|
+
|
|
183
|
+
Falls back to the token's own subscription_type if profile is unavailable.
|
|
184
|
+
"""
|
|
185
|
+
# Lazy, best-effort read of local profile data; keep this non-fatal.
|
|
186
|
+
try:
|
|
187
|
+
profile_path = Path.home() / ".claude" / ".account.json"
|
|
188
|
+
if profile_path.exists():
|
|
189
|
+
with profile_path.open("r") as f:
|
|
190
|
+
data = json.load(f)
|
|
191
|
+
account = data.get("account", {})
|
|
192
|
+
if account.get("has_claude_max") is True:
|
|
193
|
+
return "max"
|
|
194
|
+
if account.get("has_claude_pro") is True:
|
|
195
|
+
return "pro"
|
|
196
|
+
# If account is present but neither flag set, assume free tier
|
|
197
|
+
if account:
|
|
198
|
+
return "free"
|
|
199
|
+
except Exception:
|
|
200
|
+
# Ignore any profile read/parse errors and fall back
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
return self.credentials.claude_ai_oauth.subscription_type
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def scopes(self) -> list[str]:
|
|
207
|
+
"""Get OAuth scopes."""
|
|
208
|
+
return self.credentials.claude_ai_oauth.scopes
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class ClaudeProfileInfo(BaseProfileInfo):
|
|
212
|
+
"""Claude-specific profile information from API.
|
|
213
|
+
|
|
214
|
+
Created from the /api/organizations/me endpoint response.
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
provider_type: Literal["claude-api"] = "claude-api"
|
|
218
|
+
|
|
219
|
+
@classmethod
|
|
220
|
+
def from_api_response(cls, data: dict[str, Any]) -> "ClaudeProfileInfo":
|
|
221
|
+
"""Create profile from Claude API response.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
data: Response from /api/organizations/me endpoint
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
ClaudeProfileInfo instance with all data preserved
|
|
228
|
+
"""
|
|
229
|
+
# Extract account information if present
|
|
230
|
+
account = data.get("account", {})
|
|
231
|
+
organization = data.get("organization", {})
|
|
232
|
+
|
|
233
|
+
# Extract common fields for easy access
|
|
234
|
+
account_id = account.get("uuid", "")
|
|
235
|
+
email = account.get("email", "")
|
|
236
|
+
display_name = account.get("full_name")
|
|
237
|
+
|
|
238
|
+
# Store entire response in extras for complete information
|
|
239
|
+
# This includes: has_claude_pro, has_claude_max, organization details, etc.
|
|
240
|
+
return cls(
|
|
241
|
+
account_id=account_id,
|
|
242
|
+
email=email,
|
|
243
|
+
display_name=display_name,
|
|
244
|
+
extras=data, # Preserve complete API response
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def has_claude_pro(self) -> bool | None:
|
|
249
|
+
"""Check if user has Claude Pro subscription."""
|
|
250
|
+
account = self.extras.get("account", {})
|
|
251
|
+
value = account.get("has_claude_pro")
|
|
252
|
+
return bool(value) if value is not None else None
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def has_claude_max(self) -> bool | None:
|
|
256
|
+
"""Check if user has Claude Max subscription."""
|
|
257
|
+
account = self.extras.get("account", {})
|
|
258
|
+
value = account.get("has_claude_max")
|
|
259
|
+
return bool(value) if value is not None else None
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def organization_name(self) -> str | None:
|
|
263
|
+
"""Get organization name if available."""
|
|
264
|
+
org = self.extras.get("organization", {})
|
|
265
|
+
name = org.get("name")
|
|
266
|
+
return str(name) if name is not None else None
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""OAuth Claude plugin v2 implementation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, cast
|
|
4
|
+
|
|
5
|
+
from ccproxy.auth.oauth import OAuthProviderProtocol
|
|
6
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
7
|
+
from ccproxy.core.plugins import (
|
|
8
|
+
AuthProviderPluginFactory,
|
|
9
|
+
AuthProviderPluginRuntime,
|
|
10
|
+
PluginContext,
|
|
11
|
+
PluginManifest,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .config import ClaudeOAuthConfig
|
|
15
|
+
from .provider import ClaudeOAuthProvider
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
logger = get_plugin_logger()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class OAuthClaudeRuntime(AuthProviderPluginRuntime):
|
|
22
|
+
"""Runtime for OAuth Claude plugin."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, manifest: PluginManifest):
|
|
25
|
+
"""Initialize runtime."""
|
|
26
|
+
super().__init__(manifest)
|
|
27
|
+
self.config: ClaudeOAuthConfig | None = None
|
|
28
|
+
|
|
29
|
+
async def _on_initialize(self) -> None:
|
|
30
|
+
"""Initialize the OAuth Claude plugin."""
|
|
31
|
+
logger.debug(
|
|
32
|
+
"oauth_claude_initializing",
|
|
33
|
+
context_keys=list(self.context.keys()) if self.context else [],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Get configuration
|
|
37
|
+
if self.context:
|
|
38
|
+
config = self.context.get("config")
|
|
39
|
+
if not isinstance(config, ClaudeOAuthConfig):
|
|
40
|
+
# Use default config if none provided
|
|
41
|
+
config = ClaudeOAuthConfig()
|
|
42
|
+
logger.debug("oauth_claude_using_default_config")
|
|
43
|
+
self.config = config
|
|
44
|
+
|
|
45
|
+
# Call parent initialization which handles provider registration
|
|
46
|
+
await super()._on_initialize()
|
|
47
|
+
|
|
48
|
+
logger.debug(
|
|
49
|
+
"oauth_claude_plugin_initialized",
|
|
50
|
+
status="initialized",
|
|
51
|
+
provider_name=self.auth_provider.provider_name
|
|
52
|
+
if self.auth_provider
|
|
53
|
+
else "unknown",
|
|
54
|
+
category="plugin",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class OAuthClaudeFactory(AuthProviderPluginFactory):
|
|
59
|
+
"""Factory for OAuth Claude plugin."""
|
|
60
|
+
|
|
61
|
+
cli_safe = True # Safe for CLI - provides auth only
|
|
62
|
+
|
|
63
|
+
def __init__(self) -> None:
|
|
64
|
+
"""Initialize factory with manifest."""
|
|
65
|
+
# Create manifest with static declarations
|
|
66
|
+
manifest = PluginManifest(
|
|
67
|
+
name="oauth_claude",
|
|
68
|
+
version="0.1.0",
|
|
69
|
+
description="Standalone Claude OAuth authentication provider plugin",
|
|
70
|
+
is_provider=True, # It's a provider plugin but focused on OAuth
|
|
71
|
+
config_class=ClaudeOAuthConfig,
|
|
72
|
+
dependencies=[],
|
|
73
|
+
routes=[], # No HTTP routes needed
|
|
74
|
+
tasks=[], # No scheduled tasks needed
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Initialize with manifest
|
|
78
|
+
super().__init__(manifest)
|
|
79
|
+
|
|
80
|
+
def create_context(self, core_services: Any) -> PluginContext:
|
|
81
|
+
"""Create context with auth provider components.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
core_services: Core services container
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Plugin context with auth provider components
|
|
88
|
+
"""
|
|
89
|
+
# Start with base context
|
|
90
|
+
context = super().create_context(core_services)
|
|
91
|
+
|
|
92
|
+
# Create auth provider for this plugin
|
|
93
|
+
auth_provider = self.create_auth_provider(context)
|
|
94
|
+
context["auth_provider"] = auth_provider
|
|
95
|
+
|
|
96
|
+
# Add other auth-specific components if needed
|
|
97
|
+
storage = self.create_storage()
|
|
98
|
+
if storage:
|
|
99
|
+
context["storage"] = storage
|
|
100
|
+
|
|
101
|
+
return context
|
|
102
|
+
|
|
103
|
+
def create_runtime(self) -> OAuthClaudeRuntime:
|
|
104
|
+
"""Create runtime instance."""
|
|
105
|
+
return OAuthClaudeRuntime(self.manifest)
|
|
106
|
+
|
|
107
|
+
def create_auth_provider(
|
|
108
|
+
self, context: PluginContext | None = None
|
|
109
|
+
) -> OAuthProviderProtocol:
|
|
110
|
+
"""Create OAuth provider instance.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
context: Plugin context containing shared resources
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
ClaudeOAuthProvider instance
|
|
117
|
+
"""
|
|
118
|
+
# Prefer validated config from context when available
|
|
119
|
+
if context and isinstance(context.get("config"), ClaudeOAuthConfig):
|
|
120
|
+
cfg = cast(ClaudeOAuthConfig, context.get("config"))
|
|
121
|
+
else:
|
|
122
|
+
cfg = ClaudeOAuthConfig()
|
|
123
|
+
config: ClaudeOAuthConfig = cfg
|
|
124
|
+
http_client = context.get("http_client") if context else None
|
|
125
|
+
hook_manager = context.get("hook_manager") if context else None
|
|
126
|
+
# CLIDetectionService is injected under 'cli_detection_service' in base context
|
|
127
|
+
detection_service = context.get("cli_detection_service") if context else None
|
|
128
|
+
settings = context.get("settings") if context else None
|
|
129
|
+
provider = ClaudeOAuthProvider(
|
|
130
|
+
config,
|
|
131
|
+
http_client=http_client,
|
|
132
|
+
hook_manager=hook_manager,
|
|
133
|
+
detection_service=detection_service,
|
|
134
|
+
settings=settings,
|
|
135
|
+
)
|
|
136
|
+
return cast(OAuthProviderProtocol, provider)
|
|
137
|
+
|
|
138
|
+
def create_storage(self) -> Any | None:
|
|
139
|
+
"""Create storage for OAuth credentials.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Storage instance or None to use provider's default
|
|
143
|
+
"""
|
|
144
|
+
# ClaudeOAuthProvider manages its own storage internally
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# Export the factory instance
|
|
149
|
+
factory = OAuthClaudeFactory()
|