Skip to content

ccproxy.cli

ccproxy.cli

api

api(
    config=None,
    port=None,
    host=None,
    reload=None,
    log_level=None,
    log_file=None,
    auth_token=None,
    enable_plugin=None,
    disable_plugin=None,
)

Start the CCProxy API server.

Source code in ccproxy/cli/commands/serve.py
def api(
    config: Annotated[
        Path | None,
        typer.Option(
            "--config",
            "-c",
            help="Path to configuration file (TOML, JSON, or YAML)",
            exists=True,
            file_okay=True,
            dir_okay=False,
            readable=True,
            rich_help_panel="Configuration",
        ),
    ] = None,
    port: Annotated[
        int | None,
        typer.Option(
            "--port",
            "-p",
            help="Port to run the server on",
            callback=validate_port,
            rich_help_panel="Server Settings",
        ),
    ] = None,
    host: Annotated[
        str | None,
        typer.Option(
            "--host",
            "-h",
            help="Host to bind the server to",
            rich_help_panel="Server Settings",
        ),
    ] = None,
    reload: Annotated[
        bool | None,
        typer.Option(
            "--reload/--no-reload",
            help="Enable auto-reload for development",
            rich_help_panel="Server Settings",
        ),
    ] = None,
    log_level: Annotated[
        str | None,
        typer.Option(
            "--log-level",
            help="Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL). Use WARNING for minimal output.",
            callback=validate_log_level,
            rich_help_panel="Server Settings",
        ),
    ] = None,
    log_file: Annotated[
        str | None,
        typer.Option(
            "--log-file",
            help="Path to JSON log file. If specified, logs will be written to this file in JSON format",
            rich_help_panel="Server Settings",
        ),
    ] = None,
    auth_token: Annotated[
        str | None,
        typer.Option(
            "--auth-token",
            help="Bearer token for API authentication",
            callback=validate_auth_token,
            rich_help_panel="Security Settings",
        ),
    ] = None,
    enable_plugin: Annotated[
        list[str] | None,
        typer.Option(
            "--enable-plugin",
            help="Enable a plugin by name (repeatable)",
            rich_help_panel="Plugin Settings",
        ),
    ] = None,
    disable_plugin: Annotated[
        list[str] | None,
        typer.Option(
            "--disable-plugin",
            help="Disable a plugin by name (repeatable)",
            rich_help_panel="Plugin Settings",
        ),
    ] = None,
    # Removed unused flags: plugin_setting, no_network_calls,
    # disable_version_check, disable_pricing_updates
) -> None:
    """Start the CCProxy API server."""
    try:
        if config is None:
            config = get_config_path_from_context()

        # Base CLI context; plugin-injected args merged below
        cli_context = {
            "port": port,
            "host": host,
            "reload": reload,
            "log_level": log_level,
            "log_file": log_file,
            "auth_token": auth_token,
            "enabled_plugins": enable_plugin,
            "disabled_plugins": disable_plugin,
        }

        # Merge plugin-provided CLI args via helper
        try:
            from ccproxy.cli.helpers import get_plugin_cli_args

            plugin_args = get_plugin_cli_args()
            if plugin_args:
                cli_context.update(plugin_args)
        except Exception:
            pass

        # Pass CLI context to settings creation
        settings = Settings.from_config(config_path=config, cli_context=cli_context)

        setup_logging(
            json_logs=settings.logging.format == "json",
            log_level_name=settings.logging.level,
            log_file=settings.logging.file,
        )

        logger = get_logger(__name__)

        logger.debug(
            "configuration_loaded",
            host=settings.server.host,
            port=settings.server.port,
            log_level=settings.logging.level,
            log_file=settings.logging.file,
            auth_enabled=bool(settings.security.auth_token),
            duckdb_enabled=bool(
                (settings.plugins.get("duckdb_storage") or {}).get("enabled", False)
            ),
        )

        _run_local_server(settings)

    except ConfigurationError as e:
        toolkit = get_rich_toolkit()
        toolkit.print(f"Configuration error: {e}", tag="error")
        raise typer.Exit(1) from e
    except OSError as e:
        toolkit = get_rich_toolkit()
        toolkit.print(
            f"Server startup failed (port/permission issue): {e}", tag="error"
        )
        raise typer.Exit(1) from e
    except ImportError as e:
        toolkit = get_rich_toolkit()
        toolkit.print(f"Import error during server startup: {e}", tag="error")
        raise typer.Exit(1) from e
    except Exception as e:
        toolkit = get_rich_toolkit()
        toolkit.print(f"Error starting server: {e}", tag="error")
        raise typer.Exit(1) from e

app_main

app_main(ctx, version=False, config=None)

CCProxy API Server - Anthropic and OpenAI compatible interface for Claude.

Source code in ccproxy/cli/main.py
@app.callback()
def app_main(
    ctx: typer.Context,
    version: Annotated[
        bool,
        typer.Option(
            "--version",
            "-V",
            callback=version_callback,
            is_eager=True,
            help="Show version and exit.",
        ),
    ] = False,
    config: Annotated[
        Path | None,
        typer.Option(
            "--config",
            "-c",
            help="Path to configuration file (TOML, JSON, or YAML)",
            exists=True,
            file_okay=True,
            dir_okay=False,
            readable=True,
        ),
    ] = None,
) -> None:
    """CCProxy API Server - Anthropic and OpenAI compatible interface for Claude."""
    # Store config path and initialize plugin arg bucket
    ctx.ensure_object(dict)
    ctx.obj["config_path"] = config
    if "plugin_cli_args" not in ctx.obj or not isinstance(
        ctx.obj.get("plugin_cli_args"), dict
    ):
        ctx.obj["plugin_cli_args"] = {}

    # If no command is invoked, run the serve command by default
    if ctx.invoked_subcommand is None:
        # Import here to avoid circular imports
        from .commands.serve import api

        # Invoke the serve command
        ctx.invoke(api)

version_callback

version_callback(value)

Print version and exit.

Source code in ccproxy/cli/main.py
def version_callback(value: bool) -> None:
    """Print version and exit."""
    if value:
        toolkit = get_rich_toolkit()
        toolkit.print(f"ccproxy {__version__}", tag="version")
        raise typer.Exit()