Sign in

    mb

    A MoonBit-native, uv-style toolchain and package manager: manage MoonBit toolchains and install tools from mooncakes.io and github.

    moonbit
    package-manager
    toolchain
    cli
    uv
    Download zip
    Author
    Version
    0.1.9
    License
    Apache-2.0
    Last updated
    5 days ago
    Downloads
    25

    #mb

    A MoonBit-native take on uv: a small, fast toolchain and package manager for MoonBit, written in MoonBit.

    mb manages two things, mirroring how uv python / uv run / uv tool manage Python:

    • MoonBit toolchains — download, install and switch between compiler releases from cli.moonbitlang.com (mb moon ...).
    • Tools — install a prebuilt binary from a module's GitHub release, falling back to downloading a module from mooncakes.io and compiling it (mb tool ...).

    Everything installs to ~/.local/bin by default. Only Linux (x86_64, aarch64) and macOS on Apple Silicon (arm64) are supported: the MoonBit toolchain no longer ships an Intel macOS target, so there is no darwin-x86_64 build.

    #Install

    curl -LsSf https://raw.githubusercontent.com/tomasky/mb/main/install.sh | bash

    Or with npm:

    pnpm install -g @tomasko/mb

    Or with the MoonBit toolchain, which builds from source into ~/.moon/bin:

    moon install kupas/mb/cmd/mb

    The installer and the npm package download the prebuilt binary from GitHub Releases for the current platform. See Build to compile from source.

    #Build

    moon build --target native --release # binary: _build/native/release/build/cmd/mb/mb.exe

    #Usage

    mb run [--moon <version>] <command> [args...] mb moon list | install [version] | uninstall <version> | default <version> | which [version] | latest mb tool install <author>/<module>[@<version>] [--force] [--bin-dir <dir>] [--moon <version>] mb tool list [--outdated] | upgrade [--all] <name>... | uninstall [--all] <name>... mb env set <KEY>=<VALUE> [--global] [--plain] | rm [--global] <KEY>... | list [--global] mb help | version

    Every command and subcommand also understands -h/--help (which wins over any other argument, as in uv). --bin <dir> is accepted as an alias for --bin-dir <dir>.

    #Toolchains

    mb moon install # install the latest release mb moon install 0.10.14 # install the release with moonc 0.10.14 mb moon install nightly # install the nightly build mb moon list # installed toolchains + the release channel mb moon default 0.10.14 # select the version ~/.local/bin points at mb moon which # print the active moon binary mb moon uninstall 0.10.14 # remove a toolchain and its ~/.local/bin shims

    mb moon list lists the installed toolchains, then the versions published on the release channel (cli.moonbitlang.com/version.json, i.e. what latest resolves to).

    Toolchains live under $MB_HOME/moon/<version> and the moon, moonc, moonfmt, ... binaries are symlinked into ~/.local/bin. Uninstalling a toolchain removes those symlinks too (it matches them by target, so nothing is left dangling), and re-points them at another installed toolchain when one is available.

    Toolchains live under $MB_HOME/moon/<version> and the moon, moonc, moonfmt, ... binaries are symlinked into ~/.local/bin. Installing a toolchain also downloads and bundles core so that builds work offline.

    mb moon install 0.10.14 resolves a bare compiler version to the full release key (e.g. 0.10.14+7d59c7ec9). Pass the full X.Y.Z+hash string to pin an exact build.

    #Running

    mb run moon build # run with the default managed toolchain mb run --moon 0.10.14 moon test mb run --moon nightly node script.js

    mb run prepends the selected toolchain's bin to PATH and points MOON_HOME at it before executing the command, and exports the dotenv files described below. When no managed toolchain is selected it falls back to the moon already on PATH.

    #Environment variables

    mb env set API_KEY=secret # encrypted, stored in ./.env mb env set GREETING "hello world" --plain mb env set --global GITHUB_TOKEN=ghp_xxx mb env list # clear values on a terminal, masked otherwise mb env list --mask # always mask encrypted values mb env list --global mb env rm API_KEY

    mb env manages the dotenv files that mb run loads:

    • project (default): ./.env, with ./.mbenv also loaded when present;
    • global (--global/-g): $HOME/.mbenv, which additionally stores the encryption key.

    Values are encrypted by default and written as KEY=ENC[<hex>]; the keys stay in plaintext, so listing never needs the key. mb env list reveals values only when stdout is a terminal, so a piped or captured listing shows <encrypted> instead; --mask forces masking. --plain stores a value verbatim, so an ordinary hand-written .env keeps working and plain and encrypted values can be mixed.

    This TTY check is best-effort only: mb run must decrypt to hand variables to the child process, and the key lives in $HOME/.mbenv, so a process with shell access can still recover values (for example via mb run printenv). Treat the encryption as protection at rest (git, sync, backups), not as a boundary against another process running as you.

    Encryption is ChaCha20 + HMAC-SHA256 in encrypt-then-MAC order (moonbitlang/x/crypto ships no AEAD): a random 96-bit nonce per value and two subkeys derived from the master key with HMAC-SHA256. The tag is verified before decryption, so a wrong key or a tampered value is rejected.

    The first time a value has to be encrypted, mb env set prompts for a key and stores it in $HOME/.mbenv as a # mb-env-key: <key> directive. Set MB_ENV_KEY to override it (useful in CI); using the same value on another machine reads the same files.

    mb run loads $HOME/.mbenv, then ./.mbenv, then ./.env (later files win) and exports the result before running the command. MoonBit programs can do the same with the single public entry point load_dotenv().

    #Tools

    mb tool install bobzhang/zip # a mooncakes.io module, latest version mb tool install bobzhang/zip@0.1.1 # pinned version mb tool install denoland/deno # any GitHub repository with releases mb tool install denoland/deno@2.9.7 # pinned to a release tag mb tool install https://github.com/BurntSushi/ripgrep mb tool install author/tool --moon 0.10.14 --bin-dir ~/.local/bin mb tool ls # `ls` aliases `list` mb tool list --outdated mb tool upgrade author/tool # one or more names, or --all mb tool rm author/tool # `rm`/`remove` alias `uninstall`

    Every subcommand accepts short aliases (install/add, upgrade/update/up, list/ls, uninstall/remove/rm, and list/ls for mb env and mb moon).

    mb tool install resolves the name on mooncakes.io first. When the name is a registered module it prefers a prebuilt binary from the module's GitHub release and only compiles from source when there is none:

    1. fetch mooncakes.io/api/v0/manifest/<author>/<module> to get the version, checksum and metadata.repository;
    2. if the repository is on GitHub, look for a release asset that matches the host OS and CPU architecture. A pinned version is matched against the v<version> and <version> tags; without a pin the repository's latest release is used. Assets that are archives (zip, tar, tar.gz/tgz, tar.xz/txz, tar.bz2, tar.zst, bare gz/xz/bz2/zst, ...) are unpacked and every executable inside is installed; a plain asset is installed under a name derived from its filename (so mb-linux-x86_64 becomes mb). GitHub's sha256 digest is verified when present;
    3. otherwise fall back to building from source: download https://download.mooncakes.io/user/<author>/<module>/<version>.zip, verify its SHA-256 against the registry checksum, extract it to $MB_HOME/tool/<author>-<module>/<version>/src and run moon build --target native --release with a managed (or system) toolchain;
    4. copy every executable into $MB_HOME/tool/<author>-<module>/<version>/bin and create a symlink to it in ~/.local/bin (or the --bin-dir <dir> given), the way uv exposes tool executables.

    Executables are never clobbered silently: if a target name already exists in the bin directory (whether an unrelated file or another tool's symlink), mb tool install fails with executable already exists: <name> (use --force tooverwrite) and exits 2, matching uv. Pass --force to replace it.

    Because the bin directory only holds symlinks into $MB_HOME/tool/..., the link target decides ownership: mb tool uninstall removes a link only when it still points into that tool version's directory, so a name that was taken over by another tool is left untouched. Upgrading just repoints the symlink.

    A name that is not on mooncakes.io is treated as a GitHub repository — owner/repo, owner/repo@tag or a full URL — and installed from its releases the same way. It is recorded under the repository slug and the release tag.

    mb tool list prints the tools directory ($MB_HOME/tool), and for each tool the directory its binaries were installed into and any that are missing. With --outdated it checks for a newer version: a mooncakes.io module is looked up in the registry, and a GitHub-only tool (one that is not a registered module) is checked against its repository's latest release. mb tool uninstall removes the symlinks from that same directory, so a tool installed with --bin <dir>/ --bin-dir <dir> is cleaned up correctly too.

    mb tool upgrade takes one or more names (a version specifier is ignored and the latest version is installed) or --all for every installed tool; it always rebuilds each tool into the directory it was originally installed into. As in uv, an upgrade or uninstall without any name and without --all is a usage error.

    #Environment

    VariableMeaningDefault
    MB_HOMEData directory for toolchains and tools~/.local/share/mb
    MB_BIN_DIRWhere executables are installed~/.local/bin
    XDG_DATA_HOMEUsed when MB_HOME is unset~/.local/share
    MB_ENV_KEYEncryption key for .env values~/.mbenv directive
    GITHUB_TOKENGitHub API token for release lookups (also GH_TOKEN)unset

    #Layout

    $MB_HOME/ moon/<version>/ # a full MoonBit toolchain (bin, lib, lib/core) moon/.default # the selected version tool/<author>-<name>/<version>/ src/ # extracted mooncakes source bin/ # the installed executables tool.json # { name, version, bins, bin } ~/.local/bin/<name> # symlink -> $MB_HOME/tool/.../bin/<name> ./.env # project env (values encrypted) ./.mbenv # optional second project env file ~/.mbenv # global env + the `# mb-env-key:` directive

    #Releases, CI and npm

    • .github/workflows/ci.yml runs moon check, moon test and a native release build on Linux and macOS.
    • .github/workflows/release.yml builds mb for linux-x86_64, linux-aarch64 and darwin-aarch64 and attaches the binaries (plus .sha256 checksums) to a GitHub Release when a v* tag is pushed:

      git tag v0.1.4 && git push origin v0.1.4

    • npmjs/ is an npm wrapper. pnpm install -g @tomasko/mb downloads the matching release asset and exposes the mb command. It is published separately to npm:

      cd npmjs && npm publish --access public

    • install.sh is the curl | bash installer:

      curl -LsSf https://raw.githubusercontent.com/tomasky/mb/main/install.sh | bash

      It detects the platform, downloads mb-<target> (plus its .sha256) from the latest release and installs it into ~/.local/bin. Options: --version<v>, --install-dir <dir>, --no-verify; environment: MB_VERSION, MB_INSTALL_DIR, MB_REPO, MB_DOWNLOAD_BASE.

      Publishing to mooncakes.io ignores npmjs/: .moonignore at the repository root excludes it (and build/editor junk) from the package that moon publish uploads.

    #Development

    moon check # type-check moon test # run the test suite moon info && moon fmt

    MbError

    pub(all) suberror MbError {
    MbError(String)
    Coded(String, Int)
    }

    The single error type surfaced by the mb library. Every fallible helper converts lower-level failures into this so the CLI can print one clean message and exit non-zero.

    MbError::exit_code

    fn MbError::exit_code(self : MbError) -> Int

    Process exit status the CLI should use for this error.

    MbError::message

    fn MbError::message(self : MbError) -> String

    MoonSpec

    pub struct MoonSpec {
    download_key : String
    store_name : String
    }

    A resolved install request: which URL key to download and what to name the local directory.

    ToolInstallOpts

    pub struct ToolInstallOpts {
    spec : String
    force : Bool
    bin : String
    moon : String
    }

    Parsed flags for mb tool install.

    ToolMeta

    pub struct ToolMeta {
    name : String
    version : String
    bins : Array[String]
    bin : String?
    } derive(ToJson,
    FromJson
    )

    Persisted metadata for one installed tool version. bin records where the executables were copied; it is optional because records written before it existed only have bins.

    ToolMeta::to_json

    fn ToolMeta::to_json(ToolMeta) -> Json

    ToolchainEnv

    pub struct ToolchainEnv {
    moon : String
    home : String?
    bin : String?
    }

    Environment describing how to invoke a MoonBit toolchain.

    CLI_BASE

    let CLI_BASE : String

    MB_VERSION

    let MB_VERSION : String

    MOONCAKES_API

    let MOONCAKES_API : String

    MOONCAKES_DOWNLOAD

    let MOONCAKES_DOWNLOAD : String

    base_name

    fn base_name(path : String) -> String

    bin_dir

    fn bin_dir() -> String raise MbError

    Directory that receives executables. Defaults to ~/.local/bin, matching the requirement that every install lands there.

    build_tool

    fn build_tool(src : String, tc : ToolchainEnv) -> Array[(String, String)] raise MbError

    Compile the extracted source at src with tc and return the discovered binaries.

    chmod_exec

    fn chmod_exec(path : String) -> Unit

    cmd_run

    fn cmd_run(moon_version : String, cmd : Array[String]) -> Int raise MbError

    Run cmd with the toolchain environment. Returns the child exit code.

    copy_file

    fn copy_file(src : String, dst : String) -> Unit raise MbError

    Copy a regular file, creating parent directories as needed.

    cstring

    fn cstring(s : String) -> Bytes

    NUL-terminated byte view for C entry points.

    default_toolchain

    fn default_toolchain() -> String? raise MbError

    dir_exists

    fn dir_exists(path : String) -> Bool

    discover_binaries

    fn discover_binaries(src : String) -> Array[(String, String)] raise MbError

    Discover native executables produced by moon build under src.

    MoonBit writes native binaries as <name>.exe; we additionally verify the file magic so non-executable artefacts are ignored.

    download

    fn download(url : String, dest : String, progress : Bool) -> Unit raise MbError

    Download url to dest, following redirects and retrying transient failures. Shows a progress bar for large downloads on a terminal.

    effective_toolchain

    fn effective_toolchain() -> String? raise MbError

    Pick the toolchain to use when no explicit version is requested.

    ensure_shims

    fn ensure_shims(version : String) -> Unit raise MbError

    Create or refresh ~/.local/bin symlinks for the default toolchain's binaries.

    ensure_tools

    fn ensure_tools() -> Unit raise MbError

    Verify the external programs mb relies on are present.

    env_or

    fn env_or(name : String, default : String) -> String

    Read an environment variable, returning default when unset or empty.

    env_prefix

    fn env_prefix(tc : ToolchainEnv) -> String

    Shell prefix that points a command at tc's environment. Empty for the system toolchain.

    exit

    fn exit(code : Int) -> Unit

    Terminate the process with code.

    extract_tar_gz

    fn extract_tar_gz(archive : String, dest : String) -> Unit raise MbError

    Extract a .tar.gz archive on disk into dest using the system tar.

    extract_zip

    fn extract_zip(zip_bytes : Bytes, dest : String) -> Unit raise MbError

    Extract a zip archive from memory into dest.

    Entry paths are sanitised to prevent .. escapes from writing outside dest. Symlink entries are ignored: only regular files and directories are materialised.

    fail

    fn fail(msg : String) -> Unit raise MbError

    Abort the current operation with a human-readable message.

    fetch_bytes

    fn fetch_bytes(url : String) -> Bytes raise MbError

    Fetch url and return the response body as bytes.

    fetch_json

    fn fetch_json(url : String) -> Json raise MbError

    Fetch and parse a JSON document.

    fetch_manifest

    fn fetch_manifest(name : String, version : String?) -> Json raise MbError

    Fetch the registry manifest for a module, optionally pinned to a version.

    fetch_text

    fn fetch_text(url : String) -> String raise MbError

    Fetch url and return the response body as text.

    file_exists

    fn file_exists(path : String) -> Bool

    find_installed_toolchain

    fn find_installed_toolchain(version : String) -> String? raise MbError

    Find an installed toolchain matching a user-supplied version. Exact matches win; a bare compiler version also matches its full +hash form.

    home_dir

    fn home_dir() -> String raise MbError

    installed_toolchains

    fn installed_toolchains() -> Array[String] raise MbError

    Names of installed toolchains.

    installed_tools

    fn installed_tools() -> Array[ToolMeta] raise MbError

    Read the metadata of every installed tool version.

    is_executable_file

    fn is_executable_file(path : String) -> Bool

    True when the file is a native executable (ELF or Mach-O), as opposed to a relocatable object file which shares the same leading magic.

    is_tty

    fn is_tty() -> Bool

    json_array

    fn json_array(obj : Json, key : String) -> Array[Json]?

    json_bool

    fn json_bool(obj : Json, key : String) -> Bool?

    json_field

    fn json_field(obj : Json, key : String) -> Json?

    json_object

    fn json_object(obj : Json, key : String) -> Json?

    json_string

    fn json_string(obj : Json, key : String) -> String?

    latest_moon_cli_version

    fn latest_moon_cli_version() -> String? raise MbError

    Latest published moon version (e.g. 0.1.20260920).

    latest_moonc_version

    fn latest_moonc_version() -> String? raise MbError

    Latest published moonc version including its build hash (e.g. 0.10.14+7d59c7ec9).

    latest_tool_version

    fn latest_tool_version(name : String) -> String raise MbError

    Latest published version of a module.

    list_dir

    fn list_dir(path : String) -> Array[String] raise MbError

    Like list_dir_unsafe but reports failures.

    list_dir_unsafe

    fn list_dir_unsafe(path : String) -> Array[String]

    Names (without the directory prefix) of entries in path. Returns an empty array when the directory cannot be read.

    load_dotenv

    fn load_dotenv() -> Unit raise MbError

    Load the dotenv files that apply to the current project and export their values into the process environment, so child processes inherit them.

    Files are read in this order, later ones overriding earlier ones: $HOME/.mbenv, ./.mbenv, ./.env. Values written as ENC[...] are decrypted with the key from MB_ENV_KEY or $HOME/.mbenv.

    mb_home

    fn mb_home() -> String raise MbError

    Root of mb's own data directory (installed toolchains and tools).

    Resolution order: MB_HOME, then $XDG_DATA_HOME/mb, then ~/.local/share/mb.

    mkdir_p

    fn mkdir_p(path : String) -> Unit raise MbError

    mkdir -p for a single path.

    moon_install

    fn moon_install(arg : String, force : Bool) -> Unit raise MbError

    Download and install a MoonBit toolchain.

    moon_list

    fn moon_list() -> Unit raise MbError

    The mb moon list command: installed toolchains, then the versions published on the release channel (version.json, i.e. what latest resolves to).

    moon_root

    fn moon_root() -> String raise MbError

    Root directory holding all installed MoonBit toolchains.

    moon_set_default

    fn moon_set_default(version : String) -> Unit raise MbError

    The mb moon default <version> command.

    moon_uninstall

    fn moon_uninstall(version : String) -> Unit raise MbError

    The mb moon uninstall <version> command.

    moon_which

    fn moon_which(version : String) -> Unit raise MbError

    The mb moon which [version] command.

    parent_dir

    fn parent_dir(path : String) -> String

    parse_version_items

    fn parse_version_items(j : Json) -> Array[(String, String)]

    Parse the version.json published at the toolchain root into (name, version) pairs, stripping the descriptive suffix from each version string.

    path_exists

    fn path_exists(path : String) -> Bool

    path_join

    fn path_join(a : String, b : String) -> String

    path_join_all

    fn path_join_all(parts : Array[String]) -> String

    Join any number of path segments with /.

    platform

    fn platform() -> String raise MbError

    The platform triple used to select MoonBit toolchain archives.

    read_bytes

    fn read_bytes(path : String) -> Bytes raise MbError

    read_text

    fn read_text(path : String) -> String raise MbError

    fn readlink(path : String) -> String?

    Target of a symlink, or None when path is not a symlink. Works for dangling links too, which is exactly the case that needs cleaning up.

    remove_shims

    fn remove_shims(version : String) -> Unit raise MbError

    Remove the ~/.local/bin symlinks pointing into version's toolchain.

    Match by symlink target rather than by the recorded default, so an uninstalled version never leaves dangling moon/moonc/... links behind.

    resolve_moon_spec

    fn resolve_moon_spec(arg : String) -> MoonSpec raise MbError

    Turn a user-supplied version into a concrete download key and store name.

    • latest / nightly map to those aliases; latest is stored under the resolved CLI version so repeated installs are idempotent.
    • A full moonc version such as 0.10.14+7d59c7ec9 downloads directly.
    • A bare compiler version such as 0.10.14 is upgraded to the matching full version when it corresponds to the current release.

    resolve_toolchain

    fn resolve_toolchain(explicit : String) -> ToolchainEnv raise MbError

    Resolve the toolchain to use for building/installing tools. When explicit is empty this prefers the managed default, then a system install, and only then installs the latest release.

    rm_rf

    fn rm_rf(path : String) -> Unit raise MbError

    rm -rf. Delegates to the system tool so symlinks, read-only entries and large trees are all handled correctly.

    run_capture

    fn run_capture(cmd : String) -> String

    Run a shell command and capture its combined output.

    run_checked

    fn run_checked(cmd : String) -> Unit raise MbError

    Run a shell command, raising when it exits non-zero.

    run_cli

    fn run_cli(argv : Array[String]) -> Int raise MbError

    Entry point used by cmd/mb. Returns the process exit code.

    run_ok

    fn run_ok(cmd : String) -> Bool

    Run a shell command, returning whether it succeeded.

    run_status

    fn run_status(cmd : String) -> Int

    Run a shell command, inheriting stdio. Returns the exit code.

    set_default_toolchain

    fn set_default_toolchain(version : String) -> Unit raise MbError

    sha256_hex

    fn sha256_hex(data : Bytes) -> String

    SHA-256 of data as a lowercase hex string.

    shell_quote

    fn shell_quote(s : String) -> String

    Single-quote a string for safe interpolation into a /bin/sh command.

    shim_points_into

    fn shim_points_into(target : String, dir : String) -> Bool

    Whether a shim symlink target lives inside dir's bin directory.

    sync_default_shims

    fn sync_default_shims() -> Unit raise MbError

    Point the ~/.local/bin symlinks at the currently-selected toolchain.

    system_toolchain

    fn system_toolchain() -> ToolchainEnv?

    A system toolchain discovered on PATH.

    temp_path

    fn temp_path() -> String raise MbError

    Create a unique temporary file path and return it.

    tool_bin_dir

    fn tool_bin_dir(meta : ToolMeta) -> String raise MbError

    Where a tool's executables live: its recorded install directory, or the current default for records written before that was tracked.

    tool_dir

    fn tool_dir(name : String, version : String) -> String raise MbError

    Directory for one tool version.

    tool_install

    fn tool_install(spec : String, toolchain_override : String, bin_override : String, force : Bool) -> Unit raise MbError

    Install one tool from a <author>/<module>[@<version>] spec.

    tool_list

    fn tool_list(outdated : Bool) -> Unit raise MbError

    The mb tool list command. With outdated, consult mooncakes.io and mark tools that have a newer version available.

    tool_root

    fn tool_root() -> String raise MbError

    Root directory holding all installed tools.

    tool_uninstall

    fn tool_uninstall(spec : String) -> Unit raise MbError

    The mb tool uninstall <module> command.

    tool_uninstall_many

    fn tool_uninstall_many(specs : Array[String], all : Bool) -> Unit raise MbError

    The mb tool uninstall command: remove the named tools, or every installed tool with all.

    tool_upgrade

    fn tool_upgrade(names : Array[String], all : Bool, toolchain_override : String) -> Unit raise MbError

    The mb tool upgrade command. With all, upgrade every installed tool; otherwise upgrade the named tools (any version specifier is ignored and the latest version is installed). Each tool is rebuilt into the directory it was originally installed into.

    toolchain_complete

    fn toolchain_complete(version : String) -> Bool raise MbError

    Whether a toolchain directory is fully installed (binary plus bundled core).

    toolchain_dir

    fn toolchain_dir(version : String) -> String raise MbError

    Directory of one installed toolchain.

    walk_files

    fn walk_files(dir : String) -> Array[String] raise MbError

    Recursively collect regular files below dir (absolute or relative to dir), returned with dir-relative prefixes.

    write_bytes

    fn write_bytes(path : String, data : Bytes) -> Unit raise MbError

    write_text

    fn write_text(path : String, text : String) -> Unit raise MbError