Skip to content

config_cmd

config_cmd

Daemon configuration management commands for Marianne CLI.

This module implements the mzt config command group for viewing and managing the conductor daemon configuration file.

Subcommands: - mzt config show — Display current config as a Rich table - mzt config set — Update a config value - mzt config path — Show config file location - mzt config init — Create a default config file

Functions

config_callback

config_callback(ctx)

Manage conductor configuration.

Source code in src/marianne/cli/commands/config_cmd.py
@config_app.callback(invoke_without_command=True)
def config_callback(ctx: typer.Context) -> None:
    """Manage conductor configuration."""
    if ctx.invoked_subcommand is None:
        # No subcommand provided — show help
        console.print(ctx.get_help())
        raise typer.Exit(0)

show

show(config_file=Option(None, '--config', '-c', help='Path to daemon config file (default: ~/.marianne/daemon.yaml)'), show_all=Option(False, '--all', '-a', help='Show all fields (default: essential only)'), section=Option(None, '--section', '-s', help='Show only fields in a section (e.g., profiler, learning, socket)'))

Display current daemon configuration as a table.

By default shows only essential fields. Use --all for the full config or --section to drill into a specific section.

Examples:

mzt config show mzt config show --all mzt config show --section profiler

Source code in src/marianne/cli/commands/config_cmd.py
@config_app.command()
def show(
    config_file: Path | None = typer.Option(
        None,
        "--config",
        "-c",
        help="Path to daemon config file (default: ~/.marianne/daemon.yaml)",
    ),
    show_all: bool = typer.Option(
        False,
        "--all",
        "-a",
        help="Show all fields (default: essential only)",
    ),
    section: str | None = typer.Option(
        None,
        "--section",
        "-s",
        help="Show only fields in a section (e.g., profiler, learning, socket)",
    ),
) -> None:
    """Display current daemon configuration as a table.

    By default shows only essential fields.  Use --all for the full config
    or --section to drill into a specific section.

    Examples:
        mzt config show
        mzt config show --all
        mzt config show --section profiler
    """
    from marianne.daemon.config import DaemonConfig

    # Try live config from running conductor first
    live_data = _try_live_config()
    effective = DaemonConfig()  # Overwritten by live or disk path
    is_live = False

    if live_data is not None:
        try:
            effective = DaemonConfig.model_validate(live_data)
            is_live = True
        except Exception:
            _logger.warning("live config validation failed, falling back to disk", exc_info=True)

    if is_live:
        source_label = "[bold green][live][/bold green] from running conductor"
        file_data = live_data or {}
    else:
        path = _resolve_config_path(config_file)
        file_data = _load_config_data(path)

        try:
            effective = DaemonConfig.model_validate(file_data)
        except Exception as e:
            output_error(
                f"Error loading config: {e}",
                hints=["Check ~/.marianne/conductor.yaml syntax."],
            )
            raise typer.Exit(1) from None

        source_label = f"[dim]{path}[/dim]" if path.exists() else "[dim](defaults)[/dim]"

    console.print(f"\nDaemon configuration — {source_label}\n")

    table = Table(show_header=True, header_style="bold cyan", padding=(0, 1))
    table.add_column("Key", style="white", min_width=30)
    table.add_column("Value", style="green")
    table.add_column("Source", style="dim")

    # Flatten the config for display
    flat = _flatten_model(effective.model_dump(), prefix="")

    # Filter keys based on flags
    if section is not None:
        prefix = section.rstrip(".")
        visible_keys = [k for k in flat if k == prefix or k.startswith(f"{prefix}.")]
    elif not show_all:
        visible_keys = [k for k in flat if k in ESSENTIAL_KEYS]
    else:
        visible_keys = list(flat.keys())

    for key in visible_keys:
        value = flat[key]
        if is_live:
            source_display = "[green]live[/green]"
        else:
            file_value = _get_nested(file_data, key)
            source = "file" if file_value is not None else "default"
            source_display = f"[dim]{source}[/dim]" if source == "default" else source
        table.add_row(key, str(value), source_display)

    console.print(table)

    if not show_all and section is None:
        console.print(
            "\n[dim]Showing essential fields. "
            "Use --all for full config, --section <name> for a section.[/dim]"
        )

set_value

set_value(key=Argument(..., help='Config key in dot notation (e.g., socket.path, max_concurrent_jobs)'), value=Argument(..., help='New value to set'), config_file=Option(None, '--config', '-c', help='Path to daemon config file (default: ~/.marianne/daemon.yaml)'))

Update a daemon configuration value.

Values are validated against the DaemonConfig schema before saving. Use dot notation for nested keys (e.g., socket.path, resource_limits.max_memory_mb).

Examples:

mzt config set max_concurrent_jobs 10 mzt config set socket.path /tmp/custom.sock mzt config set resource_limits.max_memory_mb 4096 mzt config set log_level debug

Source code in src/marianne/cli/commands/config_cmd.py
@config_app.command("set")
def set_value(
    key: str = typer.Argument(
        ...,
        help="Config key in dot notation (e.g., socket.path, max_concurrent_jobs)",
    ),
    value: str = typer.Argument(
        ...,
        help="New value to set",
    ),
    config_file: Path | None = typer.Option(
        None,
        "--config",
        "-c",
        help="Path to daemon config file (default: ~/.marianne/daemon.yaml)",
    ),
) -> None:
    """Update a daemon configuration value.

    Values are validated against the DaemonConfig schema before saving.
    Use dot notation for nested keys (e.g., socket.path, resource_limits.max_memory_mb).

    Examples:
        mzt config set max_concurrent_jobs 10
        mzt config set socket.path /tmp/custom.sock
        mzt config set resource_limits.max_memory_mb 4096
        mzt config set log_level debug
    """
    from marianne.daemon.config import DaemonConfig

    path = _resolve_config_path(config_file)
    data = _load_config_data(path)

    coerced = _coerce_value(value)
    _set_nested(data, key, coerced)

    # Validate the full config before saving
    try:
        DaemonConfig.model_validate(data)
    except Exception as e:
        output_error(
            f"Invalid value: {e}",
            hints=["Run 'mzt config show --all' to see current values."],
        )
        raise typer.Exit(1) from None

    _save_config_data(path, data)
    console.print(f"[green]Set[/green] {key} = {coerced!r} in {path}")

path

path(config_file=Option(None, '--config', '-c', help='Path to daemon config file (default: ~/.marianne/daemon.yaml)'))

Show the daemon config file location.

Displays the resolved path and whether the file exists.

Examples:

mzt config path mzt config path --config /etc/marianne/daemon.yaml

Source code in src/marianne/cli/commands/config_cmd.py
@config_app.command()
def path(
    config_file: Path | None = typer.Option(
        None,
        "--config",
        "-c",
        help="Path to daemon config file (default: ~/.marianne/daemon.yaml)",
    ),
) -> None:
    """Show the daemon config file location.

    Displays the resolved path and whether the file exists.

    Examples:
        mzt config path
        mzt config path --config /etc/marianne/daemon.yaml
    """
    resolved = _resolve_config_path(config_file)
    exists = resolved.exists()
    status = "[green]exists[/green]" if exists else "[yellow]not created[/yellow]"
    console.print(f"{resolved}  ({status})")

init

init(config_file=Option(None, '--config', '-c', help='Path to create config file (default: ~/.marianne/daemon.yaml)'), force=Option(False, '--force', '-f', help='Overwrite existing config file'))

Create a default daemon config file.

Generates a YAML config file with all default values and descriptive comments. Refuses to overwrite unless --force is given.

Examples:

mzt config init mzt config init --config /etc/marianne/daemon.yaml mzt config init --force

Source code in src/marianne/cli/commands/config_cmd.py
@config_app.command()
def init(
    config_file: Path | None = typer.Option(
        None,
        "--config",
        "-c",
        help="Path to create config file (default: ~/.marianne/daemon.yaml)",
    ),
    force: bool = typer.Option(
        False,
        "--force",
        "-f",
        help="Overwrite existing config file",
    ),
) -> None:
    """Create a default daemon config file.

    Generates a YAML config file with all default values and
    descriptive comments. Refuses to overwrite unless --force is given.

    Examples:
        mzt config init
        mzt config init --config /etc/marianne/daemon.yaml
        mzt config init --force
    """
    from marianne.daemon.config import DaemonConfig

    resolved = _resolve_config_path(config_file)

    if resolved.exists() and not force:
        console.print(
            f"[yellow]Config file already exists:[/yellow] {resolved}\n"
            "Use --force to overwrite."
        )
        raise typer.Exit(1)

    defaults = DaemonConfig()
    data = defaults.model_dump()

    # Convert Path objects to strings for YAML serialization
    _stringify_paths(data)

    resolved.parent.mkdir(parents=True, exist_ok=True)
    _save_config_data(resolved, data)
    console.print(f"[green]Created default config:[/green] {resolved}")

check

check(config_file=Option(None, '--config', '-c', help='Path to daemon config file to validate'))

Validate a daemon config file against the DaemonConfig schema.

Loads the YAML file, validates all fields, and reports the result. Exits 0 if valid, 1 if invalid or the file cannot be loaded.

Examples:

mzt config check --config my-daemon.yaml mzt config check

Source code in src/marianne/cli/commands/config_cmd.py
@config_app.command("check")
def check(
    config_file: Path | None = typer.Option(
        None,
        "--config",
        "-c",
        help="Path to daemon config file to validate",
    ),
) -> None:
    """Validate a daemon config file against the DaemonConfig schema.

    Loads the YAML file, validates all fields, and reports the result.
    Exits 0 if valid, 1 if invalid or the file cannot be loaded.

    Examples:
        mzt config check --config my-daemon.yaml
        mzt config check
    """
    from marianne.daemon.config import DaemonConfig

    path = _resolve_config_path(config_file)
    if not path.exists():
        output_error(
            f"Config file not found: {path}",
            hints=["Run 'mzt config init' to create a default config."],
        )
        raise typer.Exit(1)

    data = _load_config_data(path)

    try:
        DaemonConfig.model_validate(data)
    except Exception as e:
        output_error(
            f"Invalid config: {path}{e}",
            hints=["Run 'mzt config show' to see the expected schema."],
        )
        raise typer.Exit(1) from None

    console.print(f"[green]Valid config:[/green] {path}")