ccproxy-api 0.1.0__tar.gz → 0.1.2__tar.gz
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-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/release.yml +3 -0
- ccproxy_api-0.1.2/CHANGELOG.md +90 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/PKG-INFO +62 -7
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/README.md +61 -6
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/_version.py +2 -2
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/app.py +70 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/dependencies.py +3 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/claude.py +0 -6
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/proxy.py +13 -6
- ccproxy_api-0.1.2/ccproxy/auth/conditional.py +84 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/claude_sdk/converter.py +1 -1
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/claude_sdk/options.py +23 -1
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/__init__.py +2 -1
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/auth.py +59 -43
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/config/commands.py +2 -2
- ccproxy_api-0.1.2/ccproxy/cli/commands/permission.py +128 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/serve.py +322 -82
- ccproxy_api-0.1.2/ccproxy/cli/main.py +115 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/claude_options.py +1 -93
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/core_options.py +1 -13
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/security_options.py +1 -9
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/server_options.py +1 -52
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/docker_settings.py +1 -1
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/pricing.py +5 -6
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/scheduler.py +5 -6
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/async_utils.py +1 -1
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/types.py +3 -9
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/messages.py +1 -2
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/models.py +5 -6
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/claude_sdk_service.py +5 -1
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/proxy_service.py +6 -7
- ccproxy_api-0.1.2/docs/examples.md +384 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/getting-started/configuration.md +5 -5
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/getting-started/quickstart.md +64 -16
- ccproxy_api-0.1.2/docs/index.md +279 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/systemd-setup.md +1 -1
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/authentication.md +5 -5
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/claude-code-options.md +3 -18
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/pyproject.toml +2 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_api.py +7 -28
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_auth.py +0 -34
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_metrics_api.py +0 -11
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_observability.py +0 -7
- ccproxy_api-0.1.0/CHANGELOG.md +0 -47
- ccproxy_api-0.1.0/ccproxy/cli/main.py +0 -193
- ccproxy_api-0.1.0/docs/examples.md +0 -114
- ccproxy_api-0.1.0/docs/index.md +0 -175
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.env.example +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/backend.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/build.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/ci.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/docs.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.github/workflows/frontend.yml.disabled +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.gitignore +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.pre-commit-config.yaml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/.python-version +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/CONTRIBUTING.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/CONVENTIONS.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/Dockerfile +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/LICENSE +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/Makefile +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/TESTING.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/__main__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/base.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/openai/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/openai/adapter.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/openai/models.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/adapters/openai/streaming.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/auth.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/cors.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/errors.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/headers.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/logging.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/request_id.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/middleware/server_header.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/responses.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/health.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/api/routes/metrics.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/bearer.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/credentials_adapter.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/dependencies.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/exceptions.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/manager.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/models.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/oauth/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/oauth/models.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/oauth/routes.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/oauth/storage.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/storage/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/storage/base.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/storage/json_file.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/auth/storage/keyring.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/claude_sdk/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/claude_sdk/client.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/config/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/commands/config/schema_commands.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/docker/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/docker/adapter_factory.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/docker/params.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/helpers.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/cli/options/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/auth.py +2 -2
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/claude.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/cors.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/discovery.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/loader.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/observability.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/reverse_proxy.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/security.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/server.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/settings.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/config/validators.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/constants.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/errors.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/http.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/http_transformers.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/interfaces.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/logging.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/middleware.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/proxy.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/system.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/transformers.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/core/validators.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/adapter.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/docker_path.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/middleware.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/models.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/protocol.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/stream_process.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/docker/validators.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/errors.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/requests.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/responses.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/models/types.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/access_logger.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/context.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/metrics.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/pushgateway.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/sse_events.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/stats_printer.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/storage/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/storage/duckdb_simple.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/observability/storage/models.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/cache.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/loader.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/pricing/updater.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/core.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/exceptions.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/manager.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/registry.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/scheduler/tasks.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/credentials/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/credentials/config.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/credentials/manager.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/services/credentials/oauth_client.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/static/.keep +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/config.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/content_generation.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/mock_responses.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/response_handlers.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/testing/scenarios.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/utils/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/utils/cost_calculator.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/ccproxy/utils/streaming_metrics.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/data/metrics.db +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/devenv.lock +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/devenv.nix +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docker-compose.monitoring.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docker-compose.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/README.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/api-reference.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/assets/extra.css +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/assets/extra.js +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/contributing.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/development/debugging-with-proxy.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/gen_ref_pages.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/getting-started/installation.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/metrics-api.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/observability.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/api-usage.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/claude-sdk-compatibility.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/mcp-integration.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/pool-configuration.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/docs/user-guide/understanding-pool-logs.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/README.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/README_chat_agent.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/ai_code_discussion_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/anthropic_streaming_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/anthropic_tools_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/client.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/common_utils.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/console_utils.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_anthropic_conversation_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_claude_code_options_example.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_json_object_example.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_json_schema_example.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_streaming_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_structured_response_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_thinking_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/openai_tools_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/project_code_access_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/simple_thinking_demo.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/examples/textual_chat_agent.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/mkdocs.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/monitoring/grafana/dashboards/ccproxy-dashboard.json +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/monitoring/grafana/provisioning/dashboards/dashboard.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/monitoring/grafana/provisioning/datasources/victoria-metrics.yml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/act-run.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/benchmark_startup_node.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/build-docs.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/entrypoint.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/format_version.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/serve-docs.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/setup-systemd.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/setup.sh +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/scripts/traffic_generator.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/systemd/ccproxy.service.template +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/test_config/test-config.toml +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/conftest.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/factories/MIGRATION_GUIDE.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/factories/README.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/factories/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/factories/fastapi_factory.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/MIGRATION_GUIDE.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/README.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/auth/MIGRATION_GUIDE.md +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/auth/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/auth/example_usage.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/claude_sdk/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/claude_sdk/internal_mocks.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/claude_sdk/responses.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/credentials.json +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/external_apis/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/external_apis/anthropic_api.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/proxy_service/__init__.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/proxy_service/oauth_mocks.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/fixtures/responses.json +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_access_logger_integration.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_adapters.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_cli_auth_commands.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_cli_config.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_cli_serve.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_docker.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_duckdb_lifecycle.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_duckdb_settings_integration.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_fastapi_factory.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_http_transformers.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_pricing.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_pushgateway_error_handling.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_queue_duckdb_storage.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_reset_endpoint.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_scheduler.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_scheduler_tasks.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_sse_events.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_sse_stream_filtering.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_stats_printer.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/tests/test_streaming.py +0 -0
- {ccproxy_api-0.1.0 → ccproxy_api-0.1.2}/uv.lock +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.2] - 2025-07-22
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Permission Mode Support**: Restored `--permission-mode` option supporting default, acceptEdits, and bypassPermissions modes
|
|
12
|
+
- **Custom Permission Tool**: Restored `--permission-prompt-tool-name` option to specify custom permission tool names
|
|
13
|
+
- **Permission Response Models**: Added `PermissionToolAllowResponse` and `PermissionToolDenyResponse` models with proper JSON serialization
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- **Message Formatting**: Modified `MessageConverter.combine_text_parts()` to join text parts with newlines instead of spaces, preserving formatting in multi-line content
|
|
17
|
+
- **Settings Integration**: Enhanced OptionsHandler to apply default Claude options from settings before API parameters
|
|
18
|
+
- **Configuration**: Extended settings to persist permission_mode and permission_prompt_tool_name
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Claude SDK Options**: Integrated Settings dependency into ClaudeSDKService to support configuration-based options
|
|
22
|
+
|
|
23
|
+
## [0.1.1] - 2025-07-22
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **Conditional Authentication**: API endpoints now support optional authentication - when `SECURITY__AUTH_TOKEN` is configured, authentication is enforced; when not configured, the proxy runs in open mode.
|
|
27
|
+
- **Startup Validation**: Added comprehensive validation checks during application startup:
|
|
28
|
+
- Validates OAuth credentials and warns about expired tokens
|
|
29
|
+
- Checks for Claude CLI binary availability with installation instructions
|
|
30
|
+
- Logs token expiration time and subscription type when valid
|
|
31
|
+
- **Default Command**: The `serve` command is now the default - running `ccproxy` without subcommands automatically starts the server.
|
|
32
|
+
- **Alternative Entry Point**: Added `ccproxy-api` as an alternative command-line entry point.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- **Authentication Variable**: Renamed environment variable from `AUTH_TOKEN` to `SECURITY__AUTH_TOKEN` for better namespace organization and clarity.
|
|
36
|
+
- **Credential Priority**: Reordered credential search paths to prioritize ccproxy-specific credentials before Claude CLI paths.
|
|
37
|
+
- **CLI Syntax**: Migrated all CLI parameters to modern Annotated syntax for better type safety and IDE support.
|
|
38
|
+
- **Pydantic v2**: Updated all models to use Pydantic v2 configuration syntax (`model_config` instead of `Config` class).
|
|
39
|
+
- **Documentation**: Improved Aider integration docs with correct API endpoint URLs and added installation options (uv, pipx).
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- **Authentication Separation**: Fixed critical issue where auth token was incorrectly used for both client and upstream authentication - now client auth token is separate from OAuth credentials.
|
|
43
|
+
- **URL Paths**: Fixed documentation to use `/api` endpoints for Aider compatibility instead of SDK mode paths.
|
|
44
|
+
- **Default Values**: Fixed default values for list parameters in CLI (docker_env, docker_volume, docker_arg).
|
|
45
|
+
|
|
46
|
+
### Removed
|
|
47
|
+
- **Status Endpoints**: Removed redundant `/status` endpoints from both Claude SDK and proxy routes.
|
|
48
|
+
- **Permission Tool**: Removed Claude permission tool functionality and related CLI options (`--permission-mode`, `--permission-prompt-tool-name`) that are no longer needed.
|
|
49
|
+
- **Deprecated Options**: Removed references to deprecated permission_mode and permission_prompt_tool_name from documentation.
|
|
50
|
+
|
|
51
|
+
## [0.1.0] - 2025-07-21
|
|
52
|
+
|
|
53
|
+
This is the initial public release of the CCProxy API.
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
#### Core Functionality
|
|
58
|
+
- **Personal Claude Access**: Enables using a personal Claude Pro, Team, or Enterprise subscription as an API endpoint, without needing separate API keys.
|
|
59
|
+
- **OAuth2 Authentication**: Implements the official Claude OAuth2 flow for secure, local authentication.
|
|
60
|
+
- **Local Proxy Server**: Runs a lightweight FastAPI server on your local machine.
|
|
61
|
+
- **HTTP/HTTPS Proxy Support**: Full support for routing requests through an upstream HTTP or HTTPS proxy.
|
|
62
|
+
|
|
63
|
+
#### API & Compatibility
|
|
64
|
+
- **Dual API Support**: Provides full compatibility with both Anthropic and OpenAI API specifications.
|
|
65
|
+
- **Anthropic Messages API**: Native support for the Anthropic Messages API at `/v1/chat/completions`.
|
|
66
|
+
- **OpenAI Chat Completions API**: Compatibility layer for the OpenAI Chat Completions API at `/openai/v1/chat/completions`.
|
|
67
|
+
- **Request/Response Translation**: Seamlessly translates requests and responses between OpenAI and Anthropic formats.
|
|
68
|
+
- **Streaming Support**: Real-time streaming for both Anthropic and OpenAI-compatible endpoints.
|
|
69
|
+
- **Model Endpoints**: Lists available models via `/v1/models` and `/openai/v1/models`.
|
|
70
|
+
- **Health Check**: A `/health` endpoint for monitoring the proxy's status.
|
|
71
|
+
|
|
72
|
+
#### Configuration & CLI
|
|
73
|
+
- **Unified `ccproxy` CLI**: A single, user-friendly command-line interface for managing the proxy.
|
|
74
|
+
- **TOML Configuration**: Configure the server using a `config.toml` file with JSON Schema validation.
|
|
75
|
+
- **Keyring Integration**: Securely stores and manages OAuth credentials in the system's native keyring.
|
|
76
|
+
- **`generate-token` Command**: A CLI command to manually generate and manage API tokens.
|
|
77
|
+
- **Systemd Integration**: Includes a setup script and service template for running the proxy as a systemd service in production environments.
|
|
78
|
+
- **Docker Support**: A `Dockerfile` and `docker-compose.yml` for running the proxy in an isolated containerized environment.
|
|
79
|
+
|
|
80
|
+
#### Security
|
|
81
|
+
- **Local-First Design**: All processing and authentication happens locally; no conversation data is stored or transmitted to third parties.
|
|
82
|
+
- **Credential Security**: OAuth tokens are stored securely in the system keyring, not in plaintext files.
|
|
83
|
+
- **Header Stripping**: Automatically removes client-side `Authorization` headers to prevent accidental key leakage.
|
|
84
|
+
|
|
85
|
+
#### Developer Experience
|
|
86
|
+
- **Comprehensive Documentation**: Includes a quick start guide, API reference, and setup instructions.
|
|
87
|
+
- **Pre-commit Hooks**: Configured for automated code formatting and linting to ensure code quality.
|
|
88
|
+
- **Modern Tooling**: Uses `uv` for package management and `devenv` for a reproducible development environment.
|
|
89
|
+
- **Extensive Test Suite**: Includes unit, integration, and benchmark tests to ensure reliability.
|
|
90
|
+
- **Rich Logging**: Structured and colorized logging for improved readability during development and debugging.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ccproxy-api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: API server that provides an Anthropic and OpenAI compatible interface over Claude Code, allowing to use your Claude OAuth account or over the API.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -43,14 +43,26 @@ It includes a translation layer to support both Anthropic and OpenAI-compatible
|
|
|
43
43
|
# The official claude-code CLI is required for SDK mode
|
|
44
44
|
npm install -g @anthropic-ai/claude-code
|
|
45
45
|
|
|
46
|
-
#
|
|
47
|
-
|
|
46
|
+
# run it with uv
|
|
47
|
+
uvx ccproxy-api
|
|
48
|
+
|
|
49
|
+
# run it with pipx
|
|
50
|
+
pipx run ccproxy-api
|
|
51
|
+
|
|
52
|
+
# install with uv
|
|
53
|
+
uv tool install ccproxy-api
|
|
54
|
+
|
|
55
|
+
# Install ccproxy with pip
|
|
56
|
+
pipx install ccproxy-api
|
|
48
57
|
|
|
49
58
|
# Optional: Enable shell completion
|
|
50
59
|
eval "$(ccproxy --show-completion zsh)" # For zsh
|
|
51
60
|
eval "$(ccproxy --show-completion bash)" # For bash
|
|
52
61
|
```
|
|
53
62
|
|
|
63
|
+
|
|
64
|
+
For dev version replace `ccproxy-api` with `git+https://github.com/caddyglow/ccproxy-api.git@dev`
|
|
65
|
+
|
|
54
66
|
## Authentication
|
|
55
67
|
|
|
56
68
|
The proxy uses two different authentication mechanisms depending on the mode.
|
|
@@ -61,12 +73,23 @@ The proxy uses two different authentication mechanisms depending on the mode.
|
|
|
61
73
|
claude /login
|
|
62
74
|
```
|
|
63
75
|
|
|
76
|
+
It's also possible now to get a long live token to avoid renewing issues
|
|
77
|
+
using
|
|
78
|
+
```sh
|
|
79
|
+
```bash
|
|
80
|
+
claude setup-token`
|
|
81
|
+
|
|
64
82
|
2. **ccproxy (`api` mode):**
|
|
65
83
|
This mode uses its own OAuth2 flow to obtain credentials for direct API access.
|
|
66
84
|
```bash
|
|
67
85
|
ccproxy auth login
|
|
68
86
|
```
|
|
69
|
-
|
|
87
|
+
|
|
88
|
+
If you are already connected with Claude CLI the credentials should be found automatically
|
|
89
|
+
|
|
90
|
+
You can check the status of these credentials with `ccproxy auth validate` and `ccproxy auth info`.
|
|
91
|
+
|
|
92
|
+
Warning is show on start up if no credentials are setup.
|
|
70
93
|
|
|
71
94
|
## Usage
|
|
72
95
|
|
|
@@ -102,6 +125,38 @@ export ANTHROPIC_BASE_URL="http://localhost:8000/api"
|
|
|
102
125
|
export ANTHROPIC_API_KEY="dummy-key"
|
|
103
126
|
```
|
|
104
127
|
|
|
128
|
+
|
|
129
|
+
## Using with Aider
|
|
130
|
+
|
|
131
|
+
CCProxy works seamlessly with Aider and other AI coding assistants:
|
|
132
|
+
|
|
133
|
+
### Anthropic Mode
|
|
134
|
+
```bash
|
|
135
|
+
export ANTHROPIC_API_KEY=dummy
|
|
136
|
+
export ANTHROPIC_BASE_URL=http://127.0.0.1:8000/api
|
|
137
|
+
aider --model claude-sonnet-4-20250514
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### OpenAI Mode with Model Mapping
|
|
141
|
+
|
|
142
|
+
If your tool only supports OpenAI settings, ccproxy automatically maps OpenAI models to Claude:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
export OPENAI_API_KEY=dummy
|
|
146
|
+
export OPENAI_BASE_URL=http://127.0.0.1:8000/api/v1
|
|
147
|
+
aider --model o3-mini
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### API Mode (Direct Proxy)
|
|
151
|
+
|
|
152
|
+
For minimal interference and direct API access:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
export OPENAI_API_KEY=dummy
|
|
156
|
+
export OPENAI_BASE_URL=http://127.0.0.1:8000/api/v1
|
|
157
|
+
aider --model o3-mini
|
|
158
|
+
```
|
|
159
|
+
|
|
105
160
|
### `curl` Example
|
|
106
161
|
|
|
107
162
|
```bash
|
|
@@ -185,16 +240,16 @@ You can enable token authentication for the proxy. This supports multiple header
|
|
|
185
240
|
**1. Generate a Token:**
|
|
186
241
|
```bash
|
|
187
242
|
ccproxy generate-token
|
|
188
|
-
# Output:
|
|
243
|
+
# Output: SECURITY__AUTH_TOKEN=abc123xyz789...
|
|
189
244
|
```
|
|
190
245
|
|
|
191
246
|
**2. Configure the Token:**
|
|
192
247
|
```bash
|
|
193
248
|
# Set environment variable
|
|
194
|
-
export
|
|
249
|
+
export SECURITY__AUTH_TOKEN=abc123xyz789...
|
|
195
250
|
|
|
196
251
|
# Or add to .env file
|
|
197
|
-
echo "
|
|
252
|
+
echo "SECURITY__AUTH_TOKEN=abc123xyz789..." >> .env
|
|
198
253
|
```
|
|
199
254
|
|
|
200
255
|
**3. Use in Requests:**
|
|
@@ -14,14 +14,26 @@ It includes a translation layer to support both Anthropic and OpenAI-compatible
|
|
|
14
14
|
# The official claude-code CLI is required for SDK mode
|
|
15
15
|
npm install -g @anthropic-ai/claude-code
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
|
|
17
|
+
# run it with uv
|
|
18
|
+
uvx ccproxy-api
|
|
19
|
+
|
|
20
|
+
# run it with pipx
|
|
21
|
+
pipx run ccproxy-api
|
|
22
|
+
|
|
23
|
+
# install with uv
|
|
24
|
+
uv tool install ccproxy-api
|
|
25
|
+
|
|
26
|
+
# Install ccproxy with pip
|
|
27
|
+
pipx install ccproxy-api
|
|
19
28
|
|
|
20
29
|
# Optional: Enable shell completion
|
|
21
30
|
eval "$(ccproxy --show-completion zsh)" # For zsh
|
|
22
31
|
eval "$(ccproxy --show-completion bash)" # For bash
|
|
23
32
|
```
|
|
24
33
|
|
|
34
|
+
|
|
35
|
+
For dev version replace `ccproxy-api` with `git+https://github.com/caddyglow/ccproxy-api.git@dev`
|
|
36
|
+
|
|
25
37
|
## Authentication
|
|
26
38
|
|
|
27
39
|
The proxy uses two different authentication mechanisms depending on the mode.
|
|
@@ -32,12 +44,23 @@ The proxy uses two different authentication mechanisms depending on the mode.
|
|
|
32
44
|
claude /login
|
|
33
45
|
```
|
|
34
46
|
|
|
47
|
+
It's also possible now to get a long live token to avoid renewing issues
|
|
48
|
+
using
|
|
49
|
+
```sh
|
|
50
|
+
```bash
|
|
51
|
+
claude setup-token`
|
|
52
|
+
|
|
35
53
|
2. **ccproxy (`api` mode):**
|
|
36
54
|
This mode uses its own OAuth2 flow to obtain credentials for direct API access.
|
|
37
55
|
```bash
|
|
38
56
|
ccproxy auth login
|
|
39
57
|
```
|
|
40
|
-
|
|
58
|
+
|
|
59
|
+
If you are already connected with Claude CLI the credentials should be found automatically
|
|
60
|
+
|
|
61
|
+
You can check the status of these credentials with `ccproxy auth validate` and `ccproxy auth info`.
|
|
62
|
+
|
|
63
|
+
Warning is show on start up if no credentials are setup.
|
|
41
64
|
|
|
42
65
|
## Usage
|
|
43
66
|
|
|
@@ -73,6 +96,38 @@ export ANTHROPIC_BASE_URL="http://localhost:8000/api"
|
|
|
73
96
|
export ANTHROPIC_API_KEY="dummy-key"
|
|
74
97
|
```
|
|
75
98
|
|
|
99
|
+
|
|
100
|
+
## Using with Aider
|
|
101
|
+
|
|
102
|
+
CCProxy works seamlessly with Aider and other AI coding assistants:
|
|
103
|
+
|
|
104
|
+
### Anthropic Mode
|
|
105
|
+
```bash
|
|
106
|
+
export ANTHROPIC_API_KEY=dummy
|
|
107
|
+
export ANTHROPIC_BASE_URL=http://127.0.0.1:8000/api
|
|
108
|
+
aider --model claude-sonnet-4-20250514
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### OpenAI Mode with Model Mapping
|
|
112
|
+
|
|
113
|
+
If your tool only supports OpenAI settings, ccproxy automatically maps OpenAI models to Claude:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
export OPENAI_API_KEY=dummy
|
|
117
|
+
export OPENAI_BASE_URL=http://127.0.0.1:8000/api/v1
|
|
118
|
+
aider --model o3-mini
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### API Mode (Direct Proxy)
|
|
122
|
+
|
|
123
|
+
For minimal interference and direct API access:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
export OPENAI_API_KEY=dummy
|
|
127
|
+
export OPENAI_BASE_URL=http://127.0.0.1:8000/api/v1
|
|
128
|
+
aider --model o3-mini
|
|
129
|
+
```
|
|
130
|
+
|
|
76
131
|
### `curl` Example
|
|
77
132
|
|
|
78
133
|
```bash
|
|
@@ -156,16 +211,16 @@ You can enable token authentication for the proxy. This supports multiple header
|
|
|
156
211
|
**1. Generate a Token:**
|
|
157
212
|
```bash
|
|
158
213
|
ccproxy generate-token
|
|
159
|
-
# Output:
|
|
214
|
+
# Output: SECURITY__AUTH_TOKEN=abc123xyz789...
|
|
160
215
|
```
|
|
161
216
|
|
|
162
217
|
**2. Configure the Token:**
|
|
163
218
|
```bash
|
|
164
219
|
# Set environment variable
|
|
165
|
-
export
|
|
220
|
+
export SECURITY__AUTH_TOKEN=abc123xyz789...
|
|
166
221
|
|
|
167
222
|
# Or add to .env file
|
|
168
|
-
echo "
|
|
223
|
+
echo "SECURITY__AUTH_TOKEN=abc123xyz789..." >> .env
|
|
169
224
|
```
|
|
170
225
|
|
|
171
226
|
**3. Use in Requests:**
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from collections.abc import AsyncGenerator
|
|
4
4
|
from contextlib import asynccontextmanager
|
|
5
|
+
from datetime import UTC, datetime
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
7
8
|
from fastapi import FastAPI, HTTPException
|
|
@@ -23,11 +24,13 @@ from ccproxy.api.routes.metrics import (
|
|
|
23
24
|
prometheus_router,
|
|
24
25
|
)
|
|
25
26
|
from ccproxy.api.routes.proxy import router as proxy_router
|
|
27
|
+
from ccproxy.auth.exceptions import CredentialsNotFoundError
|
|
26
28
|
from ccproxy.auth.oauth.routes import router as oauth_router
|
|
27
29
|
from ccproxy.config.settings import Settings, get_settings
|
|
28
30
|
from ccproxy.core.logging import setup_logging
|
|
29
31
|
from ccproxy.observability.storage.duckdb_simple import SimpleDuckDBStorage
|
|
30
32
|
from ccproxy.scheduler.manager import start_scheduler, stop_scheduler
|
|
33
|
+
from ccproxy.services.credentials import CredentialsManager
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
logger = get_logger(__name__)
|
|
@@ -58,6 +61,73 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|
|
58
61
|
"claude_cli_search_paths", paths=settings.claude.get_searched_paths()
|
|
59
62
|
)
|
|
60
63
|
|
|
64
|
+
# Validate authentication token at startup
|
|
65
|
+
try:
|
|
66
|
+
credentials_manager = CredentialsManager()
|
|
67
|
+
validation = await credentials_manager.validate()
|
|
68
|
+
|
|
69
|
+
if validation.valid and not validation.expired:
|
|
70
|
+
credentials = validation.credentials
|
|
71
|
+
oauth_token = credentials.claude_ai_oauth if credentials else None
|
|
72
|
+
|
|
73
|
+
if oauth_token and oauth_token.expires_at_datetime:
|
|
74
|
+
hours_until_expiry = int(
|
|
75
|
+
(
|
|
76
|
+
oauth_token.expires_at_datetime - datetime.now(UTC)
|
|
77
|
+
).total_seconds()
|
|
78
|
+
/ 3600
|
|
79
|
+
)
|
|
80
|
+
logger.info(
|
|
81
|
+
"auth_token_valid",
|
|
82
|
+
expires_in_hours=hours_until_expiry,
|
|
83
|
+
subscription_type=oauth_token.subscription_type,
|
|
84
|
+
credentials_path=str(validation.path) if validation.path else None,
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
logger.info("auth_token_valid", credentials_path=str(validation.path))
|
|
88
|
+
elif validation.expired:
|
|
89
|
+
logger.warning(
|
|
90
|
+
"auth_token_expired",
|
|
91
|
+
message="Authentication token has expired. Please run 'ccproxy auth login' to refresh.",
|
|
92
|
+
credentials_path=str(validation.path) if validation.path else None,
|
|
93
|
+
)
|
|
94
|
+
else:
|
|
95
|
+
logger.warning(
|
|
96
|
+
"auth_token_invalid",
|
|
97
|
+
message="Authentication token is invalid. Please run 'ccproxy auth login'.",
|
|
98
|
+
credentials_path=str(validation.path) if validation.path else None,
|
|
99
|
+
)
|
|
100
|
+
except CredentialsNotFoundError:
|
|
101
|
+
logger.warning(
|
|
102
|
+
"auth_token_not_found",
|
|
103
|
+
message="No authentication credentials found. Please run 'ccproxy auth login' to authenticate.",
|
|
104
|
+
searched_paths=settings.auth.storage.storage_paths,
|
|
105
|
+
)
|
|
106
|
+
except Exception as e:
|
|
107
|
+
logger.error(
|
|
108
|
+
"auth_token_validation_error",
|
|
109
|
+
error=str(e),
|
|
110
|
+
message="Failed to validate authentication token. The server will continue without authentication.",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Validate Claude binary at startup
|
|
114
|
+
claude_path, found_in_path = settings.claude.find_claude_cli()
|
|
115
|
+
if claude_path:
|
|
116
|
+
logger.info(
|
|
117
|
+
"claude_binary_found",
|
|
118
|
+
path=claude_path,
|
|
119
|
+
found_in_path=found_in_path,
|
|
120
|
+
message=f"Claude CLI binary found at: {claude_path}",
|
|
121
|
+
)
|
|
122
|
+
else:
|
|
123
|
+
searched_paths = settings.claude.get_searched_paths()
|
|
124
|
+
logger.warning(
|
|
125
|
+
"claude_binary_not_found",
|
|
126
|
+
message="Claude CLI binary not found. Please install Claude CLI to use SDK features.",
|
|
127
|
+
searched_paths=searched_paths,
|
|
128
|
+
install_command="npm install -g @anthropic-ai/claude-code",
|
|
129
|
+
)
|
|
130
|
+
|
|
61
131
|
# Start scheduler system
|
|
62
132
|
try:
|
|
63
133
|
scheduler = await start_scheduler(settings)
|
|
@@ -22,11 +22,13 @@ SettingsDep = Annotated[Settings, Depends(get_settings)]
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def get_claude_service(
|
|
25
|
+
settings: SettingsDep,
|
|
25
26
|
auth_manager: AuthManagerDep,
|
|
26
27
|
) -> ClaudeSDKService:
|
|
27
28
|
"""Get Claude SDK service instance.
|
|
28
29
|
|
|
29
30
|
Args:
|
|
31
|
+
settings: Application settings dependency
|
|
30
32
|
auth_manager: Authentication manager dependency
|
|
31
33
|
|
|
32
34
|
Returns:
|
|
@@ -39,6 +41,7 @@ def get_claude_service(
|
|
|
39
41
|
return ClaudeSDKService(
|
|
40
42
|
auth_manager=auth_manager,
|
|
41
43
|
metrics=metrics,
|
|
44
|
+
settings=settings,
|
|
42
45
|
)
|
|
43
46
|
|
|
44
47
|
|
|
@@ -173,9 +173,3 @@ async def list_models(
|
|
|
173
173
|
raise HTTPException(
|
|
174
174
|
status_code=500, detail=f"Internal server error: {str(e)}"
|
|
175
175
|
) from e
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
@router.get("/status")
|
|
179
|
-
async def claude_sdk_status() -> dict[str, str]:
|
|
180
|
-
"""Get Claude SDK status."""
|
|
181
|
-
return {"status": "claude sdk endpoint available", "service": "direct"}
|
|
@@ -11,6 +11,7 @@ from starlette.background import BackgroundTask
|
|
|
11
11
|
from ccproxy.adapters.openai.adapter import OpenAIAdapter
|
|
12
12
|
from ccproxy.api.dependencies import ProxyServiceDep
|
|
13
13
|
from ccproxy.api.responses import ProxyResponse
|
|
14
|
+
from ccproxy.auth.conditional import ConditionalAuthDep
|
|
14
15
|
from ccproxy.core.errors import ProxyHTTPException
|
|
15
16
|
|
|
16
17
|
|
|
@@ -22,6 +23,7 @@ router = APIRouter(tags=["proxy"])
|
|
|
22
23
|
async def create_openai_chat_completion(
|
|
23
24
|
request: Request,
|
|
24
25
|
proxy_service: ProxyServiceDep,
|
|
26
|
+
auth: ConditionalAuthDep,
|
|
25
27
|
) -> StreamingResponse | Response:
|
|
26
28
|
"""Create a chat completion using Claude AI with OpenAI-compatible format.
|
|
27
29
|
|
|
@@ -98,6 +100,9 @@ async def create_openai_chat_completion(
|
|
|
98
100
|
media_type=response_headers.get("content-type", "application/json"),
|
|
99
101
|
)
|
|
100
102
|
|
|
103
|
+
except HTTPException:
|
|
104
|
+
# Re-raise HTTPException as-is (including 401 auth errors)
|
|
105
|
+
raise
|
|
101
106
|
except Exception as e:
|
|
102
107
|
raise HTTPException(
|
|
103
108
|
status_code=500, detail=f"Internal server error: {str(e)}"
|
|
@@ -108,6 +113,7 @@ async def create_openai_chat_completion(
|
|
|
108
113
|
async def create_anthropic_message(
|
|
109
114
|
request: Request,
|
|
110
115
|
proxy_service: ProxyServiceDep,
|
|
116
|
+
auth: ConditionalAuthDep,
|
|
111
117
|
) -> StreamingResponse | Response:
|
|
112
118
|
"""Create a message using Claude AI with Anthropic format.
|
|
113
119
|
|
|
@@ -180,6 +186,9 @@ async def create_anthropic_message(
|
|
|
180
186
|
media_type=response_headers.get("content-type", "application/json"),
|
|
181
187
|
)
|
|
182
188
|
|
|
189
|
+
except HTTPException:
|
|
190
|
+
# Re-raise HTTPException as-is (including 401 auth errors)
|
|
191
|
+
raise
|
|
183
192
|
except Exception as e:
|
|
184
193
|
raise HTTPException(
|
|
185
194
|
status_code=500, detail=f"Internal server error: {str(e)}"
|
|
@@ -190,6 +199,7 @@ async def create_anthropic_message(
|
|
|
190
199
|
async def list_models(
|
|
191
200
|
request: Request,
|
|
192
201
|
proxy_service: ProxyServiceDep,
|
|
202
|
+
auth: ConditionalAuthDep,
|
|
193
203
|
) -> Response:
|
|
194
204
|
"""List available models using the proxy service.
|
|
195
205
|
|
|
@@ -226,13 +236,10 @@ async def list_models(
|
|
|
226
236
|
media_type=response_headers.get("content-type", "application/json"),
|
|
227
237
|
)
|
|
228
238
|
|
|
239
|
+
except HTTPException:
|
|
240
|
+
# Re-raise HTTPException as-is (including 401 auth errors)
|
|
241
|
+
raise
|
|
229
242
|
except Exception as e:
|
|
230
243
|
raise HTTPException(
|
|
231
244
|
status_code=500, detail=f"Internal server error: {str(e)}"
|
|
232
245
|
) from e
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
@router.get("/status")
|
|
236
|
-
async def proxy_status() -> dict[str, str]:
|
|
237
|
-
"""Get proxy status."""
|
|
238
|
-
return {"status": "proxy API available", "version": "1.0.0"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Conditional authentication dependencies."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
from fastapi import Depends, HTTPException, Request, status
|
|
6
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
7
|
+
|
|
8
|
+
from ccproxy.auth.bearer import BearerTokenAuthManager
|
|
9
|
+
from ccproxy.auth.exceptions import AuthenticationError
|
|
10
|
+
from ccproxy.auth.manager import AuthManager
|
|
11
|
+
from ccproxy.config.settings import Settings, get_settings
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# FastAPI security scheme for bearer tokens
|
|
15
|
+
bearer_scheme = HTTPBearer(auto_error=False)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def get_conditional_auth_manager(
|
|
19
|
+
request: Request,
|
|
20
|
+
credentials: Annotated[
|
|
21
|
+
HTTPAuthorizationCredentials | None, Depends(bearer_scheme)
|
|
22
|
+
] = None,
|
|
23
|
+
settings: Annotated[Settings | None, Depends(get_settings)] = None,
|
|
24
|
+
) -> AuthManager | None:
|
|
25
|
+
"""Get authentication manager only if auth is required.
|
|
26
|
+
|
|
27
|
+
This dependency checks if authentication is configured and validates
|
|
28
|
+
the token if required. If no auth is configured, returns None.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
request: The FastAPI request object
|
|
32
|
+
credentials: HTTP authorization credentials
|
|
33
|
+
settings: Application settings
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
AuthManager instance if authenticated, None if no auth required
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
HTTPException: If auth is required but credentials are invalid
|
|
40
|
+
"""
|
|
41
|
+
# Check if auth is required for this configuration
|
|
42
|
+
if settings is None or not settings.security.auth_token:
|
|
43
|
+
# No auth configured, return None
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
# Auth is required, validate credentials
|
|
47
|
+
if not credentials or not credentials.credentials:
|
|
48
|
+
raise HTTPException(
|
|
49
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
50
|
+
detail="Authentication required",
|
|
51
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Validate the token
|
|
55
|
+
if credentials.credentials != settings.security.auth_token:
|
|
56
|
+
raise HTTPException(
|
|
57
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
58
|
+
detail="Invalid authentication credentials",
|
|
59
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Create and return auth manager
|
|
63
|
+
try:
|
|
64
|
+
bearer_auth = BearerTokenAuthManager(credentials.credentials)
|
|
65
|
+
if await bearer_auth.is_authenticated():
|
|
66
|
+
return bearer_auth
|
|
67
|
+
else:
|
|
68
|
+
raise HTTPException(
|
|
69
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
70
|
+
detail="Authentication failed",
|
|
71
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
72
|
+
)
|
|
73
|
+
except (AuthenticationError, ValueError) as e:
|
|
74
|
+
raise HTTPException(
|
|
75
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
76
|
+
detail=str(e),
|
|
77
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
78
|
+
) from e
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Type alias for conditional auth dependency
|
|
82
|
+
ConditionalAuthDep = Annotated[
|
|
83
|
+
AuthManager | None, Depends(get_conditional_auth_manager)
|
|
84
|
+
]
|
|
@@ -109,7 +109,7 @@ class MessageConverter:
|
|
|
109
109
|
for block in contents:
|
|
110
110
|
text_parts.append(MessageConverter.extract_text_from_content(block))
|
|
111
111
|
|
|
112
|
-
return "
|
|
112
|
+
return "\n".join(text_parts)
|
|
113
113
|
|
|
114
114
|
@staticmethod
|
|
115
115
|
def convert_to_anthropic_response(
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from ccproxy.config.settings import Settings
|
|
5
6
|
from ccproxy.core.async_utils import patched_typing
|
|
6
7
|
|
|
7
8
|
|
|
@@ -14,8 +15,17 @@ class OptionsHandler:
|
|
|
14
15
|
Handles creation and management of Claude SDK options.
|
|
15
16
|
"""
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
def __init__(self, settings: Settings | None = None) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Initialize options handler.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
settings: Application settings containing default Claude options
|
|
24
|
+
"""
|
|
25
|
+
self.settings = settings
|
|
26
|
+
|
|
18
27
|
def create_options(
|
|
28
|
+
self,
|
|
19
29
|
model: str,
|
|
20
30
|
temperature: float | None = None,
|
|
21
31
|
max_tokens: int | None = None,
|
|
@@ -37,6 +47,18 @@ class OptionsHandler:
|
|
|
37
47
|
"""
|
|
38
48
|
options = ClaudeCodeOptions(model=model)
|
|
39
49
|
|
|
50
|
+
# First apply settings from configuration if available
|
|
51
|
+
if self.settings and self.settings.claude.code_options:
|
|
52
|
+
code_opts = self.settings.claude.code_options
|
|
53
|
+
|
|
54
|
+
# Apply settings from configuration
|
|
55
|
+
for attr_name in dir(code_opts):
|
|
56
|
+
if not attr_name.startswith("_"):
|
|
57
|
+
value = getattr(code_opts, attr_name, None)
|
|
58
|
+
if value is not None and hasattr(options, attr_name):
|
|
59
|
+
setattr(options, attr_name, value)
|
|
60
|
+
|
|
61
|
+
# Then apply API parameters (these override settings)
|
|
40
62
|
if temperature is not None:
|
|
41
63
|
options.temperature = temperature # type: ignore[attr-defined]
|
|
42
64
|
|