Files
HomeAgent/cmd/ohos
JianFeeeee 02cc74ce11 fix(proc): 子进程崩溃自愈 + 集中台账 + 注册面摘除
根因:子进程插件被 kill 后,内核只发了一个无人订阅的事件,
工具/stage handler/IO 通道全留在注册表里指向死进程,
模型继续调用只吃 ErrProcessExited,没有任何路径把插件拉回来。

## 四层修复

### 1. 专职 waitLoop(进程收割)
- 每个子进程配一根 waitLoop goroutine,是 cmd.Wait() 的唯一调用点
- 不再依赖 stdout EOF 判定死亡(孙子进程继承 stdout 时 EOF 永不到来)
- 手工 os.Pipe 替代 cmd.StdinPipe/StdoutPipe,避免 waitLoop 与
  os/exec 的内部关闭竞争
- host.go: Host.Supervisor(),Host.Close() 先 StopAll 再拆段

### 2. 集中台账 Supervisor
- proc/supervisor.go: 插件 Spawn 握手成功即 track,进程退出即 untrack
- StopAll: 并发发 plugin.stop 走优雅路径,到期仍在的一律 Kill
- 关停后才完成握手的进程被立即结束,不会活过内核
- 消除「孤儿进程持共享段映射 → SIGBUS」的隐患

### 3. 注册面摘除(detachPlugin)
- 新增 StageHost.UnregisterPluginStages:摘除指定插件的全部 stage handler
- 新增 Registry.pluginChannels 台账:记录每个插件注册的 IO 通道
- 三条路径统一走 detachPlugin:Disable / ReloadOne / RemovePlugin
- StopAndUnload 漏了 IO 通道也一并补上

### 4. 自动重启
- onProcCrash 从「只发事件」改为「摘注册面 → 从注册表移除 → 异步排重启」
- scheduleProcRestart: 窗口 5 分钟内最多 3 次,线性退避 1s/2s/3s
- 超限停手留日志;重启前复核是否已被 Disable 或被其他路径加载
- 崩溃计数窗口过期自动归零

### 5. 主动停止 vs 崩溃的区分
- proc.Plugin 新增 stopping 标志:Stop()/Close() 里 Set(true)
- handleExit 读 stopping 标志,主动停止不上报 onCrash
- 防止重载/禁用/卸载被误判为崩溃触发多余重启

### 6. Linux Pdeathsig 兜底
- procattr_linux.go: SysProcAttr.Pdeathsig = SIGKILL
- 兜 homed 自身被 SIGKILL/OOM 时子进程变孤儿的场景
- macOS/Windows 无等价物,空实现

### 7. pluginmgr 升级
- PluginManager 接口新增 PluginRuntime / ListPluginRuntimes
- plugin_list 输出运行态:loaded / alive / pid / crash_count / channel
- 新增 plugin_status: 全量运行期快照 + dead/unhealthy 汇总
- 新增 plugin_restart: 无条件重启单个插件(plgreload 不动未改二进制的插件)

### 测试
- process_test.go: 3 例(grandchild stdout 感知 / Supervisor track-untrack /
  StopAll 无孤儿)
- crash_recovery_test.go: 8 例(detach 三项齐全 / 通道重注册 / 崩溃不阻塞 /
  退避阈值 / 窗口过期 / 关停中跳过 / PluginRuntime 通道识别)
- stages_plugin_test.go: 4 例(stage 按插件摘除 / 空 stage 清理 / 空名 no-op /
  工具+stage 双摘后可重新注册同名)
2026-09-03 12:37:58 +08:00
..

HomeAgent 鸿蒙客户端

HarmonyOS / OpenHarmony 原生客户端,用 ArkTS + ArkUI 实现(不是 WebView 套壳)。 功能与 WebUI 对齐SSE 流式对话、工具调用卡片、思考过程折叠、附件上传预览、 设备桥、插件管理、设置编辑、宽屏双栏、深浅色主题。

工程结构

HomeAgent/
├── AppScope/                     应用级配置与图标
├── entry/src/main/
│   ├── ets/
│   │   ├── common/               通信与全局状态
│   │   │   ├── ApiClient.ets     REST 客户端X-API-Key 鉴权、超时、二进制附件)
│   │   │   ├── SseClient.ets     SSE 长连接Last-Event-ID 断线续传)
│   │   │   ├── DeviceBridge.ets  设备桥:把本机能力暴露给 agent
│   │   │   ├── BridgeRouter.ets  桥请求路由
│   │   │   ├── BridgeCaps.ets    能力声明
│   │   │   ├── ConnStore.ets     连接配置持久化
│   │   │   ├── StatusStore.ets   运行状态缓存
│   │   │   ├── NavBarController.ets / NavStackRegistry.ets  导航
│   │   │   ├── Constants.ets     主题色板、圆角、超时、分页大小
│   │   │   └── UserError.ets     错误转人类可读文案
│   │   ├── components/           可复用组件
│   │   │   ├── MarkdownView.ets      流式 Markdown增量渲染
│   │   │   ├── StaticMarkdown.ets    静态 Markdown历史消息一次成型
│   │   │   ├── Attachment.ets        附件卡片 + 详情
│   │   │   ├── StatusCards.ets       状态卡片
│   │   │   ├── SettingsEditor.ets    配置编辑器
│   │   │   ├── PageTopBar.ets        顶栏 + 悬浮按钮
│   │   │   ├── SubPage.ets           二级页容器
│   │   │   └── GradientBackground.ets
│   │   ├── model/Model.ets       共享类型定义
│   │   ├── pages/                页面
│   │   │   ├── Index.ets         Tab 容器(入口)
│   │   │   ├── ChatPage.ets      对话
│   │   │   ├── DevicePage.ets    设备
│   │   │   ├── PluginsPage.ets   插件
│   │   │   └── SettingsPage.ets  设置
│   │   └── entryability/EntryAbility.ets
│   ├── module.json5              权限、能力声明
│   └── resources/                字符串、颜色、图标、页面路由表
├── build-profile.json5.example   构建/签名配置模板(复制后填本机签名材料)
└── oh-package.json5              依赖

编译

需要 DevEco Studio 或 command-line-tools。 本工程用 compatibleSdkVersion 6.1.1(24) / compileSdkVersion 26.0.0

  1. 准备签名配置build-profile.json5 含密码明文,未入库):

    cd cmd/ohos/HomeAgent
    cp build-profile.json5.example build-profile.json5
    

    REPLACE_WITH_YOUR_* 换成本机 DevEco 生成的调试签名材料, 默认在 ~/.ohos/config/ 下(.cer / .p7b / .p12 三件套 + 两个密码)。 用 DevEco Studio 打开工程会自动生成,命令行可参考 deveco-cli 生成签名材料。

  2. 构建 HAP

    # hvigorw 未入库(本机是符号链接),直接用 command-line-tools 里的
    /path/to/command-line-tools/bin/hvigorw \
      --mode module -p module=entry@default assembleHap --no-daemon
    

    产物在 entry/build/default/outputs/default/entry-default-signed.hap

  3. 安装到设备

    hdc install entry/build/default/outputs/default/entry-default-signed.hap
    

连接 homed

首次启动在「设置」里填:

  • 服务地址http://<homed 主机>:8080WebUI 插件监听端口)
  • API Keyhomed 的 plugin.webui.api_key

客户端所有请求走 <服务地址>/api/v1/*,带 X-API-Key 头。 附件路径 /files/ /uploads/ 不带 /api/v1 前缀,同样携带鉴权头。

设备桥需要 homed 启用 remotedevice 插件(默认 9890 在「设备」页填 ws token 后本机能力即可被 agent 调用。

注意事项

  • 聊天历史分页:首屏只拉最新 CHAT_PAGE_SIZE40向上滚动触顶自动加载更早的。 服务端 /chat/history 支持 limit / before 游标;工具调用详情与思考内容完整下发不裁剪。
  • 修改主题色:改 common/Constants.etsDARK_PALETTE / LIGHT_PALETTE,全局生效。
  • 新增页面:同时在 resources/base/profile/main_pages.json 注册,且只有入口页带 @Entry
  • 项目代码部分由 AI 辅助生成,改动请自行评估。