02 — CLI Interface

The bext CLI is the primary interface for developers. It replaces the current bext-server [subcommand] pattern with a proper CLI that handles running, building, deploying, and managing applications.

Current State

bext-server              # Start HTTP server
bext-server build        # Run build script
bext-server validate     # Validate config
bext-server scan         # Print route table
bext-server version      # Print version
bext-server help         # Usage

Target State

# Core commands
bext run [dir]           # Run an app (auto-detect framework, serve it)
bext dev [dir]           # Dev mode (watch + HMR + source maps)
bext build [dir]         # Build for production
bext start [dir]         # Start built app in production mode

# App management
bext apps list           # List registered apps
bext apps add <name>     # Register an app
bext apps remove <name>  # Unregister an app
bext apps info <name>    # Show app details (routes, cache stats, deploy history)

# Deployment
bext deploy <dir> --app <name>   # Deploy app (build + swap)
bext rollback <name>             # Rollback to previous version
bext promote <name>              # Promote canary to 100%

# Multi-app platform
bext serve                       # Start the platform (serves all registered apps)
bext serve --config platform.toml

# Cache management
bext cache stats                 # Cache hit rates, sizes, per-app breakdown
bext cache purge --app <name>    # Purge app cache
bext cache purge --tag <tag>     # Purge by tag
bext cache purge --all           # Purge everything
bext cache warm <url-list>       # Pre-warm cache from URL list

# Plugin management
bext plugins list                # List installed plugins
bext plugins install <path.wasm> # Install WASM plugin
bext plugins remove <name>       # Remove plugin
bext plugins inspect <path.wasm> # Show manifest, permissions, size

# Inspection
bext routes [dir]                # Print route table (current scan command)
bext config validate [file]      # Validate config (current validate command)
bext health                      # Platform health check
bext metrics                     # Current metrics snapshot
bext ps                          # Running apps, isolate count, memory usage

# Flow engine
bext flows list                  # List active flows
bext flows inspect <id>          # Show flow steps + status
bext flows cancel <id>           # Cancel a running flow
bext flows gc                    # Force garbage collection

Implementation Tasks

CLI-1: CLI Framework Setup

Replace the hand-rolled arg parsing in main.rs with clap.

File: bext-server/src/cli.rs

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "bext", version, about = "Application deployment platform")]
pub struct Cli {
    #[command(subcommand)]
    pub command: Option,

    /// Config file path
    #[arg(short, long, default_value = "bext.config.toml")]
    pub config: String,

    /// Log level
    #[arg(long, default_value = "info")]
    pub log_level: String,
}

#[derive(Subcommand)]
pub enum Command {
    /// Run an app (auto-detect framework)
    Run {
        /// App directory (default: current dir)
        #[arg(default_value = ".")]
        dir: String,

        /// Listen address
        #[arg(short, long, default_value = "0.0.0.0:3000")]
        listen: String,

        /// Number of workers
        #[arg(short, long)]
        workers: Option<usize>,
    },

    /// Dev mode with watch + hot reload
    Dev {
        #[arg(default_value = ".")]
        dir: String,

        #[arg(short, long, default_value = "0.0.0.0:3000")]
        listen: String,
    },

    /// Build for production
    Build {
        #[arg(default_value = ".")]
        dir: String,

        /// Output directory
        #[arg(short, long, default_value = "dist")]
        output: String,
    },

    /// Start the multi-app platform
    Serve {
        /// Platform config
        #[arg(short, long, default_value = "platform.toml")]
        config: String,
    },

    /// Deploy an app
    Deploy {
        dir: String,
        #[arg(long)]
        app: String,
    },

    /// App management
    Apps {
        #[command(subcommand)]
        command: AppsCommand,
    },

    /// Cache management
    Cache {
        #[command(subcommand)]
        command: CacheCommand,
    },

    /// Plugin management
    Plugins {
        #[command(subcommand)]
        command: PluginsCommand,
    },

    /// Print route table
    Routes {
        #[arg(default_value = ".")]
        dir: String,
    },

    /// Validate config
    Config {
        #[command(subcommand)]
        command: ConfigCommand,
    },

    /// Platform health check
    Health,

    /// Current metrics
    Metrics,

    /// Running processes
    Ps,

    /// Flow engine management
    Flows {
        #[command(subcommand)]
        command: FlowsCommand,
    },
}

Tasks:

  • Add clap dependency to bext-server/Cargo.toml
  • Create cli.rs with full command tree
  • Migrate existing subcommands (build, validate, scan, version)
  • Wire up main.rs to use clap dispatch
  • Add shell completions generation (clap_complete)
  • Add --json flag to all inspection commands for machine-readable output
  • Colored terminal output with owo-colors or colored
  • Progress bars for long operations with indicatif

CLI-2: bext run Command

The primary command. Takes a directory, detects the framework, and serves it.

Flow:

bext run ./my-app
  1. detect framework (RT-1)
  2. load bext.config.toml if present, else generate defaults
  3. run transforms (using detected profile)
  4. if needs bundle: run build (Bun/esbuild)
  5. create isolate with SSR bundle
  6. start HTTP server
  7. serve requests: static → ISR cache → isolate render

Output:

  bext v0.5.0

  Framework:  Next.js (app router)
  Directory:  ./my-app
  Routes:     42 pages, 8 API routes
  Listen:     http://localhost:3000

  ready in 847ms

Tasks:

  • Implement run command handler
  • Call framework detection (RT-1)
  • Generate default config for detected framework
  • Trigger build if needed (delegate to Bun or esbuild)
  • Create isolate and start serving
  • Pretty startup banner with timing
  • Graceful shutdown on SIGINT/SIGTERM
  • --port shorthand for --listen
  • --open to open browser after start

CLI-3: bext dev Command

Development mode with file watching, hot reload, and source maps.

Behavior:

  • Watch source files for changes
  • On change: re-run affected transforms, rebuild SSR bundle, hot-reload isolate
  • WebSocket HMR connection to browser (send reload signal)
  • Source maps enabled for stack traces
  • Relaxed security (no rate limiting, CORS allow-all, auth bypass on localhost)

Tasks:

  • Implement dev command handler
  • File watcher (notify crate, already used in bundler.rs)
  • Incremental transform (only re-transform changed files)
  • WebSocket HMR endpoint (/bext-hmr)
  • Client-side HMR script injection (auto-inject <script> tag)
  • Source map generation in transforms
  • Dev-mode defaults (no rate limit, CORS *, auth bypass)
  • Error overlay (render build/runtime errors as HTML page)
  • --no-hmr flag to disable HMR (useful for debugging)

CLI-4: bext serve (Multi-App Platform)

Starts the platform server that routes to multiple apps based on hostname.

Config: platform.toml

[platform]
listen = "0.0.0.0:443"
tls_cert = "/etc/letsencrypt/live/example.com/fullchain.pem"
tls_key = "/etc/letsencrypt/live/example.com/privkey.pem"

[apps.marketing]
source = "./apps/marketing"
domains = ["example.com", "www.example.com"]
runtime = "ssr"
cache.default_ttl = "1h"

[apps.dashboard]
source = "./apps/dashboard"
domains = ["app.example.com"]
runtime = "ssr"
auth.required = true

[apps.api]
source = "./apps/api"
domains = ["api.example.com"]
runtime = "js"
rate_limit.rpm = 1000

[apps.docs]
source = "./apps/docs"
domains = ["docs.example.com"]
runtime = "static"

Tasks:

  • Parse platform.toml config format
  • Create PlatformServer that manages multiple AppInstances
  • Hostname → app routing (extends tenant cache)
  • Per-app isolates with independent caches
  • Per-app rate limiting
  • TLS termination (rustls)
  • Per-app health checks
  • bext apps list/add/remove commands to manage apps
  • Live reload of platform config (add/remove apps without restart)

CLI-5: Inspection Commands

Commands for inspecting the running platform state.

bext ps output:

APP          STATUS    WORKERS  MEMORY    REQ/S   CACHE HIT   UPTIME
marketing    running   4        48MB      1,240   94.2%       2d 14h
dashboard    running   2        32MB      420     87.6%       2d 14h
api          running   1        16MB      3,100   N/A         2d 14h
docs         static    -        -         180     99.8%       2d 14h

bext cache stats output:

APP          ENTRIES  HIT RATE  MEM USED   STALE/SWR  TAGS
marketing    2,481    94.2%     148MB      12         product,blog,page
dashboard    892      87.6%     64MB       3          user,report
api          -        -         -          -          -
docs         1,200    99.8%     24MB       0          -
TOTAL        4,573    93.1%     236MB      15

Tasks:

  • bext ps — query running apps, format table
  • bext health — check all subsystems (DB, cache, isolates, plugins)
  • bext metrics — JSON dump of all metrics
  • bext cache stats — per-app cache breakdown
  • bext routes — migrate existing scan command
  • bext config validate — migrate existing validate command
  • All commands support --json for machine-readable output

Binary Naming

Rename bext-server binary to bext:

# bins/bext/Cargo.toml (rename from bext-server)
[[bin]]
name = "bext"
path = "src/main.rs"

The old bext-server behavior (bext-server with no args starts the server) maps to bext serve or bext run ..

Tasks:

  • Rename bext-server crate to bext (or add bext as a bin alias)
  • Update all scripts and docs
  • Backward compat: bext with no args shows help, bext run . starts server

Shell Completions

Generate completions for bash, zsh, fish, PowerShell:

bext completions bash > /etc/bash_completion.d/bext
bext completions zsh > ~/.zfunc/_bext
bext completions fish > ~/.config/fish/completions/bext.fish

Tasks:

  • Add completions subcommand using clap_complete
  • Auto-install completions on bext install-completions