Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
# Installation
|
||||
|
||||
NomiFun has two host modes that share one Rust backend (see
|
||||
[Introduction](introduction.md)). This page covers all three ways to install
|
||||
it today:
|
||||
|
||||
- [Desktop app from source](#desktop-app-from-source) — `nomifun-desktop`
|
||||
(Tauri shell), desktop local-trust, single-user.
|
||||
- [Web server from source](#web-server-from-source) — `nomifun-web`,
|
||||
authenticated, self-hosted.
|
||||
- [Docker / Docker Compose](#docker--docker-compose) — the same web server,
|
||||
containerised.
|
||||
|
||||
> **Official pre-built installers are not yet published.** Desktop bundles,
|
||||
> macOS signing, updater artifacts, Docker, and native Linux service files can
|
||||
> be built locally; there is not yet an official public release channel. Until
|
||||
> then, every install path below builds from source. See
|
||||
> [`../contributing/building-and-packaging.md`](../contributing/building-and-packaging.md)
|
||||
> for the current packaging notes.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need a working build toolchain regardless of which mode you target. The
|
||||
exact requirements:
|
||||
|
||||
| Tool | Minimum | Why | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| **Rust** | stable, edition 2024 | Compile the backend (and the Tauri shell, for desktop). | Install via [`rustup`](https://rustup.rs/). The workspace pins `edition = "2024"` and `resolver = "3"`. |
|
||||
| **Bun** | **≥ 1.3.13** | Frontend package manager + build (and a hard runtime dependency of the agent engine). | `1.1.38` has a stdin bug — do not use it. |
|
||||
| **Tauri CLI** | v2 | Build the desktop shell. | Pulled in as a `devDependency`; no global install needed. |
|
||||
| **Git** | any recent | Clone, plus skill discovery and some built-in tools. | |
|
||||
| **C/C++ build tools** | platform-specific | `rusqlite` (bundled), `aws-lc-rs`, `libgit2-sys`. | Windows: MSVC + WebView2 runtime. macOS: Xcode CLT. Linux: `build-essential cmake clang pkg-config perl`. |
|
||||
|
||||
Optional but recommended on the host that runs Nomi (not for building):
|
||||
|
||||
- **`ripgrep`** — code-search backend; falls back to `grep` if missing.
|
||||
- **`node` / `npm` / `npx`** — many user-installed MCP stdio servers launch
|
||||
via `npx -y …`.
|
||||
|
||||
### Clone the repo
|
||||
|
||||
```bash
|
||||
git clone <your-fork-or-mirror>/nomifun-tauri.git
|
||||
cd nomifun-tauri
|
||||
```
|
||||
|
||||
The rest of this page assumes the repository root is your working directory.
|
||||
|
||||
### Install JS dependencies
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
This populates `node_modules/` for the workspace and `ui/`. Re-run it any
|
||||
time `package.json` or `ui/package.json` changes.
|
||||
|
||||
## Desktop app from source
|
||||
|
||||
The desktop app is a Tauri 2 shell (`apps/desktop`, binary
|
||||
`nomifun-desktop`) that links the backend in-process and starts it on a free
|
||||
localhost port under the desktop `TrustLocalToken` policy. Its own WebView
|
||||
receives a per-boot local trust secret, so there is no login screen in the
|
||||
desktop window.
|
||||
|
||||
### Run in development
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
What this does, end-to-end:
|
||||
|
||||
1. Tauri's `beforeDevCommand` runs `bun run --filter=./ui dev` to start the
|
||||
Vite dev server on `http://localhost:5173`.
|
||||
2. `cargo` compiles `nomifun-desktop` (and the workspace it depends on).
|
||||
3. The shell starts, picks a free port, spawns the embedded backend, and
|
||||
loads the Vite dev URL. Hot-reload works on the renderer side; the backend
|
||||
restarts only when its Rust code changes.
|
||||
|
||||
You will see a tracing line like `Server listening on 127.0.0.1:54760` in the
|
||||
console — that is the embedded backend. The renderer reads
|
||||
`window.__backendPort` (injected by the Tauri shell as an init script) so the
|
||||
SPA always knows where to call `/api`.
|
||||
|
||||

|
||||
|
||||
### Build a release binary
|
||||
|
||||
```bash
|
||||
bun run build:ui # build the SPA into ui/dist
|
||||
bun run build # tauri build → installers + standalone binary
|
||||
```
|
||||
|
||||
`tauri build` produces:
|
||||
|
||||
- A standalone executable under
|
||||
`target/release/nomifun-desktop` (`.exe` on Windows).
|
||||
- Platform installers under `target/release/bundle/` — `.msi`/`.exe`
|
||||
(Windows), `.dmg`/`.app` (macOS), `.deb`/`.AppImage` (Linux).
|
||||
|
||||
`bun run build` artifacts are suitable for local testing. For distributable
|
||||
macOS builds, configure `apps/desktop/signing/.env.signing` and use
|
||||
`bun run build:signed`. Windows signing still requires an external certificate.
|
||||
To test the updater scaffold, use `bun run build:updater`, which sets
|
||||
`bundle.createUpdaterArtifacts` to true. The updater endpoint and public key in
|
||||
`apps/desktop/tauri.conf.json` must be replaced before shipping any update.
|
||||
|
||||
### Where data lives (desktop)
|
||||
|
||||
The desktop app stores its database and runtime files under the per-user
|
||||
application-data directory, joined with `Nomi`:
|
||||
|
||||
| OS | Default path |
|
||||
| --- | --- |
|
||||
| Windows | `%LOCALAPPDATA%\NomiFun\Nomi` (e.g. `C:\Users\<you>\AppData\Local\NomiFun\Nomi`) |
|
||||
| macOS | `~/Library/Application Support/NomiFun/Nomi` |
|
||||
| Linux | `$XDG_DATA_HOME/NomiFun/Nomi` (usually `~/.local/share/NomiFun/Nomi`) |
|
||||
|
||||
Override with `NOMIFUN_DATA_DIR=<absolute path>` before launching — the
|
||||
shell appends `/Nomi`, so the dir becomes `$NOMIFUN_DATA_DIR/Nomi`.
|
||||
|
||||
> Older builds defaulted to `<system temp>/nomifun-data/Nomi`, where OS temp
|
||||
> cleanup could destroy user data. On first launch the app now relocates such
|
||||
> a legacy install to the per-user location automatically (one-shot): data is
|
||||
> copied, absolute paths inside the database are rewritten, and the old
|
||||
> directory is kept as a backup. If the relocation cannot complete, the app
|
||||
> starts from the legacy directory and retries on the next launch.
|
||||
|
||||
> Note: the app's user-facing name is `NomiFun` everywhere — the bundle
|
||||
> product name (`apps/desktop/tauri.conf.json`), the runtime window title,
|
||||
> and release artifacts. The data folder keeps its existing `/Nomi`
|
||||
> suffix for compatibility with current installs. Internal identifiers keep the legacy `nomifun`
|
||||
> name by design (crates, `NOMIFUN_*` env vars, the `com.nomifun.*`
|
||||
> bundle identifier).
|
||||
|
||||
## Web server from source
|
||||
|
||||
`nomifun-web` is an axum server that mounts the same backend in-process
|
||||
**and** serves the built SPA on the same port (default `8787`). It is the
|
||||
right path for self-hosting on a LAN, VPN, or VPS.
|
||||
|
||||
### Build and run
|
||||
|
||||
```bash
|
||||
bun install
|
||||
bun run build:ui # ui/dist — required before serving in non-dev mode
|
||||
bun run serve:web # = cargo run -p nomifun-web
|
||||
```
|
||||
|
||||
By default the server binds `127.0.0.1:8787` and uses the same per-user
|
||||
data directory as the desktop app (see
|
||||
[Where data lives (desktop)](#where-data-lives-desktop)):
|
||||
|
||||
```text
|
||||
nomifun-web: embedded backend + SPA on one port
|
||||
listening on 127.0.0.1:8787 auth=required dist=../../ui/dist
|
||||
```
|
||||
|
||||
On a machine that also has the desktop app installed, a bare `nomifun-web`
|
||||
run opens the desktop app's data directly — an exclusive `server.lock`
|
||||
guarantees the two backends never run on that directory at the same time.
|
||||
|
||||
Open `http://127.0.0.1:8787` in a browser. On the very first visit you will
|
||||
be sent to a setup screen — the username and password you type **become the
|
||||
initial admin account**. After that, login is required for everyone.
|
||||
|
||||

|
||||
|
||||
### Common flags
|
||||
|
||||
`nomifun-web` (defined in `apps/web/src/main.rs`) accepts both CLI flags and
|
||||
environment variables:
|
||||
|
||||
| Flag | Env var | Default | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `--host` | `NOMIFUN_WEB_HOST` | `127.0.0.1` | Bind address. Use `0.0.0.0` only when you intend LAN/VPN/public access; pre-seed or complete admin setup first. |
|
||||
| `--port` | `NOMIFUN_WEB_PORT` | `8787` | Port for both `/api` and the SPA. |
|
||||
| `--data-dir` | `NOMIFUN_DATA_DIR` | _per-user app-data dir, same as the [desktop default](#where-data-lives-desktop)_ | Backend data dir (db / logs / bun cache / agent state). The env value is taken literally (no `/Nomi` suffix). Use an absolute path in production. |
|
||||
| `--dist` | `NOMIFUN_WEB_DIST` | `../../ui/dist` | SPA static directory. **Set this explicitly when running outside the repo root.** |
|
||||
| `--admin-user` | `NOMIFUN_ADMIN_USERNAME` | `admin` | Username for pre-seeded admin (only honoured before the admin exists). |
|
||||
| `--admin-password` | `NOMIFUN_ADMIN_PASSWORD` | _(none — interactive first-run setup)_ | Pre-seed the admin password and skip interactive first-run. |
|
||||
| `--insecure-no-auth` | `NOMIFUN_WEB_INSECURE_NO_AUTH` | `false` | **Danger.** Disable authentication entirely (desktop-style local mode). Loopback / trusted private network only. |
|
||||
| _(env only)_ | `NOMIFUN_HTTPS` | `false` | Set to `true` when fronted by TLS so cookies get the `Secure` flag. |
|
||||
|
||||
Example, opening it up to the LAN with a pre-seeded admin:
|
||||
|
||||
```bash
|
||||
nomifun-web \
|
||||
--host 0.0.0.0 --port 8787 \
|
||||
--data-dir /var/lib/nomifun \
|
||||
--dist /opt/nomifun/web \
|
||||
--admin-user admin \
|
||||
--admin-password "change-me-to-something-strong"
|
||||
```
|
||||
|
||||
For full deployment guidance — systemd unit, reverse-proxy, and security
|
||||
notes — see
|
||||
[`../guides/web-server-deployment.md`](../guides/web-server-deployment.md).
|
||||
|
||||
## Docker / Docker Compose
|
||||
|
||||
The repository ships a multi-stage `Dockerfile` and a `docker-compose.yml`
|
||||
that produce a **headless** (no GUI) image: SPA + `nomifun-web` + `bun` on
|
||||
`debian:bookworm-slim`.
|
||||
|
||||
### Quick start with Compose
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
# then open http://<server-ip>:8787
|
||||
```
|
||||
|
||||
The service is configured with `restart: unless-stopped` so installing it
|
||||
**is** enabling it on boot. Persistent state (SQLite database, logs, bun
|
||||
cache, agent state) lives in the named volume `nomifun-data` mounted at
|
||||
`/data` inside the container.
|
||||
|
||||
The image's defaults are tuned for container life:
|
||||
|
||||
```text
|
||||
NOMIFUN_WEB_HOST=0.0.0.0
|
||||
NOMIFUN_WEB_PORT=8787
|
||||
NOMIFUN_DATA_DIR=/data
|
||||
NOMIFUN_WEB_DIST=/opt/nomifun/web
|
||||
SHELL=/bin/bash
|
||||
```
|
||||
|
||||
Authentication is on, but first-run setup can be claimed by the first browser
|
||||
that reaches the service. Pre-seed the admin or complete setup on a trusted
|
||||
network before publishing port `8787` broadly. For anything reachable from the
|
||||
internet, put TLS in front of it — the bundled `Caddyfile` and the
|
||||
commented-out `caddy` service in `docker-compose.yml` are the recommended path.
|
||||
Set
|
||||
`NOMIFUN_HTTPS=true` on the `nomifun` service when you do, so the session
|
||||
cookie gains the `Secure` flag.
|
||||
|
||||
### Pre-seed the admin (recommended for non-interactive setup)
|
||||
|
||||
The first browser visit otherwise wins the admin account; pre-seeding closes
|
||||
that race window:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml — under services.nomifun
|
||||
environment:
|
||||
NOMIFUN_ADMIN_USERNAME: admin
|
||||
NOMIFUN_ADMIN_PASSWORD: "change-me-to-something-strong"
|
||||
NOMIFUN_HTTPS: "true" # only when behind a TLS proxy
|
||||
```
|
||||
|
||||
### Speeding up Rust builds
|
||||
|
||||
The Rust stage uses BuildKit cache mounts (`/usr/local/cargo/registry` and
|
||||
`/src/target`), so a one-line source change recompiles in seconds. To use a
|
||||
mirror for the cargo registry (e.g. on slow links):
|
||||
|
||||
```bash
|
||||
docker build --build-arg CARGO_REGISTRY_MIRROR=https://rsproxy.cn/index/ .
|
||||
```
|
||||
|
||||
For the long-form deployment guide (TLS, reverse-proxy patterns, systemd
|
||||
unit, security caveats) see
|
||||
[`../guides/web-server-deployment.md`](../guides/web-server-deployment.md).
|
||||
|
||||
## Verifying your install
|
||||
|
||||
A 30-second smoke test you can run after either path:
|
||||
|
||||
```bash
|
||||
# Rust workspace compiles cleanly
|
||||
cargo check --workspace
|
||||
|
||||
# All three binaries build
|
||||
cargo build --workspace --bins
|
||||
# → target/(debug|release)/{nomicore, nomifun-web, nomifun-desktop}
|
||||
|
||||
# Web host responds with the SPA + auth status
|
||||
curl -sS http://127.0.0.1:8787/ | head -c 200
|
||||
curl -sS http://127.0.0.1:8787/api/auth/status
|
||||
# → 200 {"success":true,"needs_setup":..., "user_count":...}
|
||||
```
|
||||
|
||||
If you see `nomifun-web: embedded backend + SPA on one port` in the logs and
|
||||
`/api/auth/status` returns JSON, the backend is up and the SPA is being
|
||||
served from the same port.
|
||||
|
||||
## What's next
|
||||
|
||||
- [Quick Start](quick-start.md) — your first conversation in Nomi.
|
||||
- [`../guides/web-server-deployment.md`](../guides/web-server-deployment.md)
|
||||
— production hardening for the web host.
|
||||
- [`../contributing/development.md`](../contributing/development.md)
|
||||
— set up a developer loop (renderer hot-reload, backend rebuild, debug tools).
|
||||
@@ -0,0 +1,275 @@
|
||||
# 安装
|
||||
|
||||
NomiFun 有两种宿主模式,共享同一个 Rust 后端(参见
|
||||
[简介](introduction.zh.md))。本页覆盖目前可行的全部三种安装方式:
|
||||
|
||||
- [从源码构建桌面应用](#从源码构建桌面应用) —— `nomifun-desktop`
|
||||
(Tauri 外壳),桌面本地信任,单用户。
|
||||
- [从源码构建 Web 服务](#从源码构建-web-服务) —— `nomifun-web`,
|
||||
带鉴权,自托管。
|
||||
- [Docker / Docker Compose](#docker--docker-compose) —— 同一个 Web 服务
|
||||
的容器化方案。
|
||||
|
||||
> **官方预构建安装包尚未发布。** 桌面包、macOS 签名、updater 产物、Docker
|
||||
> 和 native Linux service 都可以本地构建;但还没有官方公开发布渠道。下面所有
|
||||
> 安装路径都需要从源码构建。当前打包说明见
|
||||
> [`../contributing/building-and-packaging.zh.md`](../contributing/building-and-packaging.zh.md)。
|
||||
|
||||
## 前置条件
|
||||
|
||||
无论你选择哪种模式,都需要一套可工作的构建工具链。具体要求如下:
|
||||
|
||||
| 工具 | 最低版本 | 用途 | 备注 |
|
||||
| --- | --- | --- | --- |
|
||||
| **Rust** | stable,edition 2024 | 编译后端(桌面端还需编译 Tauri 外壳)。 | 通过 [`rustup`](https://rustup.rs/) 安装。工作区固定使用 `edition = "2024"` 与 `resolver = "3"`。 |
|
||||
| **Bun** | **≥ 1.3.13** | 前端包管理器与构建(同时也是智能体引擎的硬运行时依赖)。 | `1.1.38` 存在 stdin 缺陷——请勿使用。 |
|
||||
| **Tauri CLI** | v2 | 构建桌面外壳。 | 作为 `devDependency` 引入;无需全局安装。 |
|
||||
| **Git** | 任何近期版本 | 克隆仓库,以及技能发现与若干内置工具会用到。 | |
|
||||
| **C/C++ 构建工具** | 因平台而异 | `rusqlite`(bundled)、`aws-lc-rs`、`libgit2-sys` 需要。 | Windows:MSVC + WebView2 运行时。macOS:Xcode CLT。Linux:`build-essential cmake clang pkg-config perl`。 |
|
||||
|
||||
在运行 NomiFun 的宿主上推荐安装(构建机不需要):
|
||||
|
||||
- **`ripgrep`** —— 代码搜索后端;不可用时回退到 `grep`。
|
||||
- **`node` / `npm` / `npx`** —— 许多用户安装的 MCP stdio 服务通过
|
||||
`npx -y …` 启动。
|
||||
|
||||
### 克隆仓库
|
||||
|
||||
```bash
|
||||
git clone <your-fork-or-mirror>/nomifun-tauri.git
|
||||
cd nomifun-tauri
|
||||
```
|
||||
|
||||
本页其余内容均假设你的工作目录为仓库根目录。
|
||||
|
||||
### 安装 JS 依赖
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
这会为整个工作区与 `ui/` 填充 `node_modules/`。每当 `package.json` 或
|
||||
`ui/package.json` 发生改变时都需重新执行。
|
||||
|
||||
## 从源码构建桌面应用
|
||||
|
||||
桌面应用是一个 Tauri 2 外壳(`apps/desktop`,二进制
|
||||
`nomifun-desktop`),它在进程内链接后端,并在一个空闲 localhost 端口上以
|
||||
桌面 `TrustLocalToken` 策略启动后端。自己的 WebView 会收到每次启动生成的
|
||||
本地信任 secret,因此桌面窗口没有登录界面。
|
||||
|
||||
### 在开发模式下运行
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
完整流程如下:
|
||||
|
||||
1. Tauri 的 `beforeDevCommand` 执行 `bun run --filter=./ui dev`,启动
|
||||
Vite 开发服务器在 `http://localhost:5173`。
|
||||
2. `cargo` 编译 `nomifun-desktop`(以及它依赖的整个工作区)。
|
||||
3. 外壳启动,挑选一个空闲端口,派生嵌入式后端,并加载 Vite 开发地址。
|
||||
渲染端支持热重载;后端只在其 Rust 代码变更时才会重启。
|
||||
|
||||
你会在控制台中看到类似 `Server listening on 127.0.0.1:54760` 的 tracing
|
||||
日志——这就是嵌入式后端。渲染端会读取 `window.__backendPort`(由 Tauri
|
||||
外壳作为 init script 注入),因此 SPA 始终知道 `/api` 该往哪里调用。
|
||||
|
||||

|
||||
|
||||
### 构建 Release 二进制
|
||||
|
||||
```bash
|
||||
bun run build:ui # 将 SPA 构建到 ui/dist
|
||||
bun run build # tauri build → 安装包 + 独立二进制
|
||||
```
|
||||
|
||||
`tauri build` 会产出:
|
||||
|
||||
- 位于 `target/release/nomifun-desktop`(Windows 上为 `.exe`)的独立可
|
||||
执行文件。
|
||||
- 位于 `target/release/bundle/` 下的平台安装包——`.msi`/`.exe`
|
||||
(Windows)、`.dmg`/`.app`(macOS)、`.deb`/`.AppImage`(Linux)。
|
||||
|
||||
`bun run build` 产物适合本地测试。要分发 macOS 构建,请配置
|
||||
`apps/desktop/signing/.env.signing` 并使用 `bun run build:signed`。Windows
|
||||
签名仍需要外部证书。若要测试 updater 骨架,可使用 `bun run build:updater`,
|
||||
它会把 `bundle.createUpdaterArtifacts` 设为 true。发布任何更新前,必须替换
|
||||
`apps/desktop/tauri.conf.json` 中的 updater endpoint 与公钥。
|
||||
|
||||
### 数据存放位置(桌面端)
|
||||
|
||||
桌面应用把数据库与运行时文件存放在按用户区分的应用数据目录下,
|
||||
再拼接 `Nomi`:
|
||||
|
||||
| 操作系统 | 默认路径 |
|
||||
| --- | --- |
|
||||
| Windows | `%LOCALAPPDATA%\NomiFun\Nomi`(例如 `C:\Users\<you>\AppData\Local\NomiFun\Nomi`) |
|
||||
| macOS | `~/Library/Application Support/NomiFun/Nomi` |
|
||||
| Linux | `$XDG_DATA_HOME/NomiFun/Nomi`(通常为 `~/.local/share/NomiFun/Nomi`) |
|
||||
|
||||
启动前可通过 `NOMIFUN_DATA_DIR=<absolute path>` 覆盖——外壳会附加
|
||||
`/Nomi`,因此目录会变成 `$NOMIFUN_DATA_DIR/Nomi`。
|
||||
|
||||
> 旧版本默认使用 `<system temp>/nomifun-data/Nomi`,操作系统的临时目录
|
||||
> 清理可能在那里销毁用户数据。现在应用首次启动时会自动把这类旧安装
|
||||
> 搬迁到按用户区分的位置(一次性):复制数据、改写数据库内的绝对路径,
|
||||
> 并把旧目录保留作备份。若搬迁无法完成,应用会先从旧目录启动,并在
|
||||
> 下次启动时重试。
|
||||
|
||||
> 提示:应用对用户呈现的名称统一为 `Nomi`——bundle 产品名
|
||||
> (`apps/desktop/tauri.conf.json`)、运行时窗口标题、数据文件夹都用
|
||||
> `Nomi`。内部标识符按设计仍保留旧的 `nomifun` 名(crate、`NOMIFUN_*`
|
||||
> 环境变量、`com.nomifun.*` bundle 标识符)。
|
||||
|
||||
## 从源码构建 Web 服务
|
||||
|
||||
`nomifun-web` 是一个 axum 服务,**既**在进程内挂载同一个后端,**又**在
|
||||
同一个端口(默认 `8787`)上提供已构建的 SPA。它适合在 LAN、VPN 或
|
||||
VPS 上自托管。
|
||||
|
||||
### 构建并运行
|
||||
|
||||
```bash
|
||||
bun install
|
||||
bun run build:ui # ui/dist —— 在非开发模式下提供服务前必须先构建
|
||||
bun run serve:web # = cargo run -p nomifun-web
|
||||
```
|
||||
|
||||
默认情况下,服务绑定在 `127.0.0.1:8787`,并使用与桌面应用相同的按用户
|
||||
数据目录(参见[数据存放位置(桌面端)](#数据存放位置桌面端)):
|
||||
|
||||
```text
|
||||
nomifun-web: embedded backend + SPA on one port
|
||||
listening on 127.0.0.1:8787 auth=required dist=../../ui/dist
|
||||
```
|
||||
|
||||
在同时装有桌面应用的机器上,直接裸跑 `nomifun-web` 会打开桌面应用的
|
||||
数据——排他的 `server.lock` 保证两个后端不会同时运行在该目录上。
|
||||
|
||||
在浏览器中打开 `http://127.0.0.1:8787`。首次访问会被引导到设置页——
|
||||
你输入的用户名与密码**将成为初始管理员账户**。此后所有人都需要登录。
|
||||
|
||||

|
||||
|
||||
### 常用参数
|
||||
|
||||
`nomifun-web`(定义在 `apps/web/src/main.rs` 中)同时接受 CLI 参数与
|
||||
环境变量:
|
||||
|
||||
| 参数 | 环境变量 | 默认值 | 含义 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--host` | `NOMIFUN_WEB_HOST` | `127.0.0.1` | 绑定地址。仅在确实需要 LAN/VPN/公网访问时使用 `0.0.0.0`;请先预置或完成管理员设置。 |
|
||||
| `--port` | `NOMIFUN_WEB_PORT` | `8787` | `/api` 与 SPA 共用的端口。 |
|
||||
| `--data-dir` | `NOMIFUN_DATA_DIR` | _按用户应用数据目录,与[桌面端默认](#数据存放位置桌面端)相同_ | 后端数据目录(数据库 / 日志 / bun 缓存 / 智能体状态)。环境变量取字面值(不附加 `/Nomi`)。生产环境请使用绝对路径。 |
|
||||
| `--dist` | `NOMIFUN_WEB_DIST` | `../../ui/dist` | SPA 静态目录。**在仓库根目录之外运行时务必显式指定。** |
|
||||
| `--admin-user` | `NOMIFUN_ADMIN_USERNAME` | `admin` | 预置管理员的用户名(仅在管理员尚未存在时生效)。 |
|
||||
| `--admin-password` | `NOMIFUN_ADMIN_PASSWORD` | _(无 —— 交互式首次设置)_ | 预置管理员密码并跳过交互式首次启动。 |
|
||||
| `--insecure-no-auth` | `NOMIFUN_WEB_INSECURE_NO_AUTH` | `false` | **危险。** 完全禁用鉴权(桌面式本地模式)。仅限 loopback / 受信任的私有网络。 |
|
||||
| _(仅环境变量)_ | `NOMIFUN_HTTPS` | `false` | 当前面有 TLS 终止时设为 `true`,使 cookie 获得 `Secure` 标记。 |
|
||||
|
||||
例:将其开放到 LAN,并预置管理员:
|
||||
|
||||
```bash
|
||||
nomifun-web \
|
||||
--host 0.0.0.0 --port 8787 \
|
||||
--data-dir /var/lib/nomifun \
|
||||
--dist /opt/nomifun/web \
|
||||
--admin-user admin \
|
||||
--admin-password "change-me-to-something-strong"
|
||||
```
|
||||
|
||||
完整的部署指南——systemd unit、反向代理与安全注意事项——请参见
|
||||
[`../guides/web-server-deployment.md`](../guides/web-server-deployment.md)。
|
||||
|
||||
## Docker / Docker Compose
|
||||
|
||||
仓库附带一份多阶段 `Dockerfile` 与一份 `docker-compose.yml`,会构建出
|
||||
一个**无 GUI**镜像:在 `debian:bookworm-slim` 上的 SPA + `nomifun-web` +
|
||||
`bun`。
|
||||
|
||||
### 用 Compose 快速上手
|
||||
|
||||
在仓库根目录:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
# 然后访问 http://<server-ip>:8787
|
||||
```
|
||||
|
||||
服务配置了 `restart: unless-stopped`,所以**安装即等同于开机自启**。
|
||||
持久化状态(SQLite 数据库、日志、bun 缓存、智能体状态)存放在挂载到容器
|
||||
内 `/data` 的命名卷 `nomifun-data` 中。
|
||||
|
||||
镜像默认值已针对容器生命周期进行了调优:
|
||||
|
||||
```text
|
||||
NOMIFUN_WEB_HOST=0.0.0.0
|
||||
NOMIFUN_WEB_PORT=8787
|
||||
NOMIFUN_DATA_DIR=/data
|
||||
NOMIFUN_WEB_DIST=/opt/nomifun/web
|
||||
SHELL=/bin/bash
|
||||
```
|
||||
|
||||
鉴权已开启,但首次设置可被第一个访问到服务的浏览器认领。请先预置
|
||||
管理员或在受信网络完成 setup,再大范围发布 `8787`。对于公网可达的部署,
|
||||
请在前面加一层 TLS——附带的 `Caddyfile` 与 `docker-compose.yml` 中被注释掉的
|
||||
`caddy` 服务即是推荐做法。届时记得把 `nomifun` 服务的 `NOMIFUN_HTTPS=true`
|
||||
打开,让会话 cookie 获得 `Secure` 标记。
|
||||
|
||||
### 预置管理员(推荐用于非交互式部署)
|
||||
|
||||
否则首次访问浏览器的人会获得管理员账号;预置管理员可以关闭这个竞态
|
||||
窗口:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml —— 在 services.nomifun 下
|
||||
environment:
|
||||
NOMIFUN_ADMIN_USERNAME: admin
|
||||
NOMIFUN_ADMIN_PASSWORD: "change-me-to-something-strong"
|
||||
NOMIFUN_HTTPS: "true" # 仅当部署在 TLS 反向代理之后时
|
||||
```
|
||||
|
||||
### 加速 Rust 构建
|
||||
|
||||
Rust 阶段使用了 BuildKit cache mounts(`/usr/local/cargo/registry` 与
|
||||
`/src/target`),所以一行源代码改动只需几秒就能重新编译。如需为 cargo
|
||||
注册表配置镜像(例如在网络较慢时):
|
||||
|
||||
```bash
|
||||
docker build --build-arg CARGO_REGISTRY_MIRROR=https://rsproxy.cn/index/ .
|
||||
```
|
||||
|
||||
完整的部署指南(TLS、反向代理模式、systemd unit、安全注意事项)请参见
|
||||
[`../guides/web-server-deployment.md`](../guides/web-server-deployment.md)。
|
||||
|
||||
## 验证你的安装
|
||||
|
||||
无论走哪条路径,都可以做一次 30 秒的快速检验:
|
||||
|
||||
```bash
|
||||
# Rust 工作区编译干净
|
||||
cargo check --workspace
|
||||
|
||||
# 三个二进制都能构建
|
||||
cargo build --workspace --bins
|
||||
# → target/(debug|release)/{nomicore, nomifun-web, nomifun-desktop}
|
||||
|
||||
# Web 主机响应 SPA + 鉴权状态
|
||||
curl -sS http://127.0.0.1:8787/ | head -c 200
|
||||
curl -sS http://127.0.0.1:8787/api/auth/status
|
||||
# → 200 {"success":true,"needs_setup":..., "user_count":...}
|
||||
```
|
||||
|
||||
如果你在日志中看到 `nomifun-web: embedded backend + SPA on one port`,
|
||||
并且 `/api/auth/status` 返回了 JSON,则后端已启动,并且 SPA 在同一个
|
||||
端口上提供服务。
|
||||
|
||||
## 接下来
|
||||
|
||||
- [快速上手](quick-start.zh.md) —— 你在 NomiFun 中的第一段会话。
|
||||
- [`../guides/web-server-deployment.md`](../guides/web-server-deployment.md)
|
||||
—— Web 主机的生产环境加固。
|
||||
- [`../contributing/development.zh.md`](../contributing/development.zh.md)
|
||||
—— 搭建开发环路(渲染端热重载、后端重建、调试工具)。
|
||||
@@ -0,0 +1,85 @@
|
||||
# Introduction
|
||||
|
||||
**NomiFun** is an open-source AI workstation and coding workspace. It unifies
|
||||
multiple AI runtimes, LLM providers, MCP servers, skills, terminals, knowledge
|
||||
bases, scheduled work, and companion/remote capability surfaces in one local-first
|
||||
application.
|
||||
|
||||
> Ready to run it? Start with [Installation](installation.md), then
|
||||
> [Quick Start](quick-start.md). For the full documentation map, see
|
||||
> [docs/README.md](../README.md).
|
||||
|
||||

|
||||
|
||||
## What NomiFun Solves
|
||||
|
||||
Modern AI workflows are scattered across separate CLIs, terminals, browser
|
||||
tabs, MCP servers, and local scripts. NomiFun pulls them into one workspace:
|
||||
|
||||
- **Many agents, one surface.** Use the built-in Nomi engine or external
|
||||
ACP-style CLIs such as Claude Code, Codex, Gemini CLI, Qwen, and OpenCode.
|
||||
- **One workspace per conversation.** Conversations can own files, previews,
|
||||
diffs, terminals, and knowledge bindings instead of living as isolated chat
|
||||
transcripts.
|
||||
- **Backend-driven automation.** Scheduled tasks, AutoWork requirements,
|
||||
terminal sessions, channel integrations, and completion notifications are
|
||||
durable backend services, not foreground browser-tab state.
|
||||
- **Extensible capability layer.** MCP servers, skills, assistants, browser use,
|
||||
computer use, and public remote capability fronts can be composed per runtime.
|
||||
- **Local-first deployment.** Run it as a Tauri desktop app or a self-hosted web
|
||||
server. You provide the model/API credentials and decide where the data lives.
|
||||
|
||||
NomiFun is not a no-code SaaS chat product. It is infrastructure for users who
|
||||
are comfortable configuring agents, providers, local tools, and self-hosted
|
||||
services.
|
||||
|
||||
## Two Hosts, One Backend
|
||||
|
||||
Both hosts run the same Rust backend (`nomifun-app`) in-process and load the
|
||||
same React SPA (`ui/dist`).
|
||||
|
||||
| Mode | Binary | Auth model | Typical use |
|
||||
| --- | --- | --- | --- |
|
||||
| Desktop app | `nomifun-desktop` | Per-boot local trust token injected into the desktop webview | Personal workstation |
|
||||
| Web server | `nomifun-web` | Login required by default; first-run setup or pre-seeded admin | Browser / LAN / server deployment |
|
||||
|
||||
```text
|
||||
nomifun-desktop
|
||||
Tauri shell -> embedded backend on 127.0.0.1:<ephemeral> -> same SPA
|
||||
|
||||
nomifun-web
|
||||
axum server -> /api + /ws + static ui/dist on one port (default 8787)
|
||||
```
|
||||
|
||||
For implementation details, see [Architecture Overview](../architecture/overview.md).
|
||||
|
||||
## Main Surfaces
|
||||
|
||||
- **Home & conversations** (`/guid`): start and continue AI sessions.
|
||||
- **Terminals**: PTY-backed agent or shell sessions inside the app.
|
||||
- **Models**: providers, local agent detection, global IDMM/failover settings.
|
||||
- **Assistant & Skill**: assistant personas and skill management.
|
||||
- **MCP**: local MCP server configuration.
|
||||
- **Open Capabilities**: WebUI remote access, remote MCP, and REST capability
|
||||
exposure.
|
||||
- **Requirements / AutoWork**: backend-owned queue processing and completion
|
||||
notifications.
|
||||
- **Scheduled tasks**: recurring or one-shot jobs.
|
||||
- **Desktop Companion** (`/nomi`): companion configuration, memory, and remote binding.
|
||||
- **Knowledge**: local knowledge-base management and session bindings.
|
||||
|
||||
The current frontend route source is
|
||||
`ui/src/renderer/components/layout/Router.tsx`.
|
||||
|
||||
## Project Status
|
||||
|
||||
NomiFun is in active development. [STATUS.md](../../STATUS.md) is the compact
|
||||
current-state snapshot. Design and audit history is not kept in the repo; consult
|
||||
git history for past decisions.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
NomiFun began as a fork of the open-source
|
||||
[AionUi](https://github.com/iOfficeAI/AionUi) project and has since been
|
||||
substantially refactored around a Tauri + Rust architecture. NomiFun is released
|
||||
under the Apache-2.0 License.
|
||||
@@ -0,0 +1,73 @@
|
||||
# 简介
|
||||
|
||||
**NomiFun** 是一个面向 AI agent 工作流的本地优先工作台。它把多种
|
||||
CLI agent、内置 Nomi 引擎、模型提供商、MCP 服务、技能、终端、计划任务
|
||||
和远程 WebUI 收拢到同一个 Rust + Tauri monorepo 中。
|
||||
|
||||
> 想立刻开始?请先读 [安装](installation.zh.md),再读
|
||||
> [快速上手](quick-start.zh.md)。完整文档地图见 [`../README.zh.md`](../README.zh.md)。
|
||||
|
||||

|
||||
|
||||
## 它解决什么问题
|
||||
|
||||
真实的 AI 工作流经常被拆散在多个地方:一个终端跑 Claude Code,一个终端跑
|
||||
Codex,浏览器里开着自托管页面,旁边还有单独的 MCP 服务和项目脚本。
|
||||
NomiFun 的目标不是再做一个聊天框,而是把这些运行时接到同一个工作区:
|
||||
|
||||
- **一个会话入口,多种 agent。** 会话可以选择内置 Nomi、Claude Code、
|
||||
Codex、Gemini、Qwen、OpenCode、CodeBuddy 等后端。
|
||||
- **一个模型目录,多处复用。** 在 `/models` 配好 Anthropic、OpenAI、
|
||||
Bedrock、Vertex 或兼容接口后,支持这些模型的 agent 和助手可以复用。
|
||||
- **一个工作区,不只是消息流。** 会话有工作目录、文件树、预览面板和后端
|
||||
管理的 PTY 终端。
|
||||
- **后端驱动的自动化。** 计划任务、AutoWork、IDMM、WebUI 远程访问、
|
||||
MCP 暴露和频道能力都由 Rust 后端持久化管理。
|
||||
- **桌面与 Web 共用后端。** Tauri 桌面端和 `nomifun-web` 自托管服务使用
|
||||
同一套 `nomifun-app` 后端与同一份 React SPA。
|
||||
|
||||
NomiFun 更适合已经在用 agent 做真实工作的用户。它要求你理解 API key、
|
||||
本地数据目录、CLI agent 安装和自托管边界;它不是零配置的 SaaS 聊天产品。
|
||||
|
||||
## 两种运行方式
|
||||
|
||||
| 模式 | 二进制 | 鉴权模型 | 典型用途 |
|
||||
| --- | --- | --- | --- |
|
||||
| 桌面应用 | `nomifun-desktop` | 桌面外壳使用本地信任 token 访问嵌入式后端;远程浏览器仍需登录 | 单机工作站、日常开发 |
|
||||
| Web 服务 | `nomifun-web` | 默认开启登录;首次访问创建管理员 | LAN/VPN/VPS 自托管 |
|
||||
|
||||
桌面模式会在进程内启动 `nomifun-app`,监听一个随机 localhost 端口,并通过
|
||||
每次启动生成的本地信任 token 让 WebView 免登录访问。WebUI 远程访问打开后,
|
||||
额外的 LAN 监听器仍然要求远程浏览器登录。
|
||||
|
||||
`nomifun-web` 则在一个端口上同时提供 SPA 与 API,默认端口是 `8787`。Docker
|
||||
和 systemd 部署都走这条路径。
|
||||
|
||||
## 当前功能地图
|
||||
|
||||
- **会话与工作区**:`/guid` 创建会话,`/conversation/:id` 运行会话。
|
||||
- **模型配置**:`/models` 管理提供商、模型、凭据和全局故障转移队列。
|
||||
- **助手与技能**:`/assistants` 管理助手;其中 `tab=skills` 管理技能。
|
||||
- **MCP**:`/mcp` 管理 MCP server、连接测试、OAuth 和 agent 配置同步。
|
||||
- **开放能力**:`/open-capabilities` 管理 WebUI 远程访问、MCP/API 暴露等外部入口。
|
||||
- **桌面伙伴**:`/nomi` 管理伙伴、远程频道绑定和 companion 相关设置。
|
||||
- **终端**:`/terminal-new` 创建、`/terminal/:id` 运行后端 PTY。
|
||||
- **计划任务**:`/scheduled` 管理 cron 触发的会话任务。
|
||||
- **AutoWork**:`/requirements` 管理需求看板和自动执行。
|
||||
|
||||
更多内部结构见 [`../architecture/`](../architecture/),用户指南见
|
||||
[`../guides/`](../guides/)。
|
||||
|
||||
## 项目状态
|
||||
|
||||
NomiFun 仍在活跃开发中,但已经不是旧的 Electron 多仓迁移状态。当前仓库是
|
||||
Rust workspace + Tauri desktop + Web host 的单仓结构。顶层
|
||||
[`../../STATUS.md`](../../STATUS.md) 记录当前状态;历史设计稿与审计记录不在
|
||||
仓库中保留,需要时请查阅 git 历史。
|
||||
|
||||
## 接下来
|
||||
|
||||
- [安装](installation.zh.md)
|
||||
- [快速上手](quick-start.zh.md)
|
||||
- [开发环境](../contributing/development.zh.md)
|
||||
- [Web 服务部署](../guides/web-server-deployment.zh.md)
|
||||
@@ -0,0 +1,82 @@
|
||||
# Quick Start
|
||||
|
||||
A short walkthrough for the first useful NomiFun session. This assumes the repo
|
||||
is already installed; otherwise start with [Installation](installation.md).
|
||||
|
||||
## 1. Launch
|
||||
|
||||
Desktop development:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
The desktop shell starts the backend on a private loopback port and injects a
|
||||
local trust token into its own webview. There is no desktop login screen.
|
||||
|
||||
Web server:
|
||||
|
||||
```bash
|
||||
bun run serve:web
|
||||
# open http://127.0.0.1:8787
|
||||
```
|
||||
|
||||
The web host requires login by default. On a fresh data directory, the first
|
||||
visitor creates the initial admin unless you pre-seed `NOMIFUN_ADMIN_PASSWORD`.
|
||||
|
||||

|
||||
|
||||
## 2. Start From `/guid`
|
||||
|
||||
After auth, the app opens `/guid`. This is the default session start surface.
|
||||
|
||||
You can choose:
|
||||
|
||||
- an agent/runtime,
|
||||
- a model/provider when the runtime supports it,
|
||||
- an assistant persona,
|
||||
- skills or MCP tools,
|
||||
- a workspace path,
|
||||
- and the first prompt.
|
||||
|
||||

|
||||
|
||||
## 3. Configure A Model
|
||||
|
||||
Open **Models** (`/models`) and configure at least one provider or detected CLI
|
||||
agent. The page owns provider credentials, agent availability, and global
|
||||
reliability settings such as IDMM and model failover.
|
||||
|
||||

|
||||
|
||||
For the simplest first run, use the built-in Nomi engine with an API provider
|
||||
you have credentials for. External CLIs such as Claude Code, Codex, Gemini CLI,
|
||||
Qwen, and OpenCode must also be installed on the host before NomiFun can use
|
||||
them.
|
||||
|
||||
## 4. Send The First Message
|
||||
|
||||
Back on `/guid`:
|
||||
|
||||
1. Choose an agent.
|
||||
2. Choose a model if required.
|
||||
3. Optionally choose an assistant.
|
||||
4. Type a prompt.
|
||||
5. Send with the button or `Ctrl/Cmd+Enter`.
|
||||
|
||||
NomiFun creates a conversation and navigates to `/conversation/<id>`.
|
||||
|
||||
## 5. Use The Workspace
|
||||
|
||||
Each conversation can use a working directory. Inside a conversation you can
|
||||
inspect messages, tool calls, file edits, previews, and terminal sessions.
|
||||
|
||||
Useful next pages:
|
||||
|
||||
- [Terminal](../guides/terminal.md)
|
||||
- [MCP & Skills](../guides/mcp-and-skills.md)
|
||||
- [Assistants](../guides/assistants.md)
|
||||
- [AutoWork & Requirements](../guides/autowork-requirements.md)
|
||||
- [Scheduled Tasks](../guides/scheduled-tasks.md)
|
||||
- [Web Server Deployment](../guides/web-server-deployment.md)
|
||||
- [Architecture Overview](../architecture/overview.md)
|
||||
@@ -0,0 +1,98 @@
|
||||
# 快速上手
|
||||
|
||||
这页带你完成第一段 NomiFun 会话。若还没有安装,请先看
|
||||
[安装](installation.zh.md)。
|
||||
|
||||
桌面应用和 Web 服务使用同一份 UI。差异主要在鉴权:桌面 WebView 通过本地
|
||||
信任 token 免登录;`nomifun-web` 和 WebUI 远程访问需要登录。
|
||||
|
||||
## 1. 启动
|
||||
|
||||
### 桌面开发模式
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
这会启动 Vite、编译 Tauri shell,并在桌面进程中嵌入 `nomifun-app` 后端。
|
||||
后端会选择一个空闲 localhost 端口,桌面 WebView 自动携带本地信任 token。
|
||||
|
||||
### Web 服务模式
|
||||
|
||||
```bash
|
||||
bun run build:ui
|
||||
bun run serve:web
|
||||
```
|
||||
|
||||
访问 `http://127.0.0.1:8787`。首次访问会创建管理员账号,之后需要登录。
|
||||
|
||||

|
||||
|
||||
## 2. 认识首页
|
||||
|
||||
登录后默认进入 `/guid`。这里提供开始会话所需的几件事:
|
||||
|
||||
- **agent 选择**:选择 Nomi、Claude Code、Codex、Gemini、Qwen、OpenCode 等后端。
|
||||
- **模型选择**:对支持模型切换的 agent 选择 provider 和 model id。
|
||||
- **助手**:选择预设 persona、system prompt、技能与工具组合。
|
||||
- **工具与工作区**:选择本次会话可见的 MCP server 和技能,并确认工作目录。
|
||||
- **输入框**:输入第一条提示词,必要时用 `@` 引用文件、技能或助手。
|
||||
|
||||

|
||||
|
||||
## 3. 配置模型
|
||||
|
||||
发送第一条消息前,至少需要配置一个可用模型。打开 `/models`:
|
||||
|
||||
- 添加 Anthropic、OpenAI、Bedrock、Vertex 或兼容 OpenAI/Anthropic 协议的 provider。
|
||||
- 为 provider 填写 API key、base URL 和默认模型。
|
||||
- 如需无人值守长任务,可配置 **Model Failover Queue**,让 Nomi 引擎在模型失败、
|
||||
限流或不可用时按顺序尝试备用模型。
|
||||
|
||||

|
||||
|
||||
外部 CLI agent 仍需要在宿主机上安装对应 CLI;`/models` 只解决模型凭据和
|
||||
模型选择,不会替你安装第三方 CLI。
|
||||
|
||||
## 4. 创建第一段会话
|
||||
|
||||
回到 `/guid`:
|
||||
|
||||
1. 选择 **Nomi**,它不依赖外部 CLI,最适合首跑验证。
|
||||
2. 选择一个已配置的模型。
|
||||
3. 可选:选择一个助手。
|
||||
4. 输入提示词,例如:
|
||||
|
||||
> 写一个返回第 n 个斐波那契数的 Python 函数,并附一个小测试。
|
||||
|
||||
5. 发送。
|
||||
|
||||
NomiFun 会创建新会话并进入 `/conversation/:id`,随后开始流式输出。
|
||||
|
||||
## 5. 使用会话工作区
|
||||
|
||||
每段会话都有独立工作目录。会话页通常包含:
|
||||
|
||||
- **消息流**:模型回复、工具调用、文件变更和执行状态。
|
||||
- **文件树**:显示本会话工作目录中的文件。
|
||||
- **预览面板**:预览代码、Markdown、PDF、Office、HTML 和 diff。
|
||||
- **终端**:从 `/terminal-new` 或会话内入口启动 PTY,默认挂载到工作目录。
|
||||
|
||||
可以让 agent 把刚才的函数写入文件,然后在文件树和预览面板里检查结果。
|
||||
|
||||
## 6. 常用入口
|
||||
|
||||
- `/assistants`:管理助手;`/assistants?tab=skills` 管理技能。
|
||||
- `/mcp`:管理 MCP server、连接测试、OAuth 和导入/同步。
|
||||
- `/open-capabilities`:管理 WebUI 远程访问和对外能力暴露。
|
||||
- `/scheduled`:创建 cron 会话任务;支持从会话带上下文创建。
|
||||
- `/requirements`:AutoWork 需求看板。
|
||||
- `/nomi`:Nomi 伙伴、远程频道绑定和 companion 设置。
|
||||
|
||||
接下来可以继续阅读:
|
||||
|
||||
- [MCP 与技能](../guides/mcp-and-skills.zh.md)
|
||||
- [助手](../guides/assistants.zh.md)
|
||||
- [终端](../guides/terminal.zh.md)
|
||||
- [WebUI 远程访问](../guides/webui-remote-access.zh.md)
|
||||
- [Web 服务部署](../guides/web-server-deployment.zh.md)
|
||||
Reference in New Issue
Block a user