Skip to content

ccproxy.cli.main

ccproxy.cli.main

Main entry point for CCProxy API Server.

Adds per-invocation debug logging of CLI argv and relevant environment variables (masked) so every command emits its context consistently.

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()

register_plugin_cli_extensions

register_plugin_cli_extensions(app)

Register plugin CLI commands and arguments during app creation.

Source code in ccproxy/cli/main.py
def register_plugin_cli_extensions(app: typer.Typer) -> None:
    """Register plugin CLI commands and arguments during app creation."""
    try:
        # Load settings to apply plugin filtering
        try:
            from ccproxy.config.settings import Settings

            settings = Settings.from_config()
        except Exception as e:
            # Graceful degradation - use no filtering if settings fail to load
            logger.debug("settings_load_failed_for_cli_discovery", error=str(e))
            settings = None

        plugin_manifests = discover_plugin_cli_extensions(settings)

        logger.debug(
            "plugin_cli_discovery_complete",
            plugin_count=len(plugin_manifests),
            plugins=[name for name, _ in plugin_manifests],
        )

        # Register new commands first
        for plugin_name, manifest in plugin_manifests:
            for cmd_spec in manifest.cli_commands:
                _register_plugin_command(app, plugin_name, cmd_spec)

        # Batch extend existing commands with new arguments
        arg_batches: dict[str, list[tuple[str, CliArgumentSpec]]] = {}
        for plugin_name, manifest in plugin_manifests:
            for arg_spec in manifest.cli_arguments:
                arg_batches.setdefault(arg_spec.target_command, []).append(
                    (plugin_name, arg_spec)
                )

        for target, pairs in arg_batches.items():
            _extend_command_with_arguments(app, target, pairs)

    except Exception as e:
        # Graceful degradation - CLI still works without plugin extensions
        logger.debug("plugin_cli_extension_registration_failed", error=str(e))

ensure_plugin_cli_extensions_registered

ensure_plugin_cli_extensions_registered(app)

Register plugin CLI extensions once, after logging is configured.

Source code in ccproxy/cli/main.py
def ensure_plugin_cli_extensions_registered(app: typer.Typer) -> None:
    """Register plugin CLI extensions once, after logging is configured."""
    global _plugins_registered
    if _plugins_registered:
        return

    register_plugin_cli_extensions(app)
    _plugins_registered = True

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)

main

main()

Entry point for the CLI application.

Source code in ccproxy/cli/main.py
def main() -> None:
    """Entry point for the CLI application."""
    # Bind a command-wide correlation ID so all logs have `cmd_id`
    set_command_context()
    # Early logging bootstrap from env/argv; safe to reconfigure later
    bootstrap_cli_logging()
    # Register plugin-supplied CLI commands after logging honors env overrides
    ensure_plugin_cli_extensions_registered(app)
    # Log invocation context (argv + env) for all commands
    _log_cli_invocation_context()
    app()