Files
HomeAgent/docs/zh/plan.md
root bc9ef15eb0 三层回退恢复机制(L0写前留档/L1恢复梯子/L2离线回滚)+ guard 父守护
- L0: files 插件写受保护系统路径(/etc 等)前自动留档,AbstractBeforeWrite 到 data/file_baseline
- L1: failback 受限 worker 执行恢复梯子 probe→还原DNS/proxy→还原LLM配置+ReloadFromConfig→probe,N轮有界
- L2: tracker changeset 持久化原文 blob,guard 离线 RollbackFromDisk 回滚 agentfs;SystemSnapshot 支撑
- guard 父守护: 心跳 IPC(PING/ACK unix socket, 文件心跳回退)、失败计数、退出码协议(42/43/44)、最后手段
- 发行版路径适配: system.protected_paths/network_paths 可注入,默认面向主流 Linux
- Windows 兼容: guard.go/failback.go 加 //go:build linux, guard_windows.go 提供 no-op 桩
- 修复: guard.yaml last_resort 键冲突、changeset Content 不落盘导致离线回滚丢原文

Build 全绿, vet 干净, system/recovery/ipc/tracker 单元测试全过
2026-08-05 16:00:08 +08:00

285 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WebUI 布局与配置归位修复计划
## 一、背景
上一轮 SDK 接口化改造完成并部署后,用户指出三个问题:
1. **WebUI 窄屏布局损坏**:顶部 `<nav>` 为桌面式横排(标题 + 6 tab + 连接指示器 + 语言 + 主题),
窄屏断点仅缩小字号不换行,`body { overflow-x:hidden }` 直接把溢出的 tab 裁掉不可点击。
2. **OpenClaw skills 目录被注册为核心配置**`core.skills.path`"OpenClaw 技能存储目录")注册在核心
配置表(`internal/config/registry.go`),但全仓无任何读取方(死配置);实际生效路径是
clawhubadapter 自己的 `skills_dir` 配置(`config_clawhubadapter` 表 + `core.daemon.data_dir`/skills 兜底)。
技能目录是 clawhubadapter 适配加载的领域,不应属于核心配置。
3. **clawhubadapter 加载的微信插件成为独立配置项**:设置页出现 `channels.wechat.*`(核心表)、
`plugin.wechat.*`config_wechat 表)、`config_openclaw_weixin`(空表)等多处微信配置,
全部为历史残留——当前代码零引用OC 技能的配置实际在其自身 `~/.openclaw/openclaw.json`
设置页会把核心表全部键 + 全部插件表当作配置组展示,导致残留以"独立配置项"形态出现。
## 二、修复计划
| # | 动作 | 位置 | 风险 |
|---|------|------|------|
| A | 窄屏导航修复:<768px nav 横向滚动h1 缩写连接指示器简化body 溢出裁切改为 nav 内滚动 | `cmd/gui/renderer/style.css` | |
| B | 删除 `core.skills.path` 核心配置注册set + RegisterDef 两处 | `internal/config/registry.go` | 无读取方 |
| C | 备份后清理残留配置`channels.wechat.*` `config_wechat` / `config_openclaw_weixin` / `config_openclaw` 含微信 token先备份 | 生产库 `/home/newqqagent/config.db` | 当前代码不读 |
## 三、实施记录
### 步骤 Awebui 窄屏导航修复(已完成)
- 修复对象为 webui HTTP 服务真正前端 `internal/plugins/webui/dashboard.html``go:embed` 内嵌
登录后 `/` 返回104KBcmd/gui 是独立 electron 客户端 webui 一部分)。
- `<768px` 断点`nav { overflow-x:auto; scrollbar-width:none; flex-wrap:nowrap }` + `::-webkit-scrollbar { display:none }`
`nav a { white-space:nowrap; flex-shrink:0 }``nav h1 { font-size:0 }`保留 logo 隐藏文字弥补窄屏空间
`nav > div { flex-shrink:0 }` 右侧语言/主题/退出按钮不压缩
- 顺带在 cmd/guielectron 客户端同步了窄屏样式与消息来源徽标`app.js`/`style.css`客户端窗口缩放同样受益
客户端需另行构建 electron 应用才生效)。
- 验证部署后 `/` 返回的 dashboard `scrollbar-width:none`/`font-size:0`/`::-webkit-scrollbar` 规则
### 步骤 B删除 core.skills.path 核心配置(已完成)
- 删除 `internal/config/registry.go` 两处`set("core.skills.path", ...)`SeedDefaults
`reg(ConfigDef{Key:"core.skills.path", ...})`定义注册)。
- 理由该键全仓无读取方grep 仅命中注册处实际生效路径是 clawhubadapter `skills_dir`
config_clawhubadapter + `core.daemon.data_dir`/skills 兜底)。技能目录属 clawhubadapter 适配领域
- 验证`grep -rn "skills.path" --include="*.go"` 零命中部署后设置页无 `core.skills.path`
### 步骤 C清理生产库残留配置已完成
- 操作前 `sqlite3 .backup /tmp/opencode/config.db.pre-clean.bak`含微信 token 数据)。
- 删除`config` `channels.wechat.*` 3 + `core.skills.path` `DROP TABLE config_wechat /
config_openclaw_weixin / config_openclaw`三者均为历史残留当前代码零引用clawhubadapter 实际
使用 config_clawhubadapter 表OC 技能配置在其自身 `~/.openclaw/openclaw.json`)。
- 验证:设置页总键数 138→129无 wechat/weixin/skills.path 残留,`plugin.clawhubadapter.skills_dir /
simulator_dir` 正常;服务 healthcheck ready、clawhubadapter "OC plugin manager started"。
---
# SDK Stop 注册接口RegisterStopHandler计划
## 一、背景
2026-08-01 20:00 起生产 homeagent 进入崩溃循环(`fatal error: thread exhaustion`
systemd 重启计数 61+)。排查定位为 SDK 示例插件 `calendar`(示例源码在 SDK 仓库
`example/calendar`,生产以 plugin.so 形态加载)三个缺陷叠加:
1. **农历引擎 3 个 bug**`daysInLunarYear` 位循环 `i > 0` 应 `i > 0x8`、缺闰月天数、
`lunarToSolar` 内层重复加闰月)→ `lunarToSolar(2026,4,12)` 返回 **2062-11-16**(偏移 36 年),
农历重复事件(`lunar_yearly`)的 next 被生成到遥远错误日期。
2. **`cleanupPastEvents` 保留过时重复事件** → 每 30s ticker 对已到点的重复事件再生成一份 next
事件从 7 个爆炸到 **45612 个**15MB events.json
3. **无提醒投递保护**15018 份同时到点的事件一次性 `go sdk.InjectInterruptText(...)` 投递
→ interrupt 风暴 → goroutine/线程耗尽。
处置:修复农历引擎 3 处 + next 去重 + 清理过时重复事件,用**新版 SDK 仓库 + 新版 plugindev 工具链**
重建 `calendar_linux_amd64.hmap`,经 **webui `POST /api/v1/plugins`**(透明代理到 pluginmgr 安装接口)
重装,重启验证收敛(事件 4 个、next 正确生成 2027-05-17、0 崩溃)。
**过程中暴露的能力缺口**SDK 只有 `Plugin` 接口的 `Name/Start/Stop`**没有 stop 注册接口**
`RegisterStopHandler`/`OnStop` 均不存在SDK v0.7.2/v0.8.0/master 一致)。插件停止时只能在自己的
`Stop()` 里写清理逻辑SDK 层无法统一执行"停止时清理"回调calendar 的 `Stop() { p.saveEvents() }`
还会用陈旧内存把已清理的数据写回磁盘(曾导致删除的重复事件复活)。
## 二、计划
| # | 动作 | 位置 | 风险 |
|---|------|------|------|
| 1 | 公共 SDK `PluginSDK` 加 `RegisterStopHandler(fn func())` + `RunStopHandlers()`(幂等、后注册先执行),两处同步 | `third_party/homeagent-sdk/sdk/plugin.go`、SDK 仓库 `sdk/plugin.go` | 低(纯新增,内置 SDK 内嵌透传) |
| 2 | 内核 Registry 保存每插件 SDK 引用(`sdkRefs``StopAll`/`ReloadOne`/`DisablePlugin` 调 `Stop()` 前执行 `RunStopHandlers` | `internal/plugin/registry.go` | 中(生命周期路径,需回归 reload/disable |
| 3 | 工具链 plugindevz_bridge 模板 `bridgeState` 存 SDK`StopPlugin` 先 `RunStopHandlers()` 再 `plugin.Stop()`init 脚手架模板加演示 | SDK 仓库 `tools/plugindev/templates.go`、`templates/main.go.tmpl` | 低 |
| 4 | 内置示例插件演示(如 timerticker 停止改为 stop handler | `internal/plugins/timer/plugin.go` | 低 |
| 5 | 外部示例插件同步(`example/calendar` 的 `saveEvents` 改由 stop handler 执行,验证 z_bridge 链路;其余 example 加演示) | SDK 仓库 `example/*` | 低 |
| 6 | 文档同步SDK README 生命周期章节 + 主仓插件开发文档 | SDK 仓库 `README.md`/`README_EN.md` 等 | 无 |
## 三、实施记录
1. SDK 公共层(`RegisterStopHandler` + `RunStopHandlers`:后注册先执行、执行后清空幂等)已落地
`third_party/homeagent-sdk/sdk/plugin.go`,并同步到 SDK 仓库 `/tmp/opencode/sdk-repo/sdk/plugin.go`(两处一致)。
2. 内核 Registry`internal/plugin/registry.go`)新增 `sdkRefs map[string]*sdk.PluginSDK` + `runStopHandlers`
`loadOne` 注册、`StopAll`/`ReloadOne`/`DisablePlugin` 在 `Stop()` 前执行(共 4 处调用点)。
3. 工具链 plugindevSDK 仓库linux `tmplLinuxBridge` 的 `go_stop_plugin` 先 `RunStopHandlers()` 再 `plg.Stop()`
windows `tmplBridge` 的 `bridgeState` 加 `sdk` 字段、`StopPlugin` 同链路;`tmplPluginGo` + `main.go.tmpl`
脚手架加 `RegisterStopHandler` 演示。plugindev 重新编译通过GOPATH=/root/go
4. 内置 timer 插件演示:`close(p.stopCh)` 移入 stop handler`Stop()` 只 `wg.Wait()`。
5. 外部示例:`example/calendar` 的 `saveEvents` 改为 `s.RegisterStopHandler(p.saveEvents)`
`Stop()` 删除写盘调用(持久化交由 stop handler避免陈旧内存复活已删事件
6. 文档SDK 仓库 `README.md`/`README_EN.md` 生命周期章节补充 RegisterStopHandler 说明。
7. 构建测试:主仓 `go build ./...` + `go test ./internal/sdk/... ./internal/plugin/...` 全绿;
SDK 仓库 `go build ./...` + `go test ./sdk/...` 全绿。
8. 生产部署验证:新 plugindev--no-bundle重建 `calendar_linux_amd64.hmap`md5 084e97c0…strings 确认
`go_stop_plugin → RunStopHandlers → saveEvents` 编译进 plugin.sowebui API 删旧装新;重装新内核
homed含 sdkRefs/runStopHandlers两次重启事件稳定 3 个不复活、events.json mtime 与 stop 时刻吻合
saveEvents 经 stop handler 真实执行、0 次 thread exhaustion、服务 active。
---
# clawhubadapter OpenClaw 通道插件兼容修复计划
## 一、背景
生产 `core.llm.provider` 已是 mock LLM 源(`core.llm.sources.mocktest`base_url
`http://127.0.0.1:18080/v1`、model mock-model、adapter openaimock LLM 服务常驻运行。
借助 **mock 通道插件**`/tmp/opencode/mock-skills/mock-wechat/`,完全复刻 openclaw-weixin 的
真实注册格式 `register(api) → api.registerChannel({ plugin: ChannelPlugin })`)放入生产 skills 目录
端到端复现,得出如下结论:
**已验证可用链路**manager 加载 mock 插件 → Go 端识别 `ocplugin mock-wechat handled by manager` →
注册工具 `mock-wechat_read_mock_wechat_input`/`mock-wechat_mock_echo` → `RegisterOutputChannel("mock-wechat")`
→ mock 自推消息经 `channel_input` 通知 → `[agent] interrupt from manager/mock-wechat` →
mock LLM 正常回复195ms
**复现的核心缺陷**(真实通道插件 wechat/dingding"根本不可用"的根因):
1. **输出断链**manager `tools/call` 通道分支只认 `channelPlugin.outbound.sendText/sendMedia`
(旧格式),真实 ChannelPluginopenclaw-weixin 等)无 outbound →
`tools/call mock-wechat → error: "channel mock-wechat has no output handler"`。
2. **生命周期静止**manager mock api 从不调用 `gateway.startAccount/stopAccount`,也无
`api.runtime`/`channelRuntime` → 通道插件加载后永不启动(不登录、不轮询、不收消息)。
3. **输入依赖错位**:真实插件把消息经 `channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher`
推送manager 完全无此对象),而不是调 `api.submitInput`。
4. **stdout 污染**:插件 `console.log` 直接进 JSON-RPC 流Go 端 readLoop 跳过非 JSON 行,有丢通知风险。
**wechat 通道的心跳机制**`openclaw-weixin/dist/index.js` `pollLoop`448 行起):每账号一个常驻
`pollLoop`,循环 `POST ilink/bot/getupdates`body `{get_updates_buf}`,超时 35s——**长轮询即心跳**
服务器收到 poll 请求即知通道在线,新消息随 poll 响应 push 回来;超时视为空响应继续轮询,真错误延时
5s 重试。**与 gateway 生命周期强绑定**
- `pollLoop` 只由 `gateway.startAccount(ctx)` 启动;不被调用 → 心跳/收消息全断(服务器侧认为通道离线)。
- `startAccount` 末尾 `await new Promise(()=>{})` **永久挂起**——OC gateway 靠它配合 health-monitor
startAccount 退出 → 判定账号崩溃 → 重启账号。manager 调 `startAccount` 必须 **fire-and-forget**。
- 停靠 `gateway.stopAccount(ctx)``ctx.account.accountId` 定位),停止时经 `statusSinks` 调
`ctx.setStatus({running:false, connected:false, lastStopAt})`;启动即上报
`ctx.getStatus()/ctx.setStatus({...running:true, connected:true, lastStartAt})`——`connected` 是
gateway 判断账号存活的依据。
- `sendTyping`ilink/bot/sendtyping + typing_ticket是打字指示非心跳无需支持。
## 二、修复计划
| # | 动作 | 位置 | 风险 |
|---|------|------|------|
| A | manager 提供 **gateway 生命周期桥**channel 插件注册后自动 `gateway.startAccount(ctx)`fire-and-forget不等待挂起的 Promise构造完整 ctx `{account, cfg, channelRuntime, getStatus, setStatus}`Go 端 `channel_stop` 通知 → `stopAccount` | `internal/plugins/clawhubadapter/manager/main.js` | 中 |
| B | 实现 **channelRuntime mock**`reply.dispatchReplyWithBufferedBlockDispatcher`deliver 回调 → `channel_output` 通知送 Go 端)、`getPolls`OC 通用通道轮询输入)、`call` 透传 | 同上 | 中 |
| C | `tools/call` 通道分支改造:无 `outbound` 的 ChannelPlugin 改走 channelRuntime 事件式发送agent 输出 → deliver不再报 "no output handler" | 同上 | 低 |
| D | 状态上报透传:`setStatus` 经 `channel_status` 通知 → Go 端可查health-monitor 语义startAccount 保持挂起) | 同上 | 低 |
| E | stdout 卫生:插件 `console.log` 重定向 stderr或 JSON-RPC 流感知封装),杜绝污染 | 同上 | 低 |
| F | Go 端:`channel_output`/`channel_status` 通知接入(事件分发),通道输出 handler 保持 `sp.CallTool` | `internal/plugins/clawhubadapter/registry.go`、`plugin.go` | 中 |
| G | 端到端验证mock 通道插件 + mock LLM生产环境临时放入/移出 skills 目录)复跑全链路(登录启动→收消息→回复→出站→停止) | 生产 | 低 |
## 三、实施记录
(逐步填写)
1. **manager/main.js — 通道运行时与生命周期桥(已完成,独立运行验证)**
- `makeChannelRuntime(chName, ch)``reply.dispatchReplyWithBufferedBlockDispatcher(opts)` 提取
`dispatcherOptions.deliver`/`typingCallbacks` 按 `ctx.AccountId` 挂到 `ch.deliverers`,随后
`notify('channel_input', {channel, payload:{content: BodyForAgent||Body, from, sessionKey, accountId,
messageSid, chatType, raw}})` 入站;返回 dispatchersendNow/addToBuffer/sendBuffer/closeBuffer
`chatPolls`/`getPolls` 空转(防断连误判)、`call` 转发 `channel_output` 通知。
- `startChannels(name)`channel 插件注册后自动枚举账号(`config.listAccountIds`→`resolveAccount`
缺省 `['default']`),构造完整 ctx `{account, cfg, channelRuntime, getStatus, setStatus}`
**fire-and-forget** 调 `gateway.startAccount`(真实插件会永久挂起,绝不等待);崩溃/状态变更经
`channel_status` 通知透传health-monitor 语义startAccount 不退出=账号存活)。
- `stopChannels(name)`:逐个账号 `gateway.stopAccount`;进程 SIGTERM/SIGINT 时统一执行优雅停靠。
- `tools/call` 通道分支:保留 outbound旧格式→ 新增 **deliver 事件式发送**
`deliverItem` 按 accountId 取 deliver + typingCallbacks.onReplyStart/onCleanup 包裹)→
无 deliver 时降级 `channel_output` 通知 → 兜底报错。不再出现 "has no output handler"。
- **stdout 卫生**:全局 `console.log` 重定向 stderrJSON-RPC 流仅承载协议帧。
2. **mock 插件升级(/tmp/opencode/mock-skills/mock-wechat/index.js**:完全复刻真实 weixin 行为——
`gateway.startAccount` 永久挂起 + `setStatus` 上报 + `setInterval` 心跳轮询 + 800ms 后经
`dispatchReplyWithBufferedBlockDispatcher` 推送入站deliver 本地记录发送);`stopAccount` 停轮询+状态置否。
3. **manager 独立运行验证(通过)**`channel_status` 启动上报running=true connected=true
`tools/call mock-wechat` → `{"status":"sent","via":"channelRuntime.deliver"}`,插件 deliver 收到
`text="hello from agent"` 且 typing onReplyStart/onCleanup 正确包裹;心跳 poll #1-4 常驻;
SIGTERM → `stopAccount called` 退出码 0插件 console 输出全部走 stderr协议流零污染
4. **Go 端通知接入plugin.go translateAndRegister + registry.go 状态缓存)**`channel_status` 存
`channelStatus` map可查+ 日志;`channel_output` 降级事件日志。`go build ./...` 通过。
5. **回复闭环修复(同步注入)**`channel_input → InjectInterruptText` 的 InputEvent 不带 ResponseCh
internal/agent/io/channel.go:276agent 回复在 emitResponseeventloop.go:384被静默丢弃。
改为 `s.InjectInputSync(pluginName, channel, "text", payload)`(内部 SDK 已有 4 参版本,返回
`*OutputEvent`)同步等待回复 → 提取 `Payload["content"]` → `sp.CallTool(channel, {payload, meta})`
→ manager `tools/call` → deliver → 插件发送 → 微信送达。公共 SDK IOInjector 同步补
`InjectInputSync(source, channel, text) string`ioAdapter 实现,供外部插件一致使用)。
6. **mock LLM 恒定文本化**/opt/llm-mock/mock_server.py `decide()` 删除工具调用分支,一律回文本
"无论收到什么消息都通过微信插件发送"),保证每条入站消息回复必然走通道输出。
7. **生产微信闭环验证(通过)**:用户微信发"你好..." → pollLoop 收到 → dispatchReply →
InjectInputSync → mock LLM 回文本 → CallTool(wechat) → deliver → `POST ilink/bot/sendmessage`
→ **status=200 message_id=7489545365740590088**,微信收到"mock已收到消息长度 324 字符。"
8. **通用性审查(无硬编码)**manager/plugin.go/registry.go 均无 weixin/wechat 特判,全部按 OC 规范
字段实现gateway/config/capabilities/channelRuntime/deliver/typingCallbacks。修正规范签名参数
约定:`listAccountIds(cfg)`、`resolveAccount(cfg, accountId)` 正确传 cfg。
9. **已知边界**非硬编码架构性a) `channelRuntime.getPolls/chatPolls` 返回空 msgs——依赖
runtime 轮询输入的通用通道型插件收不到消息weixin/dingding 类自带 pollLoop 的通道不受影响);
b) `gatewayMethods` 登录流程web.login.start/QR 扫码未实现CLI 有 stub通道凭 token
配置直连c) startAccount ctx 提供 account/channelRuntime/cfg/getStatus/setStatus 核心字段。
10. **补充边界 a) getPolls/chatPolls 消息源(已完成)**manager `makeChannelRuntime` 的
`chatPolls/getPolls` 改为读 `ch.pollQueues`(按 accountId 队列poll 取走即消费);新增
`channel/send` RPCGo 端注入 → 队列 → 插件轮询取走Go 端 `SendToChannel(channel, payload)`
+ `ChannelSender()` 单例Start 时置位。验证mock-poll 插件(纯 chatPolls 轮询型)——
`channel/send` → `{"status":"queued"}` → `[mock-poll] poll got msg` → dispatchReply →
`channel_input` 入站完整content/from/sessionKey/accountId/messageSid/chatType
微信链路回归正常Polling started + channel_status running=true
11. **边界 b) 登录流程核实(已解决,无需实现)**:真实登录机制是 **SKILL 脚本旁路**——
`weixin-openclaw-login` SKILL 的 `scripts/get-login-url.js`ilink 二维码 URL+
`poll-login-status.py`(轮询扫码状态)→ agent 经 exec 执行 → 拿 bot_token 写入
`~/.openclaw-weixin/account.json`2026-07-28 17:31 创建token 有效)→ 插件启动
`resolveAccountData` 直读。不依赖 manager gatewayMethodsweb.login.start 等 OC gateway
协议 stub 不影响真实使用)。
12. **clawhubadapter 全量管理接口(已完成并验证)**:向 agent 暴露完整插件/通道管理面——
- 新工具:`clawhubadapter_plugin_info`(类型/工具/关联通道详情)、`plugin_reload`reloadPlugin
`channel_list`(全部注册通道 + 实时状态)、`channel_send`SendToChannel 投递)、
`channel_start`/`channel_stop`manager `channel/start`、`channel/stop` RPC
- manager 新增 RPC`plugins/channels`registeredChannels 摘要含 status/accounts
`channel/start`fire-and-forget startChannels 恢复账号)、`channel/stop`stopAccount
按 channel 全停或按 accountId 单停,省略 channel 则全部停止)。
- Go 侧 `channelSummary(mgr)` 合并 manager 注册信息与 `channel_status` 实时缓存(缓存优先)。
- 端到端验证生产实例LLM 为本地 mock 源):微信发"你好通道..." → agent 执行
`channel_list` → `- wechat | plugin=openclaw-weixin type=text running=true connected=true
accounts=[default]` → 回复送达;发"注入..." → 执行 `channel_send` → "消息已投递到通道
wechat" → 回复送达。standalone manager 另验证 `channel/start`mock-wechat startAccount
重新执行、心跳恢复)与 `channel/stop`stopAccount called
13. **插件删除回调 onRemove 全套(已完成并验证)**`RegisterOnRemoveHandler`(仅卸载触发、
重载/禁用不触发,与 stop handler 互补——stop 每次停止都执行。registry.RemovePlugin 流程:
stop handlers → Stop → runOnRemoveHandlers → 移除 plugins/sdkRefs/instances →
UnregisterPluginTools → **配置清理**。配置清理含两层:`ConfigRegistry.RemovePlugin`
删除 defs 中 `plugin.<name>.*` 配置项定义 + DROP `config_<name>` 插件配置表
含用户设置值ListPlugins 基于 config_% 表枚举故配置区完全消失);已用临时程序验证
before: defs=1/plugins=[timer] → after: defs=0/plugins=[]。示例盘点SDK 仓库 15 个):
calendarevents.json、memomemos.json、rss订阅数据目录、weather缓存目录已加
filesfilesDir 为用户配置的访问根目录,默认 /、bili/qq用户下载资产
ocr函数内 defer RemoveAll 自清理按语义不加plugindev 模板 main.go.tmpl + README.tmpl
含 onRemove 演示SDK README/README_EN 生命周期文档补"删除清理onRemove"小节。
14. [2026-08-03] SDK 工具链/打包/重装 + dlclose 修复:
- 工具链源码位置澄清SDK 仓完整内容位于 third_party/homeagent-sdk主仓 .gitignore 仅跟踪
sdk/meta/go.mod"两个远程仓库各取所需"/tmp/opencode/sdk-repo 为工作克隆,远程=gitcode
- 工具链支持公共 IOInjector.InjectInputSyncCORE_INJECT_INPUT_SYNC=47C 桥 dispatchIO
callString 回传回复文本);主仓 cabi loader case 47 用内部 4 参版 InjectInputSync 取
OutputEvent.Payload["content"] setResultmeta.go ID 47 + loader.go主仓 3f252ed
- plugindev 构建环境GOMODCACHE=/root/go/pkg/modyaegi 缓存所在、GOPROXY=off。
- 工具链打包 memoplg.json BOM 去除、name_en "Memo/Notes"→"Memo"toSnake 不处理斜杠,
name_en 带 / 会使 hmap 名含子路径报错bundle=true 时走全平台交叉编译(本机无 darwin
工具链),打包用 `build --no-bundle --target linux/amd64`;产物 dist/memo_linux_amd64.hmap。
- 重装pluginmgr HTTP API127.0.0.1:9876DELETE /plugins/memo 卸载(走内核 RemovePlugin
+ onRemove→ POST /plugins binary body 传 hmap返回 installed+checksum生效用
webui `POST /api/v1/plugins/reload`X-API-Key生产 admin123
- 关键 bugLinux dlopen 同路径复用旧句柄——RemovePlugin/ReloadOne 只 Stop 不 dlclose
插件二进制更新后重载仍执行旧代码(生产 memo 装新版仍注册旧 3 工具)。修复:
cabiPlugin.Close()handle.Close+ Registry.closeDynamic 在卸载/重载时调用(主仓 649e312
生产 homed-new7 验证memo 6 工具memo_todo_add/complete/list + memo_memo_create/list/delete
注册正常wechat 通道 running。
- SDK 仓推送 b6e30f9工具链 47 + 重建 bin 二进制 + memo plg.json + sdk/plugin.go 注释精简)。
15. [2026-08-03] 嵌套 git 恢复 + 工作区清理 + codegraph 索引修正:
- 嵌套 git 恢复third_party/homeagent-sdk 原本是"单仓库双提交"(目录内嵌套 .git 推 gitcode
homeagent-sdk 仓,主仓 git 跟踪 sdk/meta/go.mod 推 HomeAgent 仓),嵌套 .git 此前被误删;
已从 /tmp/opencode/sdk-repo 复制 .git 恢复remote=homeagent-sdk.gitHEAD=b6e30f9工作区干净
主仓 git 不受影响。以后 SDK 改动直接在 third_party 内 git commit+pushSDK 仓推送仍用带凭据
URL https://JianFeeeee:BCkb32xBuLxWD9P4MmU8ydZ5@gitcode.com/JianFeeeee/homeagent-sdk.git
不再经 /tmp 中转。
- /tmp 清理:删除 /tmp/opencode/sdk-repo、hasdk-fresh、plugindev-new、plugindev_new、mock-run、
lunartestSDK 中转/临时目录);保留 homed-new*生产二进制备份、mock-skills 等非 SDK 内容。
- replace 修正go.mod 第 17 行已是 `./third_party/homeagent-sdk`正确package-linux.sh
prepare_gomod 优先用 $PROJECT_ROOT/third_party/homeagent-sdk仅缺失时才 clone /tmp/homeagent-sdk
兜底(主仓 108faac
- codegraph 索引修正:根目录 codegraph.jsonPROJECT_CONFIG_FILENAME
includeIgnored+include: ["third_party/homeagent-sdk"]codegraph index 后 Files 179→233
third_party 文件 7→61tools/plugindev 与 sdk/plugin.goInjectInputSync 等)均可查询
(此前嵌套 SDK 仓被主仓 .gitignore 挡在索引外codegraph sync 不感知配置变更,需 index 全量重建。