mobius-cli 0.15.31

The terminal client for a möbius gateway
Documentation
//! möbius terminal frontend.

use mobius::Result;
use mobius::protocol::FrontendBlock;
use mobius_gateway::client::{GatewayEvents, GatewaySender};
use mobius_gateway::wire::{ProviderInstance, ReadyPayload, SessionReadyPayload};

mod bots;
mod catalog;
mod cloudflare_setup;
mod dashboard;
mod extensions;
mod gateway;
mod gateway_actions;
mod headless;
mod reinitialize;
mod setup;
mod terminal;
mod theme;
mod tui;

pub use headless::run as run_headless;
pub use terminal::terminal_text;

pub use cloudflare_setup::{CloudflareInit, run as run_cloudflare_setup};
pub use dashboard::{run as run_gateway_dashboard, run_provider as run_gateway_provider};
pub use reinitialize::confirm as confirm_gateway_reinitialize;
pub use setup::run_gateway_login;

/// Returns the run extensions.
/// # Errors
///
/// Returns an error if validation or an operation required to complete the request fails.
pub async fn run_extensions(
    sender: GatewaySender,
    events: GatewayEvents,
    gateway: ReadyPayload,
) -> Result<()> {
    extensions::standalone(sender, events, gateway).await
}

fn block_text(block: &FrontendBlock) -> String {
    let mut text = block.title.clone();
    if !block.text.is_empty() {
        if !text.is_empty() {
            text.push('\n');
        }
        text.push_str(&block.text);
    }
    for part in &block.content.0 {
        text.push('\n');
        match part {
            mobius::protocol::ContentPart::Text { text: part } => text.push_str(part),
            mobius::protocol::ContentPart::Image { image } => text.push_str(&format!(
                "[image] {} · {} × {} · file_id={}",
                image.file.name, image.width, image.height, image.file.id
            )),
            mobius::protocol::ContentPart::File { file } => text.push_str(&format!(
                "[file] {} · {} bytes · file_id={}",
                file.name, file.size, file.id
            )),
        }
    }
    for file in &block.files {
        if !text.is_empty() {
            text.push('\n');
        }
        text.push_str(&format!(
            "[file] {} · {} · {} bytes",
            file.name, file.media_type, file.size
        ));
    }
    text
}

fn provider_instance_label<'a>(
    instances: &'a [ProviderInstance],
    instance: &str,
) -> Option<&'a str> {
    instances
        .iter()
        .find(|entry| entry.selection.instance == instance)
        .map(|entry| entry.label.as_str())
}

/// Runs the interactive terminal frontend.
/// # Errors
///
/// Returns an error if validation or an operation required to complete the request fails.
pub async fn run(
    sender: GatewaySender,
    events: GatewayEvents,
    gateway: &mut ReadyPayload,
    session: &mut SessionReadyPayload,
    gateway_endpoint: String,
    choose_initial_bot: bool,
) -> Result<(FrontendExit, GatewaySender, GatewayEvents)> {
    let workspace = session.workspace.path.clone();
    let catalog = catalog::UiCatalog::build(&session.contributions, &workspace)?;
    tui::runtime::run(
        sender,
        events,
        gateway,
        session,
        catalog,
        gateway_endpoint,
        choose_initial_bot,
    )
    .await
}

/// Why a frontend returned control to its launcher.
pub enum FrontendExit {
    /// Selects the exit case.
    Exit,
    /// Selects the resume case.
    Resume(String),
    /// Selects the reload case.
    Reload,
    /// Selects the reconnect case.
    Reconnect,
    /// Selects the fresh-session case after deleting the open chat.
    Fresh,
}