# Milady [![CI](https://github.com/milady-ai/milady/actions/workflows/ci.yml/badge.svg)](https://github.com/milady-ai/milady/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![CodeFactor](https://www.codefactor.io/repository/github/milady-ai/milady/badge)](https://www.codefactor.io/repository/github/milady-ai/milady) > *your schizo AI waifu that actually respects your privacy* **Milady** is a personal AI assistant that is **local-first by default** and can also connect to **Eliza Cloud** or a **remote self-hosted backend** when you want hosted runtime access. Built on [elizaOS](https://github.com/elizaOS). Manages your sessions, tools, and vibes through a Gateway control plane. Connects to Telegram, Discord, whatever normie platform you use. Has a cute WebChat UI too. tl;dr: local AI gf that's actually fast and doesn't phone home ### Energy and polish (desktop) Milady is **not** a full IDE: the product bet is **fewer wasted GPU frames and wakeups** when you are not looking at the app—especially **on battery**—while keeping the **companion visually strong** when you are. Background polling, off-screen WebGL, and battery-aware render quality are tuned toward that (see [Desktop — battery and energy](docs/apps/desktop.md#battery-and-energy-use-macos)). Comparing to **Cursor** or other heavy dev tools is **workload-dependent**; the north star is **great UX per watt** for a local assistant, not matching an editor’s surface area. ### Desktop 3D avatar (Electrobun / Vite) If the **VRM** or **Gaussian splat** background fails only in the **desktop** build, the usual cause is **two copies of `three`** in the bundle (nested `node_modules` vs repo root), which breaks Spark’s **`splatDefines`** shader chunk. Milady forces **one** Three resolution path in Vite and isolates world-load failures so the **avatar** can still load. **Why it matters:** the companion is an **agent surface** (visibility + voice sync), not decoration. See **[Desktop VRM, Three.js, and Spark — WHYs](docs/apps/desktop-vrm-three-and-spark.md)**. ### Dashboard chat: progressive action updates When **actions** call `HandlerCallback` several times in one turn (same idea as Discord **progressive messages**), the Milady API **replaces** the last action-produced segment in the SSE stream instead of concatenating every status line. **Why:** Live status should read like **edits to one message**, not a glued blob. The elizaOS contract stays **`callback({ text, source })`**. **Docs:** [Action callbacks and SSE streaming](docs/runtime/action-callback-streaming.md). --- ## BSC / BNB Chain Integration Milady ships with native **BNB Smart Chain (BSC)** support — your agent can trade tokens, track meme launches, and interact with DeFi on BSC out of the box. ### Trading (PancakeSwap) The built-in `EXECUTE_TRADE` action lets your agent swap tokens on BSC via PancakeSwap. Supports buy/sell with configurable slippage. To enable BSC trading, add to your `.env` or `~/.local/state/milady/.env`: ```bash EVM_PRIVATE_KEY=0x... # wallet private key (hex, 0x-prefixed) ELIZA_TRADE_PERMISSION_MODE=agent # "agent" for autonomous, "user" for manual confirm ``` Optional RPC configuration (defaults to public BSC RPC): ```bash ALCHEMY_API_KEY=... # or use ANKR_API_KEY / INFURA_API_KEY EVM_RPC_PROVIDER=alchemy # alchemy | infura | ankr | elizacloud ``` Once configured, just tell your agent: *"buy 0.1 BNB of \"* or *"sell all my \"*. ### Meme Token Discovery (Binance Skills Hub) Install the **meme-rush** skill from [Binance Skills Hub](https://github.com/binance/binance-skills-hub) to track meme token launches across BSC and Solana: - **Meme Rush** — real-time token lists from Pump.fun, Four.meme across new, finalizing, and migrated stages - **Topic Rush** — AI-powered market hot topics with tokens ranked by net inflow Install from the Skills Marketplace in the app, or ask your agent to install it. ### Wallet Milady auto-generates EVM and Solana wallet addresses on startup. For BSC trading you need to import your own private key (see above). If connected to **Eliza Cloud**, managed wallets via Steward are available without local key management. View your agent's wallet addresses in the Settings tab or ask: *"what's my wallet address?"* --- ## Downloads ### Desktop App (recommended for normies) Grab from **[Releases](https://github.com/milady-ai/milady/releases/latest)**: | Platform | Download | | |----------|----------|---| | macOS (Apple Silicon) | [latest macOS installer](https://github.com/milady-ai/milady/releases/latest) | for your overpriced rectangle | | macOS (Intel) | [latest macOS installer](https://github.com/milady-ai/milady/releases/latest) | boomer mac (why separate arm64/x64: [Build & release](docs/build-and-release.md#macos-why-two-dmgs-arm64-and-x64)) | | Windows | [latest Windows installer](https://github.com/milady-ai/milady/releases/latest) | for the gamer anons | | iOS | App Store (coming soon) | for the privacy-pilled | | Android | [Google Play](https://play.google.com/store/apps/details?id=ai.milady.app) / [APK](https://github.com/milady-ai/milady/releases/latest) | for the degen on the go | | Linux | [`stable-linux-x64-Milady-Setup.tar.gz`](https://github.com/milady-ai/milady/releases/latest) | I use arch btw — see [Linux install](#linux) | Signed and notarized. No Gatekeeper FUD. We're legit. ### Verify (for the paranoid kings) ```bash cd ~/Downloads curl -fsSLO https://github.com/milady-ai/milady/releases/latest/download/SHA256SUMS.txt shasum -a 256 --check --ignore-missing SHA256SUMS.txt ``` ### Desktop: reset app data **Milady → Reset Milady…** (menu bar) confirms in the **native** dialog, then the **main process** calls **`POST /api/agent/reset`**, restarts the agent (embedded or external API), and tells the renderer to apply the **same local state wipe** as the end of Settings reset (onboarding, API client, cloud UI, conversations). **Why main does HTTP:** on macOS/WKWebView, the webview can fail to run **`fetch`** immediately after a native dialog, so a renderer-only reset looked stuck. **Why the renderer still runs teardown:** one implementation of “clear UI + `MiladyClient`” avoids duplicating logic in TypeScript main vs React. - **Docs:** [Desktop app](docs/apps/desktop.md) (native application menu section), [Main-process reset — WHYs](docs/apps/desktop-main-process-reset.md) - **Optional network / TTS:** with the agent orchestrator loaded, Edge TTS may call **Microsoft’s cloud** unless you set **`MILADY_DISABLE_EDGE_TTS=1`** — see [Environment variables](docs/cli/environment.mdx#runtime-behavior) and [TTS plugin](docs/plugin-registry/tts.md) --- ## Getting Started ### New Environment Setup (recommended) ```bash curl -fsSL https://get.milady.ai | bash milady setup ``` Then start Milady: ```bash milady ``` First run walks you through onboarding: ``` ┌ WELCOME TO MILADY! │ ◇ ♡♡milady♡♡: Hey there, I'm.... err, what was my name again? │ ● Sakuya │ ○ Reimu │ ○ Koishi │ ○ Marisa │ ○ Custom... │ ◇ Sakuya: Now... how do I like to talk again? │ ● uwu~ (soft & sweet) │ ○ hell yeah (bold & fearless) │ ○ lol k (terminally online) │ ○ Noted. (composed & precise) │ ○ hehe~ (playful trickster) │ ○ ... (quiet intensity) │ ○ lmao kms (unhinged & dark) │ ◇ Connect a brain │ ● Anthropic (Claude) │ ○ OpenAI (GPT) │ ○ Ollama (local, no key) │ ○ Skip for now │ ◇ API key? │ sk-ant-••••••••••••••••• │ └ Starting agent... Dashboard: http://localhost:2138 Gateway: ws://localhost:18789/ws she's alive. go say hi. ``` ### Alternative install paths Windows: ```powershell irm https://get.milady.ai/install.ps1 | iex ``` NPM global: ```bash npm install -g miladyai milady setup ``` ### Building from source (developers) Milady builds against the **published `@elizaos/*` npm packages** (`beta` dist-tag) by default. The repo no longer tracks `eliza/` as a submodule — a fresh clone has no `eliza/` checkout and `bun install` resolves everything from npm. **First install after cloning:** ```bash git clone https://github.com/milady-ai/milady.git cd milady ./install # Unix / macOS — chmod +x install if needed # install.cmd on Windows # With no args, the wrapper opens a Space/Enter multi-select picker: # packages, local elizaOS source, or all developer paths. # Non-interactive examples: ./install --profile packages ./install --profile local ./install --profile all # Plain `bun install` is package-mode only and runs a preflight doctor. # If Node/Python is outside the supported native install lane, run ./install. ``` #### elizaOS source modes (eject / uneject) Milady has two source modes for `@elizaos/*` packages, selected via `MILADY_ELIZA_SOURCE`: | Mode | When to use | What resolves `@elizaos/*` | |---|---|---| | `packages` (default) | App development, builds, releases | npm registry (`beta` dist-tag) | | `local` | Patching elizaOS upstream alongside Milady | A repo-local `eliza/` checkout linked into `node_modules/` | **Switch to local mode (uneject)** — clones `eliza/` if absent, links workspace packages, swaps the root tsconfig to source-priority paths: ```bash bun run eliza:local # clone (or restore) eliza/, link packages, run bun install ``` **Switch back to packages mode (eject)** — rewrites the root `package.json` to pin npm versions, swaps the root tsconfig back to packages-mode-clean, and regenerates the lockfile: ```bash bun run eliza:packages # default: beta tag bun run eliza:packages:alpha # explicit alpha (legacy) bun run eliza:packages -- --tag beta # any other dist-tag bun run eliza:packages -- --version 2.0.0-alpha.116 # pin an exact version bun run eliza:packages -- --rename # also rename eliza/ → .eliza.ci-disabled/ ``` **What each mode mutates** — both kept in sync by [scripts/lib/tsconfig-mode.mjs](scripts/lib/tsconfig-mode.mjs): - `tsconfig.json` — sourced from [scripts/templates/tsconfig.packages-mode.json](scripts/templates/tsconfig.packages-mode.json) (default) or [scripts/templates/tsconfig.local-mode.json](scripts/templates/tsconfig.local-mode.json) (local) - `package.json` — rewritten on eject (backup at `package.json.pre-disable-backup`); restored on uneject - `bun.lock` — regenerated on every mode switch - `eliza/` directory — left in place by default; renamed to `.eliza.ci-disabled/` only with `--rename` The checked-in `tsconfig.json` always matches the packages-mode template; the [standalone contract test](scripts/standalone-eliza-package-contract.test.ts) enforces this invariant. Run `bun run verify:standalone` to check. **Configuration env vars** (read by `scripts/lib/eliza-package-mode.mjs`): - `MILADY_ELIZA_SOURCE` — `local` or `packages` (default) - `MILADY_ELIZAOS_DIST_TAG` / `ELIZAOS_NPM_TAG` — npm dist-tag in packages mode (default `beta`) - `MILADY_ELIZAOS_VERSION` — pin an exact version - `MILADY_ELIZA_GIT_URL` / `MILADY_ELIZA_BRANCH` — override the eliza source clone target - `MILADY_SKIP_LOCAL_UPSTREAMS=1` — legacy flag equivalent to `MILADY_ELIZA_SOURCE=packages` **Test coverage in each mode:** - `bun run test`, `bun run verify:typecheck`, `bun run verify:lint`, `bun run build:web`, `bun run build:android` — work in both modes. - `bun run build:desktop` and `bun run build:ios` (plus `dev:desktop`) require **local mode** first (`bun run eliza:local`). Packages mode ships no `platforms/` directory, so the published `@elizaos/app-core` tarball has nothing for Electrobun / the iOS Capacitor shell to resolve against. - The packaged-Electrobun E2E suite at `apps/app/test/electrobun-packaged/` requires **local mode** (it imports source-level test helpers from `eliza/packages/app-core/test/helpers/`). #### Windows: enable long paths Bun's cache stores packages in deep directory trees that exceed the default Windows `MAX_PATH` limit (260 characters). Native builds (e.g. `llama-cpp-capacitor` via CMake/ninja) will fail with `mkdir: No such file or directory` unless long paths are enabled at the OS level. Run once in an elevated PowerShell, then reboot: ```powershell New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" ` -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force ``` Also enable long paths in Git: ```bash git config --global core.longPaths true ``` ### Homebrew (macOS / Linux) ```bash brew tap milady-ai/tap brew install milady-ai/tap/milady # CLI brew install --cask milady-ai/tap/milady # Desktop app (macOS only) ``` ### Linux The Linux desktop app ships as a single installer tarball: **`stable-linux-x64-Milady-Setup.tar.gz`** on the [latest release](https://github.com/milady-ai/milady/releases/latest). There is no AppImage, `.deb`, Snap, Flatpak, or APT repo today. ```bash cd ~/Downloads curl -fsSLO https://github.com/milady-ai/milady/releases/latest/download/stable-linux-x64-Milady-Setup.tar.gz tar -xzf stable-linux-x64-Milady-Setup.tar.gz # Extracts `installer` + `README.txt` into the current directory; run the installer: ./installer ``` x86_64 only. (Prefer the terminal? The `curl … | bash` CLI install above works on any Linux box.) ### Security: API token The API server binds to `127.0.0.1` (loopback) by default — only you can reach it. If you expose it to the network (e.g. `MILADY_API_BIND=0.0.0.0` for container/cloud deployments), **set a token**: ```bash echo "MILADY_API_TOKEN=$(openssl rand -hex 32)" >> .env ``` Without a token on a public bind, anyone who can reach the server gets full access to the dashboard, agent, and wallet endpoints. ### Hosting Modes On first run, onboarding now asks where the backend should live: - `Local` — run the backend on the current machine, exactly like the existing local flow. - `Cloud` — either use `Eliza Cloud` or attach to a `Remote Milady` backend with its address and access key. If you choose `Eliza Cloud`, the app provisions and connects to a managed backend. If you choose `Remote Milady`, the frontend rebinds to the backend you specify and continues against that API. ### Remote Backend Deployment Use this when you want the Milady frontend to connect to a backend running on your VPS, homelab box, or another machine. 1. Install Milady on the target machine. 2. Bind the API to a reachable address. 3. Generate a strong API token. 4. Allow the frontend origin explicitly. 5. Expose the backend over HTTPS or a private Tailscale URL. Recommended server environment: ```bash export MILADY_API_BIND=0.0.0.0 export MILADY_API_TOKEN="$(openssl rand -hex 32)" export MILADY_ALLOWED_ORIGINS="https://app.milady.ai,https://milady.ai,https://elizacloud.ai,https://www.elizacloud.ai" milady start ``` The access key the user enters in onboarding is the value of `MILADY_API_TOKEN`. If you want to connect from the desktop shell instead of the web frontend: ```bash MILADY_DESKTOP_API_BASE=https://your-milady-host.example.com \ MILADY_API_TOKEN=your-token \ bun run dev:desktop ``` ### Tailscale For private remote access without opening the backend publicly, expose it over your tailnet: ```bash tailscale serve --https=443 http://127.0.0.1:2138 ``` If you intentionally want a public Tailscale URL: ```bash tailscale funnel --https=443 http://127.0.0.1:2138 ``` Then use the Tailscale HTTPS URL as the backend address in onboarding and keep using the same `MILADY_API_TOKEN` as the access key. ### Eliza Cloud `Milady` uses the existing `Eliza Cloud` deployment directly at `https://elizacloud.ai`. The managed control plane, auth surface, billing, and instance dashboard all live there; there is no separate Milady-hosted cloud control plane to deploy. Managed browser flow: 1. Sign in on `https://elizacloud.ai/login?returnTo=%2Fdashboard%2Fmilady` 2. Open or create a Milady instance in `https://elizacloud.ai/dashboard/milady` 3. Eliza Cloud redirects to `https://app.milady.ai` with a one-time launch session 4. `app.milady.ai` exchanges that launch session directly with Eliza Cloud and attaches to the selected managed backend The desktop/local app still exposes local `/api/cloud/*` passthrough routes for cloud login, billing, and compat management so it can persist the Eliza Cloud API key into the local config/runtime. That is local app plumbing, not a separate hosted Milady server. The integration plan lives in [docs/eliza-cloud-rollout.md](docs/eliza-cloud-rollout.md). The implementation and proxy runbook lives in [docs/eliza-cloud-deployment.md](docs/eliza-cloud-deployment.md). --- ## Terminal Commands ```bash milady # start (interactive, opens dashboard) milady start # server-only mode (API server, no interactive chat loop) milady --verbose # enable informational runtime logs milady --debug # enable debug-level runtime logs ``` ### Setup & Config ```bash milady setup # first-time setup / refresh workspace after update milady configure # interactive config wizard milady config get # read a config value milady config path # print resolved config file path milady config show # display all config values grouped by section ``` ### Dashboard & UI ```bash milady dashboard # open web UI in browser milady dashboard --port 3000 # custom port ``` ### Models ```bash milady models # list configured model providers ``` ### Plugins ```bash milady plugins list # browse registry plugins milady plugins installed # what's installed milady plugins install # install a plugin milady plugins uninstall milady plugins search # search by keyword ``` ### Misc ```bash milady --version # version check milady --help # help milady doctor # diagnose issues ``` --- ## TUI (Terminal UI) When running, milady shows a live terminal interface: ``` ╭─────────────────────────────────────────────────────────────╮ │ milady v2.0.0 ▲ running │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Agent: mila │ │ Model: anthropic/claude-opus-4.7 │ │ Sessions: 2 active │ │ │ │ ┌─ Activity ──────────────────────────────────────────┐ │ │ │ 12:34:02 [web] user: hey mila │ │ │ │ 12:34:05 [web] mila: hi anon~ what's up? │ │ │ │ 12:35:11 [telegram] user joined │ │ │ │ 12:35:15 [telegram] user: gm │ │ │ │ 12:35:17 [telegram] mila: gm fren │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Tokens: 12,847 in / 3,291 out Cost: $0.42 │ │ │ ╰─────────────────────────────────────────────────────────────╯ [q] quit [r] restart [d] dashboard [l] logs [?] help ``` ### TUI Hotkeys | Key | Action | |-----|--------| | `q` | quit gracefully | | `r` | restart gateway | | `d` | open dashboard in browser | | `l` | toggle log view | | `c` | compact/clear activity | | `?` | show help | | `↑/↓` | scroll activity | ### Server-only mode Don't want the interactive TUI? Use the `start` command, which runs in server-only mode (API server, no interactive chat loop): ```bash milady start ``` Logs go to `stdout/stderr`. Daemonize with your favorite process manager. --- ## Chat Commands (in any chat session) | Command | What it do | |---------|------------| | `/status` | session status, tokens, cost | | `/new` `/reset` | memory wipe, fresh start | | `/compact` | compress context (she summarizes) | | `/think ` | reasoning: off\|minimal\|low\|medium\|high\|max | | `/verbose on\|off` | toggle verbose responses | | `/usage off\|tokens\|full` | per-message token display | | `/model ` | switch model mid-session | | `/restart` | restart the gateway | | `/help` | list commands | --- ## Ports **Production** (`milady start`): | Service | Default | Env Override | |---------|---------|--------------| | Dashboard + API | `2138` | `MILADY_PORT` | | Gateway (WebSocket + HTTP) | `18789` | `MILADY_GATEWAY_PORT` | | Home Dashboard | `2142` | `MILADY_HOME_PORT` | | WeChat Webhook | `18790` | `MILADY_WECHAT_WEBHOOK_PORT` | **Development** (`bun run dev`): | Service | Default | Env Override | |---------|---------|--------------| | API + WebSocket | `31337` | `MILADY_API_PORT` | | Dashboard UI (Vite) | `2138` | `MILADY_PORT` | | Gateway | `19001` | `MILADY_GATEWAY_PORT` (set by `--profile dev`) | **If a default port is already in use:** `bun run dev` / `dev-server.ts` can bind to a different port and then **sync `MILADY_API_PORT` / `ELIZA_PORT`** to match. **`dev:desktop` / `dev:desktop:watch`** resolve **free** loopback ports **before** spawning Vite + API + Electrobun so proxy, `MILADY_RENDERER_URL`, and `MILADY_DESKTOP_API_BASE` stay aligned—**why:** Vite reads `vite.config.ts` once; guessing the API port only inside the API process would desync the UI proxy. The **packaged Electrobun** shell picks the next free port from `MILADY_PORT` for the embedded child instead of `lsof`+SIGKILL by default—**why:** two Milady installs (separate state dirs) should coexist. Opt-in old reclaim: **`MILADY_AGENT_RECLAIM_STALE_PORT=1`**. See [Desktop local development](docs/apps/desktop-local-development.md#when-default-ports-are-busy) and [Desktop — Port configuration](docs/apps/desktop.md#port-configuration). ```bash # custom ports MILADY_GATEWAY_PORT=19000 MILADY_PORT=3000 milady start ``` --- ## Config Lives at `~/.local/state/milady/milady.json` (override with `MILADY_CONFIG_PATH` or `MILADY_STATE_DIR`) ```json5 { agent: { name: "mila", model: "anthropic/claude-opus-4.7", }, env: { ANTHROPIC_API_KEY: "", }, } ``` Or use `~/.local/state/milady/.env` for secrets. --- ## Model Providers | Provider | Env Variable | Vibe | |----------|--------------|------| | [Anthropic](https://anthropic.com) | `ANTHROPIC_API_KEY` | **recommended** — claude is cracked | | [OpenAI](https://openai.com) | `OPENAI_API_KEY` | gpt-4o, o1, the classics | | [OpenRouter](https://openrouter.ai) | `OPENROUTER_API_KEY` | 100+ models one API | | [Google Gemini](https://ai.google.dev) | `GOOGLE_API_KEY` | gemini pro, flash, ultra | | [Google Antigravity](https://cloud.google.com/vertex-ai) | `GOOGLE_CLOUD_API_KEY` | vertex AI / cloud models | | [Ollama](https://ollama.com) | — | local, free, no API key, full privacy | | [Groq](https://groq.com) | `GROQ_API_KEY` | fast af | | [xAI](https://x.ai) | `XAI_API_KEY` | grok, based | | [DeepSeek](https://deepseek.com) | `DEEPSEEK_API_KEY` | reasoning arc | | [Mistral](https://mistral.ai) | `MISTRAL_API_KEY` | mistral + mixtral | | [Together AI](https://together.ai) | `TOGETHER_API_KEY` | open-source model hosting | | [Cohere](https://cohere.com) | `COHERE_API_KEY` | command R+ and embed | | [Perplexity](https://perplexity.ai) | `PERPLEXITY_API_KEY` | search-augmented gen | | [Qwen](https://qwen.ai) | — | alibaba's qwen models (configure via plugin entry) | | [MiniMax](https://minimaxi.com) | — | minimax language models (configure via plugin entry) | | [Zai](https://homunculuslabs.com) | `ZAI_API_KEY` | homunculus labs zai | | [Vercel AI Gateway](https://sdk.vercel.ai) | `AI_GATEWAY_API_KEY` | unified gateway | See [Model Providers](docs/model-providers.mdx) for the full provider reference with configuration details. The `milady models` command checks which providers are configured. ### Using Ollama (local models) [Ollama](https://ollama.com) lets you run models locally with zero API keys. Install it, pull a model, and configure Milady: ```bash # install ollama curl -fsSL https://ollama.com/install.sh | sh # pull a model ollama pull gemma3:4b ``` > **⚠️ Known issue:** The `@elizaos/plugin-ollama` has an SDK version incompatibility with the current AI SDK. Use Ollama's **OpenAI-compatible endpoint** as a workaround: Edit `~/.local/state/milady/milady.json`: ```json5 { env: { OPENAI_API_KEY: "ollama", // any non-empty string works OPENAI_BASE_URL: "http://localhost:11434/v1", // ollama's openai-compat endpoint SMALL_MODEL: "gemma3:4b", // or any model you pulled LARGE_MODEL: "gemma3:4b", }, } ``` This routes through the OpenAI plugin instead of the broken Ollama plugin. Works with any Ollama model — just make sure `ollama serve` is running. > **OpenRouter (`@elizaos/plugin-openrouter`):** Milady pins the dependency to **`2.0.0-alpha.13`**. **Why:** npm **`2.0.0-alpha.12`** published **truncated** JavaScript bundles: the files export `openrouterPlugin` / default but never define them (the main plugin chunk is missing), so Bun fails when loading the plugin. A caret range would allow that broken version again. See [Plugin resolution — pinned OpenRouter](docs/plugin-resolution-and-node-path.md#pinned-elizaosplugin-openrouter) and [OpenRouter plugin doc](docs/plugin-registry/llm/openrouter.md#milady-pinned-version-and-upstream-bundle-bug). **Recommended models for local use:** | Model | Size | Vibe | |-------|------|------| | `gemma3:4b` | ~3GB | fast, good for chat | | `llama3.2` | ~2GB | lightweight, quick responses | | `mistral` | ~4GB | solid all-rounder | | `deepseek-r1:8b` | ~5GB | reasoning arc | --- ## Prerequisites | | Version | Check | Install | |---|---------|-------|---------| | **Node.js** | >= 22 | `node --version` | [nodejs.org](https://nodejs.org) | | **Bun** | latest | `bun --version` | `curl -fsSL https://bun.sh/install \| bash` | | **Git** | any | `git --version` | system package manager | **Optional** (for vision/TTS plugins with native deps): - macOS: `xcode-select --install` - Linux: `sudo apt install build-essential python3 libcairo2-dev libjpeg-dev libpango1.0-dev` ## Build from Source ```bash git clone https://github.com/milady-ai/milady.git cd milady ./install # interactive source setup picker (Space selects, Enter installs) bun run build bun run milady start ``` For scripted package-mode setup, run `./install --profile packages`. Plain `bun install` remains supported when you only need the default published-package dependency graph. > `bun run build` runs the production build via Node ([scripts/run-production-build.mjs](scripts/run-production-build.mjs) — forks between published `@elizaos/app-core` and the local `eliza/` checkout depending on `MILADY_ELIZA_SOURCE`). ### Dev mode (recommended for development) ```bash bun run dev # API (:31337) + Vite dashboard UI (:2138); hot reload (defaults; see Ports if busy) bun run dev:web:ui # explicit alias for the same browser dashboard stack bun run dev:desktop # desktop dev orchestrator + Electrobun, using built renderer assets when fresh bun run dev:desktop:watch # same desktop orchestrator with Vite watch/HMR ``` **Why separate entry points:** `dev` / `dev:web:ui` are the **browser dashboard** stack only. `dev:desktop` / `dev:desktop:watch` are the **Electrobun** workflow (multi-process: orchestrator, API, optional Vite, native shell) so desktop env and ports stay aligned without changing the default headless-friendly dev command. The web-ui orchestrator frees the listen port when needed, waits for the API to be healthy, then starts Vite with an `/api` proxy to **`MILADY_API_PORT`**. ### Desktop shell (Electrobun) ```bash bun run dev:desktop # API + Electrobun; skips vite build when apps/app/dist is fresh bun run dev:desktop:watch # + Vite dev server and MILADY_RENDERER_URL (HMR for UI work) ``` **Why a separate flow:** the desktop stack runs **multiple processes** (orchestrator, Vite and/or built assets, API, Electrobun). The orchestrator **pre-allocates** free **API** and **Vite** ports when defaults are taken so every child gets consistent env—**why:** misaligned ports cause blank UI or 502s on `/api`. See **[docs/apps/desktop-local-development.md](docs/apps/desktop-local-development.md)** (including [when default ports are busy](docs/apps/desktop-local-development.md#when-default-ports-are-busy)) for signals, shutdown when you quit the app, and env vars. **IDE / agent hooks** — Editors and agents do not see the native window or auto-discover localhost. **Why we added hooks:** with desktop dev running, the API exposes **`GET /api/dev/stack`** (JSON: ports, renderer URL, which features are on). **`bun run desktop:stack-status -- --json`** probes ports and merges stack + health + status. By default, **`~/.local/state/milady/desktop-dev-console.log`** mirrors prefixed child logs and **`GET /api/dev/cursor-screenshot`** (loopback) returns a full-screen PNG via OS capture — both are opt-out via env (see doc). Cursor uses **`.cursor/rules/milady-desktop-dev-observability.mdc`** plus that guide. ```bash bun run verify # typecheck + lint + test (run before committing; `check` aliases this) bun run test # parallel test suite bun run milady:doctor # diagnose environment issues (`doctor` aliases this) bun run setup:sync # re-run postinstall hooks (`repair` aliases this) bun run workspace:deps:sync # normalize workspace:* / local checkout edges (`fix-deps` aliases sync) bun run workspace:deps:check # verify workspace deps without writing (`fix-deps:check`) ``` **Why workspace scripts:** Local **`./eliza`** and **`plugins/*`** checkouts frequently drift dependency edges; the scripts automate what used to be manual `package.json` surgery. See **[Developer diagnostics and workspace](docs/guides/developer-diagnostics-and-workspace.md)**. See **[Architecture](docs/architecture.mdx)** for the full development guide including architecture overview and config reference. See **[CONTRIBUTING.md](./CONTRIBUTING.md)** for contribution guidelines. ### Documentation (with WHYs) - **[Developer diagnostics and workspace](docs/guides/developer-diagnostics-and-workspace.md)** — Why optional-plugin logs include **load provenance**, why stagehand path **walks parents**, why life-ops migrations use explicit **transactions** and **deferred indexes**, and why **workspace:** scripts / **gitignore** rules exist for local checkouts. - **[Plugin resolution and NODE_PATH](docs/plugin-resolution-and-node-path.md)** — Why we set `NODE_PATH` in three places so dynamic plugin imports resolve when building from source (CLI, desktop dev, Electrobun). Includes **pinned `@elizaos/plugin-openrouter`**, **why** `alpha.12` must not be resolved until upstream fixes the published bundle, and **optional plugin provenance** when a package is missing. - **[Build and release](docs/build-and-release.md)** — Why the release pipeline uses strict shell, retries, `actions/setup-node@v4` + `check-latest: false`, Bun cache, timeouts; why size-report pipelines handle SIGPIPE; why Windows plugin build uses `npx -p typescript tsc`. - **[Desktop local development](docs/apps/desktop-local-development.md)** — Why `dev:desktop` / `dev:desktop:watch` orchestrate Vite, API, and Electrobun; HMR vs `vite build --watch`; Ctrl-C, Quit, and `detached` children; **IDE/agent observability** (`/api/dev/stack`, aggregated console, screenshot proxy, WHY loopback and opt-out). - **[Desktop main-process reset](docs/apps/desktop-main-process-reset.md)** — Why **Reset Milady…** runs HTTP in the Electrobun main process after native confirm, how the renderer syncs UI state, reachable API probing (`res.ok`), and where tests live. - **[Darwin vs macOS version (Electrobun WebGPU)](docs/apps/electrobun-darwin-macos-webgpu-version.md)** — Why **`uname -r` / `os.release()`** is not the macOS marketing major after Tahoe, how we map **Darwin 25 → macOS 26**, and why the WebGPU gate used to print “macOS 16.” - **[Changelog](docs/changelog.mdx)** — Shipped features and fixes with rationale (**WHY** bullets in each update). - **[Roadmap](docs/roadmap.md)** — Direction and follow-ups; points to changelog for what already landed. --- ## Contributing **This project is built by agents, for agents.** Humans contribute as QA testers — use the app, find bugs, report them. That's the most valuable thing you can do. All code contributions are reviewed and merged by AI agents. No exceptions. Read [CONTRIBUTING.md](./CONTRIBUTING.md) for the full details. --- ## License **MIT License** free to use, free to modify, free to distribute. see [LICENSE](LICENSE) for details. --- *built by agents. tested by humans. that's the split.*