# Tauri 外壳（app/src-tauri/）

OpenHuman 的桌面宿主：Tauri v2 + WebView、IPC 命令、窗口管理，以及桥接到嵌入式的 `openhuman-core` Rust 运行时（核心 JSON-RPC）。它 **不** 重复完整的领域栈；那部分位于仓库根目录的 Rust crate（`openhuman_core`, `src/main.rs`).

## 职责

1. **Web UI**。从 `app/dist` （或端口 1420 上的开发服务器）加载 Vite 构建。
2. **IPC**。暴露一小组明确的 Tauri 命令（见 [命令](#tauri-ipc-commands-app-src-tauri)).
3. **核心生命周期**。启动进程内核心服务器，并通过 `core_rpc_relay`.
4. **磁盘上的 AI 提示词**。解析已打包的 `src/openhuman/agent/prompts` ，从 resources / 开发 cwd 中供 `ai_get_config` / `write_ai_config_file`.
5. **窗口 + 托盘**。桌面窗口行为和系统托盘（见 `lib.rs`).

## 核心进程模型

`app/package.json` `core:stage` 有意设计为无操作，仅为保持脚本兼容性而保留。桌面应用在进程内链接核心，因此本地构建不再需要一个已暂存的 `openhuman-core-*` sidecar，位于 `app/src-tauri/binaries/`.

## 卡住进程恢复

正常退出应用会从 `RunEvent::ExitRequested`执行清理：在 CEF 关闭前先关闭子 webview，触发嵌入式核心的取消令牌，最终进程清扫会向 `SIGTERM` 发送给直接子进程，然后在短暂宽限期后将仍然挂起的进程升级为 `SIGKILL` 。清扫摘要会记录为 `[app] sweep: term=N kill=M total=K`；任何非零的 `kill` 计数都是警告，意味着某个子进程忽略了优雅关闭。

在 macOS 上，强制退出（Force Quit、 `SIGKILL`、渲染器崩溃）可能会跳过正常清理。下次启动会在 CEF 缓存预检查之前运行启动恢复：它会列出可执行路径属于启动中的 `.app/Contents`的 OpenHuman 进程，跳过当前进程，发送 `SIGTERM`，短暂等待，然后 `SIGKILL`仍与相同 pid+command 匹配的掉队进程。日志使用 `[startup-recovery]` 前缀。

在以下情况下会跳过启动恢复： `OPENHUMAN_CORE_REUSE_EXISTING=1` 已设置（这样手动 CLI 核心复用仍可工作），以及当 CEF 的 `SingletonLock` 被一个存活进程持有时（这样正常的第二实例路径可以失败，而不会杀掉已在运行的应用）。Tauri 命令 `process_diagnostics_list_owned` 返回当前拥有的进程列表；macOS 实现按 bundle 作用域划分，Linux/Windows 当前返回空。

## Tauri shell 架构（`app/src-tauri/`)

### 概述

该 **`app/src-tauri`** crate（Rust 包 **`OpenHuman`**，二进制文件 **`OpenHuman`**）是一个 **仅桌面端** 宿主。它嵌入 React UI，注册插件（深链、opener、OS、通知、自动启动、更新器），管理主窗口和托盘，并且 **转发 JSON-RPC** 到嵌入式核心服务器。

非桌面目标会在编译时报错（`compile_error!` 位于 `lib.rs`).

### 目录布局（实际）

```
app/src-tauri/src/
├── lib.rs                 # `run()`、托盘/菜单操作、插件、`generate_handler!`、核心启动
├── main.rs                # 二进制入口
├── core_process.rs        # CoreProcessHandle、嵌入式核心服务器任务
├── core_rpc.rs            # 到核心 JSON-RPC 的 HTTP 客户端
├── commands/
│   ├── mod.rs             # 重新导出
│   ├── core_relay.rs      # `core_rpc_relay`、服务管理的核心引导
│   ├── openhuman.rs       # 守护进程宿主配置、类 systemd 服务帮助器
│   └── window.rs          # 显示/隐藏/最小化/关闭窗口
└── utils/
    ├── mod.rs
    └── dev_paths.rs       # 解析已打包的 AI 提示词路径
```

这里 **没有** `src-tauri/src/services/session_service.rs` 在这个目录树中；会话语义在适用情况下由 Web 层 + 后端 + 核心处理。

### 数据流：UI → core

```
React（invoke）
    → core_rpc_relay { method, params, serviceManaged? }
        → core_rpc::call HTTP POST 到 OPENHUMAN_CORE_RPC_URL
            → 嵌入式 openhuman core 服务器
```

`CoreProcessHandle` 位于 `core_process.rs` 拥有嵌入式服务器任务； `commands/core_relay.rs` 可选地确保一个 **服务管理的** 核心在转发前已运行。

### 窗口和托盘行为

* shell 在启动时创建托盘图标，并将操作绑定为打开主窗口或退出。
* 在守护进程模式下（`daemon` / `--daemon`），主窗口在启动时隐藏，并且可以从托盘操作中重新打开。
* 在 macOS 上 `RunEvent::Reopen` 也会恢复并聚焦主窗口。
* Windows 和 Linux 使用相同的托盘操作（`打开 OpenHuman`, `退出`），在某些 Linux 设置上托盘渲染会因桌面环境不同而有所差异。

### 已打包资源

`tauri.conf.json` 打包了 **`../../skills/skills`** 以及 **`../../src/openhuman/agent/prompts`** ，因此 skills 和提示词 markdown 会随应用一起发布。

### 相关

* IPC 接口：见 [命令](#tauri-ipc-commands-app-src-tauri) 下方章节
* HTTP 桥接：见 [核心桥接与帮助器](#core-bridge-helpers-app-src-tauri) 下方章节
* Rust 领域（实现）：仓库根目录 `src/openhuman/`, `src/core_server/`

## Tauri IPC 命令（`app/src-tauri`)

所有命令都注册在 **`app/src-tauri/src/lib.rs`** 中的 `tauri::generate_handler![...]` （桌面构建）里。下面的名称是 **Rust** 命令名（在适用情况下通过 serde 在 JS 中为 camelCase）。

### 演示 / 诊断

| 命令      | 用途                |
| ------- | ----------------- |
| `greet` | 演示字符串（生产环境中可安全删除） |

### AI 配置（已打包提示词）

| 命令                     | 用途                                                                                    |
| ---------------------- | ------------------------------------------------------------------------------------- |
| `ai_get_config`        | 构建 `AIPreview` 从已解析的 `SOUL.md` / `TOOLS.md` 位于已打包或开发环境下 `src/openhuman/agent/prompts` |
| `ai_refresh_config`    | 与 `ai_get_config` 相同的读取路径（刷新钩子）                                                       |
| `write_ai_config_file` | 写入单个 `.md` 到仓库下 `src/openhuman/agent/prompts` （开发 / 安全文件名检查）                          |

### 核心 JSON-RPC 转发

| 命令               | 用途                                                                                                   |
| ---------------- | ---------------------------------------------------------------------------------------------------- |
| `core_rpc_relay` | 主体： `{ method, params?, serviceManaged? }` → 转发到本地 **`openhuman-core`** HTTP JSON-RPC（`core_rpc.rs`) |

使用 **`app/src/services/coreRpcClient.ts`** (`callCoreRpc`）从前端调用。

### 窗口管理

来自 **`commands/window.rs`** （名称可能略有不同；见 `lib.rs`):

| 命令                  | 用途      |
| ------------------- | ------- |
| `show_window`       | 显示主窗口   |
| `hide_window`       | 隐藏主窗口   |
| `toggle_window`     | 切换可见性   |
| `is_window_visible` | 查询可见性   |
| `minimize_window`   | 最小化     |
| `maximize_window`   | 最大化     |
| `close_window`      | 关闭      |
| `set_window_title`  | 设置标题字符串 |

### OpenHuman 守护进程 / 服务帮助器

来自 **`commands/openhuman.rs`** （准确负载请参见源代码）：

| 命令                                 | 用途                 |
| ---------------------------------- | ------------------ |
| `openhuman_get_daemon_host_config` | 读取守护进程宿主偏好设置（例如托盘） |
| `openhuman_set_daemon_host_config` | 持久化守护进程宿主偏好设置      |
| `openhuman_service_install`        | 安装后台服务（平台相关）       |
| `openhuman_service_start`          | 启动服务               |
| `openhuman_service_stop`           | 停止服务               |
| `openhuman_service_status`         | 查询状态               |
| `openhuman_service_uninstall`      | 卸载服务               |

### 屏幕共享选择器（CEF / macOS）

来自 **`screen_capture/mod.rs`**。为页面内的 `getDisplayMedia` shim 提供支持，位于 `webview_accounts/runtime.js`。受会话门控：shim 必须在有效用户手势后先打开一个会话，枚举 / 缩略图捕获才能成功。参见 issue #713（选择器 UX）+ #812（会话门控）。

| 命令                              | 用途                                                                                                           |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `screen_share_begin_session`    | 在账户 webview 中于一次 `navigator.userActivation.isActive` 手势之后打开一个 30 秒会话。返回 `{ token, sources }`。每个账户每分钟限制 10 次。 |
| `screen_share_thumbnail`        | 将单个源的缩略图捕获为 base64 PNG。需要有效 token 和一个 `id` ，且该 id 必须是此会话签发时对应的。仅支持 macOS；其他平台返回错误。                           |
| `screen_share_finalize_session` | 关闭会话。由 shim 在 Share 或 Cancel 时调用；对未知/过期 token 调用也是安全的（无操作）。                                                  |

### 工作区文件链接

来自 **`workspace_paths.rs`** （关闭 `#1402`）。这些命令只接受相对于工作区的路径。shell 会根据当前活动的 OpenHuman 工作区解析每个路径，将目标规范化，并在打开或读取任何内容之前拒绝路径遍历、绝对路径、类似 URI 的前缀以及符号链接逃逸。

| 命令                       | 用途                          |
| ------------------------ | --------------------------- |
| `open_workspace_path`    | 使用操作系统默认应用打开现有工作区文件或目录。     |
| `reveal_workspace_path`  | 在操作系统文件管理器中显示现有工作区文件或目录。    |
| `preview_workspace_text` | 从现有工作区文件中读取有上限的 UTF-8 文本预览。 |

### 已移除 / 不存在

以下内容 **不** 存在于当前的 `generate_handler!` 列表中： `exchange_token`, `get_auth_state`, `socket_connect`, `start_telegram_login`。认证和 socket 由 **React** 应用和 **core** 进程处理，而不是通过这些 IPC 名称。

### 示例：core RPC

```typescript
import { invoke } from "@tauri-apps/api/core";

const result = await invoke("core_rpc_relay", {
  request: {
    method: "your.rpc.method",
    params: { foo: "bar" },
    serviceManaged: false,
  },
});
```

***

*参见 `app/src-tauri/src/lib.rs` 以获取权威列表。*

## 核心桥接与帮助器（`app/src-tauri`)

本文档取代了旧的“SessionService / SocketService”拆分。Tauri crate **不** 嵌入重复的 Socket.io 服务器或 Telegram 客户端；相反，它专注于 **进程管理** 以及 **HTTP JSON-RPC** 到 **`openhuman-core`** 二进制文件。

### `CoreProcessHandle` (`core_process.rs`)

* 解析 **`openhuman-core`** 可执行文件（暂存于 `binaries/` 或 `PATH`  / 开发布局下）。
* 启动或附加到核心进程，并公开其 RPC URL（`OPENHUMAN_CORE_RPC_URL`).
* 在应用设置期间用于 `lib.rs` (`app.manage(core_handle)`).

### `core_rpc` (`core_rpc.rs`)

* 用于核心 JSON-RPC 接口（localhost）的 HTTP 客户端。
* 由 **`core_rpc_relay`** 使用以转发 `method` + `params` 来自前端。

### `commands/core_relay.rs`

* **`core_rpc_relay`**。确保核心正在运行（进程内句柄或 **服务管理的** 路径），然后调用 `core_rpc`.
* **`ensure_service_managed_core_running`**。当 RPC 不可用时引导类 systemd/launchd 服务（特定于平台的行为位于核心 CLI 内部）。

### `commands/openhuman.rs`

* 位于应用数据目录下的守护进程宿主 JSON 配置（例如托盘可见性）。
* 用于 **openhuman** 后台服务的安装/启动/停止/状态/卸载帮助器。

### `utils/dev_paths.rs`

* 解析 **`src/openhuman/agent/prompts`** 用于开发环境以及 AI 预览的已打包资源路径。

### `utils/tauriSocket.ts` （前端）

不在 `src-tauri`中，但 **配合** shell：当使用 Rust 侧客户端时，React 应用会监听镜像 socket 活动的 Tauri 事件。参见 `app/src/utils/tauriSocket.ts` 以及 [前端服务](/openhuman/zh/kai-fa/architecture/frontend.md#services-layer) 章节。

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tinyhumans.gitbook.io/openhuman/zh/kai-fa/architecture/tauri-shell.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
