From 5375e7599c86fe46d1e23518bd51995c1c75ae26 Mon Sep 17 00:00:00 2001 From: qiyinxi Date: Mon, 2 Nov 2026 15:53:26 +0100 Subject: [PATCH] =?UTF-8?q?README=EF=BC=9A=E5=8A=A0=E4=B8=8A=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E7=9A=84=E8=8B=B1=E6=96=87=E7=89=88=20README.en.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 与中文 README.md 逐节对应,互相放了语言切换的链接;参与一节写明约定:改一份就在同一个 PR 里改另一份。 --- README.en.md | 235 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 5 ++ 2 files changed, 240 insertions(+) create mode 100644 README.en.md diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..a2037b7 --- /dev/null +++ b/README.en.md @@ -0,0 +1,235 @@ +# ModAVDManager + +[简体中文](README.md) | **English** + + + +The management program for [ModAVD](https://github.com/ModAVD/ModAVD): the graphical manager `ModAVD.exe`, and the CLI and MCP server `modavd.exe`. Written in Go, standard library only. It currently supports Windows x64 only; the platform-specific parts are already split into a platform layer. For the plans for other platforms, see [ModAVD#27](https://github.com/ModAVD/ModAVD/issues/27). + +> **Version note (`main` and Releases)**: This document describes the implementation on the repository's `main`. `main` is currently at version `0.3.0`, the latest standalone Release is `v0.1.0` (matching ModAVD `0.36.0`), and `v0.2.0` has been drafted but not published. The background initialization on create, the `update` command and exit code 9, all new in `0.2.0`, are not in the `v0.1.0` download package. If you use a published package, go by the notes of the matching Release and the version inside the package. + +Usage instructions and guidance for contributing are on the ModAVD documentation site: [The manager and the CLI](https://modavd.github.io/en/components/manager), [Development: the manager and the CLI](https://modavd.github.io/en/develop/manager). + +- [What this is](#what-this-is) +- [How it relates to ModAVD](#how-it-relates-to-modavd) +- [Command line](#command-line) +- [MCP](#mcp) +- [The manager ModAVD.exe](#the-manager-modavdexe) +- [Building](#building) +- [Testing](#testing) +- [Contributing](#contributing) +- [License](#license) + +## What this is + +- ModAVD is an Android emulator based on the AOSP Android Emulator plus a set of patches. It supports Windows today, and more platforms are planned. This repository holds the programs that manage it: create, change and delete instances; start and shut down; the computer check before starting; install APKs and the arm64 execution bridge; view the screen and the logs. +- It manages one ModAVD root folder, that is, the folder the ModAVD release package unpacks to (it contains `mas-avd.json`, `sdk\` and `avd\`). The instance files and `mas-avd.json` are written the same way as in [AUTO-MAS](https://github.com/AUTO-MAS-Project/AUTO-MAS), and the tests compare against AUTO-MAS code byte for byte. +- The manager `ModAVD.exe` is for people; the CLI `modavd.exe` and its MCP server are for scripts, AUTO-MAS and AI assistants. + +## How it relates to ModAVD + +- This public repository is the basis for developing and releasing the manager and the CLI. Its version number is maintained independently, and it has its own GitHub Releases. +- The ModAVD package still ships `ModAVD.exe` and `tools\modavd.exe`; each ModAVD release pins the manager version and hash it uses. +- The standalone download works from any folder: pass `--root` or set `MODAVD_ROOT` to point it at the ModAVD root folder, and check the download against the hashes below. Do not unpack it into the ModAVD root folder over the files from the package. + +| Artifact | Source | What it does | +| --- | --- | --- | +| `\tools\modavd.exe` | `main.go`, `internal/cli` | The CLI; `modavd mcp` is the MCP server | +| `\ModAVD.exe` | `cmd/modavd-gui`, `internal/ui` | The graphical manager (local server + Edge app window) | + +Both programs share the implementation under `internal/`: + +| Package | Contents | AUTO-MAS code compared against | +| --- | --- | --- | +| `root` | Root folder layout, `mas-avd.json`, port rules, component checks | `constants.py`, `components.py` | +| `avd` | Adding, changing and removing instance files (`mas_.ini`, `config.ini`) | `instances.py` | +| `jsonord` | Order-preserving JSON reading and writing, in the same format as Python's `json.dumps(indent=2, ensure_ascii=False)` | — | +| `emu` | Start, post-boot tuning, first-boot initialization, execution bridge, shutdown, status, computer check | `manager.py`, `precheck.py` | +| `adb` | Private adb server, re-registration, emulator console | `host.py` | +| `win` | Processes, command lines, ports, launching detached from the process tree, file locks | `host.py`, `windows/process.py` | +| `vkfix`, `apk`, `guest` | Guest Vulkan driver patch, APK manifest, scripts pushed into the guest | `vulkanfix.py`, `res/avd/guest/` | +| `cli`, `mcp`, `ui` | The CLI, MCP, and the manager's HTTP server and pages | — | + +`internal/guest/scripts/*.sh` and the scripts under AUTO-MAS `res/avd/guest/` are the same files; change both together. + +## Command line + +``` +modavd [--root ] [--json] [--quiet] [--verbose] [args] +``` + +| Command | Description | +| --- | --- | +| `list` | All instances and their status | +| `info ` / `status ` | Details of one instance / its status | +| `create [--index N] [--title T] [--memory 6G] [--cpu N] [--data-gb N] [--fps 30\|60] [--no-bridge] [--timeout 1200]`, `create --resume N` | Create an instance, then start it once in the background, headless, to finish initialization, and shut it down (see below). The arm64 execution bridge is included by default; `--no-bridge` records that this instance does not want the bridge, so it is not installed on later starts either. `--resume N` redoes the initialization of an instance whose creation failed | +| `set [--title T] [--memory M] [--cpu N] [--fps F] [--data-gb N]` | Change settings; they take effect at the next start. `--data-gb` can only be increased, and game data is untouched: the emulator (from 0.36) grows the disk image at start and grows the file system online after boot. A disk created before 0.36 at 16 GB or smaller can only be grown to just below the next backup group (a 16 GB disk to about 30 GB) | +| `delete --yes` | Delete a stopped instance, together with its game data | +| `start [--window] [--timeout 300] [--no-bridge]` | Start, wait until boot finishes and the post-boot setup is done, and before returning confirm that files can be written to `/sdcard` (normally it is ready long before this; it waits up to 60 more seconds and only warns at the deadline). If the instance has no execution bridge yet, install it afterward; `--no-bridge` skips that this time | +| `stop [--force]`, `stop --all` | Shut down: first ask Android for a clean shutdown, and if that fails, sync + kill; `--force` is sync + force-kill | +| `restart [--window] [--timeout 300] [--force] [--no-bridge]` | Shut down, then start; options as for `stop` and `start` | +| `bridge install\|uninstall\|verify` | Install, uninstall or check the arm64 execution bridge; it is installed automatically at start, so you normally do not run it by hand (see below) | +| `check [--memory M]` | Computer check before starting | +| `report-config [--no-clipboard]` | Copy the computer configuration and the game info from the running instances (read-only) | +| `collect-info [--out-dir D] [--stage-dir D] [--keep-stage] [--no-explorer]` | Collect a problem report and zip it (read-only) | +| `components [status]`, `components fetch [--accept-license] [--mirror ID] [--mirror-url URL] [--keep-downloads]` | View or fetch the Google components (adb and the system image); does what `补全组件.bat` (fetches the Google components) does (see "Fetching the Google components") | +| `window show\|hide`, `install `, `shell `, `adb -- ` | Window, install an APK, guest command, adb passthrough | +| `update [status\|check\|prepare\|install\|recover] [--automas-version V]` | Update the whole ModAVD package in the root folder, using the update source from "Settings" (`设置`) (see "Updating ModAVD") | +| `servers`, `version`, `mcp`, `help [command]` | | + +- Write `` as `3` or `mas_3` (0-9, except 5). Memory is written as `6144`, `6144M` or `6G`, with only four tiers: 3 / 4 / 5 / 6 GB. Cores are 2 / 4 / 6, and the frame rate is 30 / 60. +- Root folder: `--root`; if not given, the environment variable `MODAVD_ROOT`; otherwise the folder containing `modavd.exe`, or its parent. + +**The arm64 execution bridge**: + +- It applies only to the games on the built-in list; other apps keep using the original translation layer. The bridge is installed in the instance's own system / vendor overlay and needs an emulator from 0.36 on. +- Every time modavd (the CLI, MCP) and the manager start an instance, they check: if the instance has no bridge yet, they install it after the post-boot setup is done, with one or two shutdowns and cold boots in between. This happens only when they started the emulator this time; when taking over an instance that is already running, nothing is installed. If installation fails it is only a warning, the instance keeps running, and it is tried again at the next start. +- To keep an instance without the bridge, pass `--no-bridge` on create, or run `bridge uninstall` later. Both record `instances..bridge` as `false` in `mas-avd.json`, so later starts no longer install it automatically; `bridge install` again brings it back. The `--no-bridge` of `start` / `restart` applies to that one run only. +- `bridge` calls `tools\modavd-bridge.ps1` in the ModAVD package. `install`: shuts down and starts again if needed, and installs; if it is already installed, it updates only when the payload has changed. `uninstall`: removes the bridge, the boot marker, and the GL transport mode (asg) that was set when the bridge was installed; uninstall before going back to an earlier version. If the instance was off before, it is shut down again afterward. `verify`: a read-only check; the instance must be running. +- The `bridge` field of `info` / `list` / `status` is `off` (marked as not wanting the bridge), `pending` (will be installed at the next start that modavd launches), `installed` (the bridge is present in the running guest) or `unknown`. It only queries a guest that has finished booting. For an instance that is off or still booting, the boot marker stays once the bridge is installed and is also there when installation failed, so when first-boot initialization has been done and the marker is present, the value is `unknown`; to know the exact result, check after the instance has started. +- While the bridge is being installed (including the shutdowns and cold boots in it), `busy` is `bridging`; during this time `delete` and another `bridge` return instance busy (exit code 5). Callers should treat any `busy` value they do not recognize as busy. +- An instance that was not started through modavd or the manager (another program launches the emulator directly) does not get the bridge automatically; to install it, run `bridge install` once. + +**Machine-readable output** (`--json`): + +- stdout has exactly one line: `{"ok":true,"data":…}` or `{"ok":false,"error":{"code","message","advice"}}`. The fields of `data` are in `internal/emu/types.go`, in snake_case, and all fields are always present. +- Progress logs go to stderr, one JSON object per line: `{"time","level","msg"}`. Callers must not merge stderr into stdout. +- `protocol` in `version --json` is the contract version; it is incremented only when a field changes meaning or is removed. +- Running instances gain `started_at_ms` (Unix milliseconds); the original `started_at` stays. `stop --all` returns `ok:true` and the result list even if some instances failed, and exits with the exit code of the first failure. +- **Create (from 0.2.0)**: after creating the files, `create` starts the instance once in the background, headless, and finishes the post-boot setup, the host IME, first-boot initialization and the execution bridge (with one or two cold boots in between). Then it syncs first, shuts down cleanly and confirms that qemu has exited; only then is creation complete. This usually takes a few minutes (`--timeout` covers the whole process, default 1200 seconds; at the deadline it shuts down cleanly first and then records a failure). After that, the first `start` is as fast as usual. Instances created before 0.2.0 are unaffected and still finish this at their first `start`. + - During this, `status` in `status` / `list` / `info` is `creating` and `busy` is `creating`; `start`, `set`, `bridge`, `delete` and a `create` with the same index return instance busy (exit code 5). + - A failure (including the create process being killed) is `create_failed`; `creation_error` gives the reason and `creation_log` is the log of that boot. After that, `start`, `set` and `bridge` return `not_ready` (exit code 4). Use `create --resume N` to redo the initialization, or `delete N --yes` to delete the instance. + - The new fields `creation` (`""` / `creating` / `failed`), `creation_error` and `creation_log` are always present. The state is kept in keys such as `instances..creationState` in `mas-avd.json` and removed when creation is done. Callers (AUTO-MAS) should treat any `status` value they do not recognize as busy. + - Progress: the CLI prints each step to stderr; MCP's `create_instance` sends progress notifications (when a progressToken is given); the manager shows a progress bar on the card, and closing the window lets it finish in the background. + +**Exit codes**: 0 success, 1 internal error, 2 bad usage or arguments / license agreement not accepted, 3 instance does not exist, 4 root folder not ready / check failed / not supported, 5 instance busy / already exists / port in use, 6 timeout, 7 start failed, 8 canceled, 9 cannot check or download during an update or component fetch / the download failed verification. Without `--json`, `shell` returns the guest command's exit code and `adb` returns adb's exit code; for `bridge` without `--json`, it is 1 if the bridge is still not installed after `install` or still present after `uninstall`, and `verify` returns the script's exit code. + +**Conventions** (the same as AUTO-MAS): + +- adb only uses the root folder's private server (`adbServerPort` in `mas-avd.json`, default 20050) and never touches 5037. +- The emulator, the adb server and logcat are all launched through an intermediate process that exits immediately, so they are not in modavd's process tree: if the caller ends modavd (Ctrl+C, the MCP client disconnects), the emulator is not taken down with it. +- After a successful start, each instance also gets a boot guardian that is likewise outside the process tree and exits when qemu exits. After the guest restarts on its own (qemu keeps running), the guardian redoes the post-boot tuning and the Vulkan patch, and never launches a new emulator. +- Starts and shutdowns of the same instance queue on `\avd\.mas_.{start,boot,stop}.lock`: a start waits for the previous start and shutdown to finish; a shutdown can interrupt an instance that is starting. + +## MCP + +``` +modavd mcp --root D:\ModAVD +``` + +It supports both MCP 2026-07-28 (stateless, `server/discover`) and 2025-11-25 / 2025-06-18 (`initialize` handshake). The root folder is fixed at startup and is not a tool parameter. Tools: `list_instances`, `get_instance`, `check_host`, `create_instance`, `update_instance`, `delete_instance`, `start_instance` (with progress), `stop_instance`, `set_window_visible`, `shell`, `install_apk`. + +To register it in Claude Code (in PowerShell, `--` needs quotes, or it gets swallowed): + +```powershell +claude mcp add --transport stdio --scope user modavd '--' D:\ModAVD\tools\modavd.exe mcp --root D:\ModAVD +``` + +Or in the project's `.mcp.json`: + +```json +{"mcpServers": {"modavd": {"command": "D:\\ModAVD\\tools\\modavd.exe", "args": ["mcp", "--root", "D:\\ModAVD"]}}} +``` + +## The manager ModAVD.exe + +Double-click `ModAVD.exe` in the package root: it starts a local server that listens on 127.0.0.1 only, then opens a window in Edge's app mode (`msedge --app`, built into Win10 / 11); if Edge is not found, it uses the default browser. The pages are HTML / CSS / JS embedded in the exe and need no network; it goes online only when you click "Check for updates" (`检查更新`) in "Settings" (`设置`). + +- Features: instance cards (with a screen thumbnail when the instance is online), start and shut down (with progress), create, settings, delete, computer check, activity panel, screen viewer (a click is a tap; Back / Home / Recent tasks), logs, install APK, open folder; light / dark / follow system. +- Security: each launch uses a random token, which is exchanged for an HttpOnly cookie (its name includes the port, so managers for two root folders running at the same time do not overwrite each other); it validates Host (against DNS rebinding) and Origin; strict CSP. +- One root folder gets one server; double-clicking again just reopens the window. The state file is in `%LOCALAPPDATA%\ModAVD\`, and the log is at `\logs\modavd-ui.log`. +- After the window is closed, it waits for the running tasks to finish before exiting. +- Options: `--root`, `--port`, `--no-window` (start only the server), `--dev` (exposes a fake backend under `/dev/`, for adjusting page styles). +- The front end / back end API is in `internal/ui/API.md` (in Chinese). The pages are in `internal/ui/web/`; the icons are fan art (see `internal/ui/web/assets/README.md` and [NOTICE](NOTICE)) and are not licensed under the code license. + +## Updating ModAVD + +`modavd update` updates the whole ModAVD package in the root folder (the emulator, components and scripts, and also `ModAVD.exe` and `tools\modavd.exe` themselves). It does the same thing as AUTO-MAS's "ModAVD one-click update" (`魔改 AVD 一键更新`), and uses the same `\.update\` folder and state file: if an update is interrupted partway on either side, the other can finish it or roll it back. Under the current integration agreement, AUTO-MAS keeps its own updater for now; whether and when it switches to calling this command is up to the AUTO-MAS project's own maintenance, and this is not a feature or promise already delivered by this repository. + +- It does only the step you explicitly ask for; it never goes online automatically and never downloads in the background: + - `modavd update` (`status`) looks at local state only; `check` looks up the latest version; + - `prepare` downloads, unpacks, verifies file by file against the `modavd-manifest.json` in the package, and stages the result; instances can be running; + - `install` runs `prepare` first if needed, then swaps in `sdk\emulator` and the other files in the package. All instances, manager windows and MCP clients must be closed first; if any program is using the files, it changes nothing (exit code 5), and the staged new version is kept so the next run can swap it in directly; + - `recover` finishes or rolls back a swap that was left unfinished last time (it also runs first at the start of every step). +- The update source is chosen in the manager's "Settings" (`设置`): GitHub (the latest Release of `ModAVD/ModAVD`, the full package) or MirrorChyan (downloads need a CDK, and it can provide delta packages; on errors it downloads from GitHub instead and says so in the result's `notices`). The CDK is encrypted with Windows DPAPI and stored in `%LOCALAPPDATA%\ModAVD\update-settings.json`; only this Windows user can decrypt it, and it is not written to logs and does not appear on the command line or in API responses. For now the manager can only change the settings and check for updates; the "Update" (`更新`) button is not available. +- Only files from the package are swapped: `avd\`, `mas-avd.json` (only `components.emulator` is changed and an `updateHistory` entry is recorded), `sdk\platform-tools`, `sdk\system-images`, `logs\` and so on are never touched; `sdk\emulator` is replaced as a whole. A root folder that sets `sdkRoot` in `mas-avd.json` is not updated. +- `--automas-version` is the version of the calling AUTO-MAS: if the first line of the new version's announcement requires a higher AUTO-MAS, it does not install. +- Exit codes: being unable to check (offline, rate-limited, a GitHub error) is 9 (`update_unavailable`), a downloaded package that fails verification is also 9 (`update_rejected`), and a program in use or an update already in progress is 5. Go does not read the Windows system proxy; it only honors `HTTPS_PROXY` / `NO_PROXY`. + +## Fetching the Google components + +The ModAVD release package does not include adb (platform-tools 37.0.1) or the Android 14 Google APIs x86_64 system image (r14, about 1.5 GB, about 4.2 GB unpacked): they belong to Google, are governed by the Android Software Development Kit License Agreement, and cannot be redistributed. `modavd components fetch` downloads them from official channels and installs them into `sdk\`; its rules match the bundled `fetch-components.ps1` (`补全组件.bat`) one for one: + +- First it fetches the full license text from `repository2-3.xml` on a mirror and displays it (stating which mirror it came from and which components will be downloaded), and continues only if you type `y`; otherwise nothing is downloaded and `downloads\` gets no extra files. `--accept-license` skips the prompt, but the full text is still fetched and displayed, and if it cannot be fetched it still exits; with `--json` it is required, otherwise the command reports `usage` before going online. The license text and the prompt are not affected by `--quiet`. +- Mirrors are tried in this order: googledownloads.cn → Tencent Cloud mirror → dl.google.com. `--mirror` uses only one of them (case-insensitive); `--mirror-url` uses your own directory URL in place of those three (it can be given several times or comma-separated, and the URLs are tried in order). A mirror from which the license manifest could not be fetched is moved to the end when downloading. +- It first downloads to `downloads\*.part`; if interrupted, running it again resumes (`Range`), and files that are already complete are just verified. The server's response is checked before the `.part` file is touched: a 200 must match the expected size, and a 206 must continue exactly from the existing offset. The file is renamed only after the size and SHA-1 are verified. Requests carry `Accept-Encoding: identity`, and any compressed response from the server is rejected. HTTP 4xx, a size mismatch, compressed content or a timeout switches to the next mirror immediately (the `.part` is kept so the next mirror can continue it); other errors are retried up to 3 times on the same mirror, waiting 3 seconds between tries. +- It unpacks to `sdk\.staging\`, and moves the result into place only after the whole package is unpacked and verified (key files, the SHA-256 pinned for the three adb files, the version); a missing `source.properties` is written in. A directory that was previously incomplete is renamed `.incomplete-