Skip to content

feat: integrate managed opencode plugin host - #2221

Open
Can2Nya wants to merge 1 commit into
GCWing:mainfrom
Can2Nya:plugin_host
Open

feat: integrate managed opencode plugin host#2221
Can2Nya wants to merge 1 commit into
GCWing:mainfrom
Can2Nya:plugin_host

Conversation

@Can2Nya

@Can2Nya Can2Nya commented Aug 11, 2026

Copy link
Copy Markdown

Summary

  • Add a managed Bun Extension Host compatible with the public OpenCode plugin and SDK contract.
  • Implement bidirectional, length-framed JSON-RPC communication between the Bun host and Rust backend, including handshake validation, frame limits, reentrant requests, and streamed payload handling.
  • Add per-instance loopback HTTP/SSE gateways so plugins can use the injected SDK and raw HTTP APIs while Rust retains ownership of application state, persistence, lifecycle, and supervision.
  • Support plugin hooks, tools, authentication, providers, workspace adapters, and local/npm plugin loading.
  • Integrate Plugin Host activation into the local CLI lifecycle and update CLI packaging, installation, self-update, and build prerequisite contracts.
  • Add protocol documentation, schema generation, examples, boundary rules, and focused Rust/Bun test coverage.

Fixes #

Type and Areas

Type:

Feature

Areas:

  • Extension Host / Plugin Runtime
  • OpenCode Compatibility
  • Rust Core and Adapter Layer
  • CLI Runtime and Activation
  • Packaging, Installation, and Self-update
  • Build and Dependency Boundaries
  • Protocol Documentation and Tests

Motivation / Impact

BitFun needs to run established OpenCode Server plugins without embedding or depending on an OpenCode server process. This change introduces a separately supervised Bun compatibility host while keeping application state, persistence, HTTP behavior, security-sensitive lifecycle decisions, and process supervision in Rust.

The integration enables configured OpenCode-compatible plugins to contribute hooks, tools, authentication flows, providers, and workspace behavior to local CLI sessions.

Current scope and impact:

  • Local CLI workspaces can activate the configured Plugin Host.
  • Remote CLI workspaces explicitly skip local Plugin Host activation to avoid executing a controller-side plugin against a remote workspace.
  • Desktop Plugin Host activation remains disabled.
  • Local plugins are loaded in place and must resolve their own dependencies.
  • npm plugins are installed into the managed cache with lifecycle scripts disabled.
  • Compatibility is currently pinned to the public OpenCode plugin/SDK contract at version 1.17.18.

Verification

Passed:

  • cargo check -p bitfun-cli
  • Core dependency and feature-boundary checks
  • Build prerequisite tests: 8 passed
  • Rust OpenCode Plugin Host tests: 24 passed
  • git diff --check
  • Rebase verification against the latest origin/main

Bun Extension Host test result on Windows:

  • 44 tests passed.
  • 24 tests failed because the current test fixtures assume POSIX behavior, including /tmp paths, POSIX path formatting, and the printf command.
  • These Windows-specific failures remain documented and require test portability follow-up before the Bun suite can be considered fully green on Windows.

Remote scenario coverage:

  • Local CLI activation was exercised.
  • Remote workspace behavior is covered by the activation test that verifies local Plugin Host startup is skipped.
  • Desktop, remote control, Peer Device Mode, and Detached Dispatch execution were not exercised because this MR does not enable Plugin Host activation on those surfaces.

Reviewer Notes

Please pay particular attention to:

  • The trust boundary between the Bun Extension Host and Rust backend.
  • Handshake token validation and negotiated frame-size limits.
  • Reentrant JSON-RPC dispatch and request/response ID handling.
  • Stream lifecycle, backpressure, cancellation, and cleanup.
  • Per-instance loopback gateway isolation.
  • Plugin package resolution and disabled npm lifecycle scripts.
  • CLI startup, shutdown, packaging, installation, and self-update behavior.
  • The explicit decision to skip local activation for remote CLI workspaces.
  • Compatibility assumptions tied to OpenCode 1.17.18.

The Extension Host is a compatibility process, not an OpenCode server. Rust remains the owner of product state and lifecycle policy; Bun owns JavaScript module loading and function-valued plugin capabilities that cannot cross the JSON boundary.

Checklist

  • This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts.
  • Relevant verification is recorded above, or skipped checks are explained.
  • User-facing strings, docs, and locales are updated where applicable.

@Can2Nya
Can2Nya force-pushed the plugin_host branch 3 times, most recently from 09a3376 to 3607d5e Compare August 13, 2026 09:31
@limityan
limityan self-requested a review August 18, 2026 09:15

@limityan limityan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review(Request Changes)

对 PR #2221 的独立对抗性复审意见。整体上,传输层工程质量不错(监听先行绑定、首帧强制握手、帧长先校验后分配、进程树监督、npm --ignore-scripts、符号链接逃逸防护等均已核实),但存在四类阻断性问题(架构归属、信任/审批门、成熟度阶段、副作用路由权限),另有若干中等级别的具体缺陷。以下问题、风险与建议均基于实际 diff(merge-base 9d4db799c)逐条核实。

A. 阻断性问题

A1. 架构分层违规:OpenCode 兼容语义与 Plugin Host 进程对象都落在 bitfun-core(Assembly 层)

  • src/crates/assembly/core/src/plugin_host_http_routes.rs + plugin_host_http_routes_impl.rs(约 1850 行)把 30+ 条 OpenCode Client 路由(session/PTY/file/mcp/lsp/provider/tool)及其 wire 语义实现为 core 内部逻辑;plugin_host.rs 在 core 中持有 PluginHost 进程对象与实例/PTY 静态注册表;product_runtime.rs 新增 fork_session_for_plugin
  • 冲突条款:根 AGENTS.md 分层表规定 Assembly「must not implement concrete adapter, OS, or service details」;docs/architecture/extensions/plugin-runtime-design.md 职责表规定物理进程(进程树、资源预算、IPC、强制回收)应沿 ScriptToolRuntime/services 边界承担,「不得把 Rust 侧实现命名为 Host」「Rust 主应用…没有 Plugin Host 对象」;生态格式/参数/错误语义应保留在适配器,而 opencode-plugin-host 适配器自身的 AGENTS.md 声明不拥有配置选择与信任决策——实际上这些恰恰都被放进了 core。
  • 建议:进程监督下沉 services(或明确由适配器持有进程、core 只做组装);OpenCode 路由与序列化移到 bitfun-opencode-plugin-host;core 只保留 owner 端口调用。

A2. 信任/审批门缺失:config.plugin 非空即启动并执行第三方代码

  • plugin_host.rs:84-197 只要 launch_policy != Disabledconfig.has_configured_plugins() 就 spawn Bun 进程并对全部声明做 prepare_plugins(含 npm 依赖安装与 import);ensure_configured_plugin_instance 无任何确认直接 open_instance
  • 裸 npm spec 在 extension-host/src/loader.ts:217-233 被解析为 name@latest,每次 prepare 都可能静默安装最新版本,无候选版本展示、无更新确认。
  • 冲突条款:docs/architecture/product-architecture.md(首次激活/import 前须由既有归属模块重新确认执行许可,确认前不得 import module/产生直接脚本副作用);opencode-extension-compatibility.md §4.2(来源限定身份+插件身份+入口类型+执行域+更新策略确认)。
  • 全 PR 未接入既有 plugin_source/trust 文件/来源 owner,也没有任何 UI/CLI 首次启用确认。
  • 建议:合并前至少补齐一个最小确认门(CLI 交互确认或持久化 trust 记录),并把 npm 安装固定到可验证版本;若「config 中存在 plugin」本身即构成用户审批,请在 PR/文档中明确写出该流程与证据。

A3. 成熟度阶段被一次性跳过,且无对应设计/计划文档

  • 本 PR 一次落地 package 依赖安装 + 全套 Hook(chat.message/permission.ask/tool.execute.before/shell.env 等)+ 回环 Server 路由 + auth/provider/workspace adapter,而设计文档明确这些应分阶段(OC-E0→E3)交付:opencode-extension-compatibility.md:87-90docs/plans/opencode-extension-compatibility-plan.md:355-363(暂停条件:为完整兼容一次性引入 package manager、Hook、renderer、Server 和权限系统时应停止扩面先修架构)、plugin-runtime-design.md:276-278 及 §8 的固定 fixture 六项验证要求。
  • 本 PR 文件清单中没有任何 docs/ 变更,未更新兼容矩阵「当前状态」,也未提供阶段设计/退出标准。
  • 建议:补充本实现对应的分阶段设计文档与固定版本真实插件夹具验证证据,或先在文档中关闭/降级相应阶段。

A4. 副作用路由缺少权限归属模块的调用时检查

  • 第三方插件可经回环路由执行 session_create/delete/fork/abort/updatepty_create(允许插件提供任意 env)、file_read、全局 mcp_status/lsp_status(routes_impl.rs:111-142、173-184、244-264、676-709、945-1008)。入口只有 instance 目录作用域校验与 30s 超时,没有任何 Permission owner / tool-permission 策略检查。
  • 冲突条款:product-architecture.md(经 BitFun 接口发起的调用仍执行调用时权限判断);opencode-plugin-runtime-adapter-design.md(在 Rust 归属模块检查来源、凭据、文件范围、工具覆盖等)。
  • 建议:副作用路由接入 Permission owner 调用时检查;不支持能力返回稳定 unsupported 错误而不是统一 400。

B. 具体缺陷(Medium)

B1. 握手读帧无超时(可用性/本地 DoS)
adapters/opencode-plugin-host/src/lib.rs:201-206STARTUP_TIMEOUT 只覆盖 listener.accept()complete_handshake → read_frame → read_exact 无超时。本机任意进程抢连后不发 payload(或 Bun 子进程握手前卡死)即可让 CLI 启动路径无限挂起。建议把 accept+handshake 放进同一 timeout。

B2. 单条流取消失败会中断实例 dispose
extension-host/src/host.ts:841await this.#streams.cancelAll(...) 位于 disposer 循环(847-863)与实例/目录清理(865-868)之前;main.ts:54-62cancelAll 未吞 rejection,streams.ts:85-99reader.cancel() 抛错会提前退出 dispose:disposer 不执行、目录永久占用,后续 open 永久报 directory_exists。违反 README「invokes every registered disposer once」。

B3. 出站超限帧拖垮整个 Host
extension-host/src/rpc.ts:257-275:所有出站消息(工具结果、auth loader options、hook 输出)强制 ≤ 协商帧长;插件返回一个序列化后 >16 MiB 的结果即触发 #fail → terminate(),整条连接关闭、全部实例失败。与「按插件隔离失败」的设计意图冲突,建议超限响应降级为 JSON-RPC error 而非断开连接。

B4. 帧协商上限恒 16 MiB,64 MiB 为死代码;且无下限 clamp
adapters/opencode-plugin-host/src/lib.rs:445-448.min(DEFAULT_MAX_FRAME_BYTES).min(MAX_FRAME_BYTES) 恒 ≤ 16 MiB,MAX_FRAME_BYTES=64 MiB 永不生效,与 PROTOCOL/README「可达 64 MiB」表述不符(方向安全)。另外协商值无下限:子进程可协商到 1 字节,之后后端所有出站响应都会超限断连。

B5. opencode-plugin-host feature 闭包过宽
assembly/core/Cargo.tomlopencode-plugin-host = ["plugin-runtime", "remote-connect", "git", "lsp", ...]remote-connect 的唯一消费点是 get_ai_model_catalog(lib.rs:82-85 门控;routes.rs:349 仅为 provider 投影),会把 phone-relay/rustls/tungstenite 等整块 owner 拖进插件宿主闭包。建议改用窄的 model-catalog 能力口,避免为读模型目录引入 remote-connect。

B6. Remote workspace 静默跳过
src/apps/cli/src/plugin_host_activation.rs:30-36:远程绑定只打 tracing::debug! 后返回 Ok,用户配置了插件在远程工作区运行会得到静默 no-op,没有任何「不支持」可见状态。违反仓库「Remote compatibility」规则(gate 或显示明确 unsupported 状态)与「禁止静默回落本机」的设计要求。

B7. 兼容基线不一致:pin 1.17.18 vs 文档固定 v1.18.9,且 Bun 被固定为唯一后端
extension-host/src/loader.ts:8 与 package.json 固定 @opencode-ai/plugin@1.17.18,而 opencode-extension-compatibility.md:24,33 明确「稳定兼容只固定 v1.18.9」;plugin_host.rs:53-62 把 Bun 硬编码为唯一运行时,文档要求「确有需要的裁决」。需要二选一并在文档中说明。

B8. 投影数据硬编码,向插件返回不实状态
plugin_host_http_routes.rs:501 所有 agent 恒定 "permission": {"edit":"ask","bash":{},"webfetch":"ask"};:376-388 把所有 enabled provider 都标为 connected;:92 无条件 "vcs":"git"。违反设计「不伪造成功」,插件会基于错误状态做决策。

B9. Bun 运行时未随包安装、无预检
launch spec 仅从 PATH 解析 bunplugin_host.rs:53-62),安装/自更新只捆绑扩展宿主入口文件;用户配置了插件但机器没有 bun 时,初始化错误被吞(仅 error 日志),直到首次会话操作才报「Configured plugin host is not running」硬错误,难以定位。建议安装/自更新增加 bun 检测与明确指引,或随包捆绑。

C. 次要问题(Low)

  • extension-host/README.md:47 引用了不存在的 docs/architecture/extensions/plugin-host-rpc-example.zh-CN.md(悬空引用)。
  • 握手前 host 侧已注册部分方法(main.ts:72-79 早于握手 83-101),host.stream.read/cancel 不等待握手即可被处理,与协议「握手前不允许 host.* 调用」不一致(纵深防御缺口)。
  • 握手 token 比较非常数时间(lib.rs:438);token 用 thread_rng 而非显式 CSPRNG(lib.rs:178-182)(令牌空间足够大,实际风险低)。
  • #pending(rpc.ts)与 #preparations(host.ts:445)无超时/无逐出,长期运行可累积。
  • 环回地址校验 startsWith("127.") 过宽(main.ts:191-195)。
  • 流读取出错时 owner 映射条目残留(main.ts:149-155 + streams.ts:66-71);read_host_stream 中途 RPC 失败不 cancel 对端流(http.rs:392-436)。
  • host 侧 handler 无超时,32 槽信号量可被占死(peer_runtime.rs:104-116)。
  • shutdown Notify 存在错过唤醒竞态,且 SHUTDOWN_STARTED 永不复位(plugin_host.rs:514-522)。
  • bun add 对以 - 开头的 spec 存在 CLI flag 注入面(bun-loader.ts:51,可信来源,低危)。
  • 测试缺口:握手负路径、协商边界、握手超时、read_host_stream 失败路径、handler 超限拒绝等均无覆盖。
  • 与本特性无关的纯 rustfmt 格式化改动混入 PR(glob_search.rs、web_readable.rs、hook_import.rs、client_info.rs、page_upload.rs、transcript.rs、remote_ssh/transport.rs 等),建议剥离以保持 PR focused。

D. 风险汇总

  1. 安全:启动即 import/执行第三方代码、无确认门;副作用路由(会话删除/fork、PTY、文件读取)无权限闸门;@latest 静默换包。
  2. 可用性:握手无超时可使 CLI 启动挂死;单插件超限结果可拖垮整个 Host 与全部实例;bun 缺失时的错误难以诊断;远程场景静默 no-op 误导用户。
  3. 治理一致性:与 product-architecture.md / plugin-runtime-design.md / opencode-extension-compatibility.md 三份文档直接冲突(分层、信任、阶段、版本基线),若不先修文档或实现,后续架构审计会持续失败。

E. 已核实正确的部分

  • 监听先绑定、后 spawn;首帧强制 backend.handshake 并逐项校验(method/jsonrpc/id/token/protocolVersion)。
  • 帧长先校验后分配;进程树监督(Unix process group / Windows Job Object)与失败清理属实。
  • 远程 CLI 跳过本地激活与桌面 Disabled 符合 PR 声明;npm 安装 --ignore-scripts 有测试;符号链接逃逸防护(realpath);自更新回滚完整;新 crate 的边界规则为收紧方向。

F. 建议的处理顺序

  1. 信任门(合并前):接入来源/信任 owner 确认或明确 config 写入即审批;npm 安装固定可验证版本、去 @latest
  2. 架构归位:进程监督下沉 services/适配器,路由与序列化移到适配器,core 只做组装。
  3. 权限:副作用路由接入 Permission owner 调用时检查,不支持能力返回稳定 unsupported。
  4. 文档与阶段:更新兼容矩阵「当前状态」与 plugin-runtime-design.md §7;补分阶段设计与固定 fixture 验证;统一版本 pin。
  5. 必修 bug:B1(握手超时)、B2(dispose 中断)、B3(超限帧降级)。
  6. 次要:remote 显式不支持状态、bun 预检、feature 闭包解耦、清理格式化 churn 与悬空引用。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants