mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-03 15:53:56 +00:00
docs: 文档与发布脚本同步到 v1.0.0 子进程架构
README/架构文档仍在描述 C ABI 动态库加载,与 v1.0.0 实际实现不符。
新用户按文档走会去做 -buildmode=c-shared,产物新内核根本不加载。
README.md / README_EN.md:
- 设计要点补子进程架构段(三面通信、崩溃自愈、真热重载)
- 代码结构 plugin/ 描述:.so 动态加载器 → 子进程加载器
- 项目状态补 v1.0.0 条目(6 类缺陷 + 实测数字),v0.9.0 标注 ABI 已退场
- 新增「下载」章节:三变体对照 + 各平台包格式 + macOS 限制
assets/docs/{zh,en}/ARCHITECTURE.md:
- 四种加载方式表:外部 .so/C ABI → 外部子进程/握手+stdio JSON-RPC
- 加载流程改写为 exec.Command → 继承 fd → 握手 → init → start
- 内置 vs 外部对照表 7 行更新
- 新增「子进程插件的三个通信面」小节,含每个面的选择理由
assets/docs/{zh,en}/OVERVIEW.md:插件系统段落改写
deploy/ 发布脚本三处回归(v0.7.2 的 5006712 把 package/ 移到
deploy/packaging/ 使目录深度 1→2,但没改相对路径,此后两个版本
的发布都没有二进制资产):
- build.sh:.syso 按目标平台 hide/restore(trap 兜底),恢复
windows 目标的 CXX,arm64 刻意不带 CXX
- installer.nsi:5 处 ..\build → ..\..\build,PRODUCT_VERSION 可注入
(原先硬编码 0.8.0)
- homeagent.spec:server 变体补装 waiter(control-server 声明了 CLI 却没装)
deploy/scripts/upload_assets.py:release 资产上传(两步签名 URL → OBS
PUT)。放 deploy/scripts/ 而非 scripts/,因为后者在 .gitignore 里。
支持 GITCODE_REPO/ASSET_DIR 环境变量以复用于 SDK 仓。
This commit is contained in:
@ -259,12 +259,18 @@ VM built-ins: `json.encode` / `json.decode` / `log` / `http_get` / `http_post`.
|
||||
| Method | Registration Mechanism | Compilation | Usage |
|
||||
|--------|----------------------|-------------|-------|
|
||||
| Built-in | `init()` → `RegisterFactory` | `internal/plugins/` compiled into kernel | webui/cli/timer/mcp etc. |
|
||||
| External `.so` | C ABI dynamic loading | `-buildmode=c-shared` + bridge | qq/browser/files etc. |
|
||||
| External subprocess plugin | Handshake + stdio JSON-RPC reverse registration | `plugindev build` → `plugin.bin` (ordinary Go binary) | qq/browser/files etc. |
|
||||
| Lua script plugin | Execute `main.lua` to register tools | No compilation, takes effect after restart/reload | luademo etc. |
|
||||
| SKILL plugin | Parse `SKILL.md` | Markdown definition | Loaded via clawhubadapter |
|
||||
|
||||
Built-in plugin registration: `internal/plugins/all.go` blank imports → each plugin `init()` → `Registry.Load()` scans directory to match factory.
|
||||
External plugin loading: `internal/plugin/dynamic.go` → copy to SHA256 temp path (bypass `plugin.Open` path cache) → `Open` + `Lookup("NewPlugin")`.
|
||||
|
||||
External plugin loading (since v1.0.0): `internal/plugin/dynamic_proc.go` → `exec.Command(plugin.bin)`
|
||||
→ inherit shared-segment fds → handshake (protocol version check) → `plugin.init` → `plugin.start`
|
||||
(the plugin reverse-registers tools/stages/channels during this window).
|
||||
**The C ABI channel (`-buildmode=c-shared` + bridge) was removed entirely in v1.0.0**—
|
||||
the old `plugin.Open` path-cache workarounds (SHA256 temp-path copies) retired with it.
|
||||
|
||||
Lua script plugin loading: `internal/plugin/` → the gopher-lua interpreter executes `main.lua` (at load time `sdk.register_*` only buffers handlers), then `Start()` swaps in the real SDK implementation and registers them in batch. The script is read only once at load time; runtime execution happens via callbacks.
|
||||
|
||||
### Built-in vs External Plugins
|
||||
@ -272,20 +278,39 @@ Lua script plugin loading: `internal/plugin/` → the gopher-lua interpreter exe
|
||||
| Dimension | Built-in Plugin | External Plugin |
|
||||
|-----------|----------------|-----------------|
|
||||
| Registration | `init()` calls `plugin.RegisterFactory(name, factory)` | Implements `NewPluginFactory(name, config) (sdk.Plugin, error)` entry function |
|
||||
| Compilation | Compiled into `homed` binary, no separate build | Compiled via `plugindev build` to `.so`/`.dll` (`-buildmode=c-shared`), loaded via C ABI bridge |
|
||||
| Compilation | Compiled into `homed` binary, no separate build | Compiled via `plugindev build` to `plugin.bin` (ordinary Go binary, zero cgo); the kernel spawns it as a subprocess |
|
||||
| Distribution | Bundled with kernel, not independently installable | `.hmap` package (ZIP archive), installed via WebUI or pluginmgr API |
|
||||
| Metadata | `plugin.RegisterPluginMeta()` for display name | `plugin.json` manifest file (name, version, entry, platforms, etc.) |
|
||||
| Plugin directory | No separate directory, compiled into binary | `plugins/<name>/` independent directory with `plugin.json` + binary |
|
||||
| SDK permissions | Full PluginSDK (SocialAPI read/write, Publish events) | Restricted SDK (SocialAPI read-only, Subscribe-only events) |
|
||||
| Lifecycle | Starts/stops with kernel, no individual hot-reload | Independent Start/Stop, supports hot-reload (ReloadOne) and enable/disable |
|
||||
| Crash recovery | No independent recovery | Supports `SetAutoRestart(true)` for automatic crash restart |
|
||||
| Metadata | `plugin.RegisterPluginMeta()` for display name | `plugin.json` manifest file (name, version, entry, platforms, capabilities, etc.) |
|
||||
| Plugin directory | No separate directory, compiled into binary | `plugins/<name>/` independent directory with `plugin.json` + `plugin.bin` |
|
||||
| SDK permissions | Full PluginSDK (SocialAPI read/write, Publish events) | Narrowed `procCore` surface + manifest capabilities declaration + RPC boundary rejection |
|
||||
| Lifecycle | Starts/stops with kernel, no individual hot-reload | Independent process; true hot-reload by swapping `plugin.bin` (ReloadOne) plus enable/disable |
|
||||
| Crash recovery | No independent recovery | Process-level isolation: a crash cannot take down the kernel; the kernel detaches its registrations then restarts it with backoff (`SetAutoRestart(false)` opts out) |
|
||||
|
||||
Common ground:
|
||||
- Built-in `RegisterFactory` and external `NewPluginFactory` share the same `NativeFactory` type signature
|
||||
- `Registry.Load()` handles both uniformly: checks factory table first (built-in), falls back to dynamic loading (external)
|
||||
- Both use the same `Plugin` interface and `PluginSDK`; tool registration, stage hooks, and output channel APIs are identical
|
||||
- `Registry.Load()` handles both uniformly: checks the factory table first (built-in), otherwise dispatches by the manifest `entry` to the proc / lua / skill channel
|
||||
- Both use the same `Plugin` interface and public SDK API; tool registration, stage hooks, and output channel APIs are identical
|
||||
- Both share the same tool registry (`StageHost`); LLM invocations treat them identically
|
||||
|
||||
### The Three Communication Planes of Subprocess Plugins (v1.0.0)
|
||||
|
||||
| Plane | Mechanism | Why this choice |
|
||||
|---|---|---|
|
||||
| Control | stdio JSON-RPC (NDJSON frames), 51 `core.*` methods | The process boundary *is* the ABI boundary—no need to maintain three platform-specific dynamic-library loaders |
|
||||
| Data | Shared memory segment, **one segment shared by all subprocesses** | One segment per plugin would degrade "kernel ctx → segment → plugin mutates → read back" into the copy model under concurrency, reproducing lost updates exactly |
|
||||
| Notification | Event ring + platform notify (Linux eventfd / macOS pipe / Windows Event) | The kernel must never block on a consumer: streaming output publishes per token, so any wait shows up as stutter |
|
||||
|
||||
**Subprocess lifecycle management**:
|
||||
- One dedicated `waitLoop` per subprocess (the sole `cmd.Wait()` call site)—it does not rely on
|
||||
stdout EOF, because grandchild processes forked by a plugin (browser spawning chromium,
|
||||
editdoc spawning python) inherit the same stdout, so EOF never arrives after the plugin itself dies
|
||||
- Central ledger `proc.Supervisor`: registered on successful handshake, unregistered on exit;
|
||||
`Host.Close()` runs StopAll before tearing down the segment (reversing that order leaves plugins
|
||||
holding a mapping that has been unmapped—SIGBUS on their next access)
|
||||
- Crash self-healing: detach registrations (tools + stage handlers + IO channels) → remove from
|
||||
the registry → restart with backoff
|
||||
- Linux `Pdeathsig` is the last-resort guard so subprocesses do not linger as orphans when homed is SIGKILLed
|
||||
|
||||
### PluginSDK Four Channels
|
||||
|
||||
```
|
||||
|
||||
@ -50,7 +50,10 @@ Code is in the project root, implemented in Go.
|
||||
|
||||
**Plugin System** (`internal/plugin/`):
|
||||
- Built-in plugins: Go `init()` self-registration, compiled into kernel
|
||||
- External plugins: Go `-buildmode=c-shared` compiled to `.so`, dynamically loaded via C ABI bridge; also supports Lua script plugins
|
||||
- External plugins (since v1.0.0): compiled to an ordinary Go binary `plugin.bin`, spawned by the
|
||||
kernel as an **independent subprocess**, communicating over stdio JSON-RPC (control plane) +
|
||||
a shared memory segment (data plane) + an event ring (notification plane); Lua script plugins are
|
||||
also supported (the C ABI shared-library channel, `-buildmode=c-shared`, was removed entirely in v1.0.0)
|
||||
- PluginSDK (`internal/sdk/`) defines four channels: RegisterTool / RegisterStage / Subscribe / RegisterOutputChannel
|
||||
- 7 stage hooks: on_input → pre_action → post_action → before_toolcall → after_toolcall → before_output → after_output
|
||||
|
||||
|
||||
Reference in New Issue
Block a user