feat(cli): add app-server stdio host command - #2343
Conversation
limityan
left a comment
There was a problem hiding this comment.
结论:Request Changes。复审基线为 b104eb44e3e482f65271d9427a7030fd71cf30cc。方向上,为编辑器提供 stdio App Server Host 是合理需求;但当前实现尚未交付一个受批准架构和 Host 合同约束的入口。
[P1] 新 stdio Server Host 尚未成为经批准的架构例外
问题: src/apps/cli/Cargo.toml:58 新增了 bitfun-app-server implementation 依赖,随后 forbidden-rules.mjs 和 self-test.mjs 直接把原禁止规则改成允许;但当前 product-architecture.md:499-509、agent-runtime-deployment-design.md:400-408 仍规定 Headless CLI 使用独立 adapter,CLI 不依赖 App Server implementation/client。新 Host 还直接选择 DeliveryProfile::Cli,权威设计没有定义其产品身份和 capability 上限。
风险: checker 开始证明一个权威设计仍禁止的状态,后续无法区分普通 Headless CLI/TUI composition 与独立 Server Host 的依赖边界,也无法判断该入口承诺的是 CLI 能力、完整 Server 能力还是子集。
建议: 明确记录 bitfun server 是同一二进制中的独立 stdio Server Host,解释选择 CLI profile 的依据和能力上限,并将依赖例外收窄到 Host 装配点;守卫仍应禁止 TUI/controller/普通 Headless CLI 引入 App Server implementation/client。这里不要求为了依赖关系机械拆 crate,但不能只修改 checker。
[P1] 声称的 cwd-only scope 没有覆盖完整控制面
问题: root_handlers.rs:958-982 只为 Agent Runtime 固定 cwd,随后挂载的是没有 Host scope 的全局 AppManagementService 和完整通用 handlers。Git、Hook、External Source、Skill 等调用仍直接信任请求中的任意 repository_path / workspace_path,config handler 还能修改全局配置。
风险: 被 spawn 的客户端可越过 cwd 读取或修改其他本机工作区及用户全局状态,违反 App Server Host 必须拥有 canonical workspace、execution domain 和 method/capability allowlist 的架构合同。
建议: 由 Host 注入不可变的 canonical scope/identity/execution context;所有携带路径的请求在进入 owner 前 fail closed,并建立该 Host 的显式 method allowlist。
[P1] stdin EOF/客户端断连不能终止 Host
root_handlers.rs:988 直接等待 serve(),而 server/event_forwarder.rs:25 的事件循环不观察 connection close。实测初始化约 0.2 秒完成,但关闭 stdin 后 30 秒仍不退出。编辑器关闭管道后会遗留子进程,连接拥有的操作也没有确定性的 cancel、drain 或 outcome_unknown 收敛。请把 EOF/connection close 纳入 Host 生命周期,并增加真实子进程测试。
[P2] capability 与真实 provider 不一致
app/initialize 静态声明 session/reloadContext 可用,但该路径只调用 BitfunAppRuntime::new,没有注入 context-reload provider;实际请求必然返回 internal error。capability 应由真实 provider、Host allowlist 和健康状态构造。
[P2] 16 MiB transport limit 没有在 stdio reader 执行
初始化响应宣称 16 MiB frame 上限,但 ByteStreams 的行读取会先无界收取整行。请在读取阶段真正限帧,并覆盖超限拒绝测试。
当前 8 项远端 CI 全部通过,本地 command parse、core-boundaries 和 diff 检查也通过;但新增测试只验证 Clap 解析,没有覆盖 stdio initialize/round-trip、stdout 纯净性、cwd 越界拒绝、capability/provider 一致性、EOF 退出或活动 Turn 断连生命周期。因此绿色检查不能消除上述 Host 行为和架构缺口。
limityan
left a comment
There was a problem hiding this comment.
第二轮复审结论:无阻塞问题,架构层面成立。
- limityan 提出的 P1×3 / P2×2 已在当前 commit 全部解决(架构例外记录、cwd scope 控制面、EOF 生命周期、reloadContext capability、16MiB 帧限制)。
- 架构文档(app-server-architecture.md §1.3、agent-runtime-deployment-design.md、product-architecture.md)、check-core-boundaries 规则与实现三方一致;初始化序列与 serve_acp_stdio 同构;CI 8/8 全绿。
以下为非阻塞建议(Medium,按需处理即可):
-
validate_path 错误语义(src/crates/interfaces/app-server/src/server/host_policy.rs:260)
dunce::canonicalize 失败被统一映射为 PathOutsideWorkspace。最常见失败原因是路径不存在(客户端拼错或文件已删除),会被误报为"超出 workspace scope",误导诊断。建议区分:canonicalize 失败且 error.kind() 为 NotFound 时返回语义准确的错误(如 path_not_found,或放行交由 handler 自然报文件不存在);仅当路径存在且 canonical 后不在 workspace root 内时才报 PathOutsideWorkspace。保持 fail-closed:不要因此弱化绝对路径与 scope 校验。 -
validate_scope 递归深度(host_policy.rs:206)
递归遍历 params JSON 无显式深度上限。当前 Value 由 serde_json 解析、默认 128 层限制能部分缓解(128 层递归栈安全),但建议在递归入口加显式深度上限(如 64)或改为显式栈迭代,超限直接拒绝,防御未来非 parser 构造的 Value 传入。
另有若干 Low 项记录(不阻塞):
- agent/permissionEvent 已从 ALLOWED_METHODS 移除,但同类 server→client 通知 agent/event、agent/frontendEvent 保留,能力广告不一致;客户端仍能收到 permissionEvent,仅广告缺失。
- Web Host 不再广告 session/reloadContext 的行为变化建议在 PR 描述中补充说明。
- PR 描述 "reported as unavailable" 与实际不符:被过滤空的能力描述符是整体丢弃(not advertised)。
- 广告 eventBufferCapacity=1024 与实际 RUNTIME_EVENT_BUFFER=256 不符(预存在)。
Summary
Adds a
bitfun servercommand that hosts the BitFun App Server over stdio.stdout carries JSON-RPC traffic only and logs go to stderr, so an App Server
client (for example an editor integration) can spawn this command as its host.
The workspace scope is the current directory, matching the CLI's cwd-only
session scope, and the host reuses the reviewed CLI product runtime
(
DeliveryProfile::Cli+ CLI assembly) instead of ACP or server-host profiles.Type and Areas
Type: Feature
Areas: Rust core — CLI surface (
src/apps/cli) and App Server interface wiring(
bitfun-app-server); repository boundary rules (scripts/core-boundaries).Motivation / Impact
app:
bitfun serverspeaks the App Server protocol over stdin/stdout.stderr.
service, and CLI product runtime as interactive sessions, so kernel behavior
matches the CLI delivery profile.
are served from local configuration. Account sync, MCP management, and local
worktree management are reported as unavailable by this host through the
existing capability descriptors, so clients degrade loudly instead of getting
fake success.
Verification
cargo test -p bitfun-cli server_command— new clap parse testserver_command_tests::server_command_parses_as_stdio_hostpasses(1 passed, 0 failed).
cargo build -p bitfun-cli+bitfun server --help— command parses andhelp documents the stdio contract ("stdout carries JSON-RPC traffic only;
logs are written to stderr").
pnpm run check:core-boundaries— passes with the updated CLI manifestguard:
bitfun-climay depend onbitfun-app-server(stdio host surface),while
bitfun-app-server-client,bitfun-app-server-protocol, andbitfun-tui-managementremain forbidden; self-test expectations updated tomatch.
Reviewer Notes
Server server-side host surface. The CLI still must not import the typed
client transport, wire DTOs, or a shared TUI management crate.
ByteStreams::new(stdout, stdin), andruntime events cross the connection through the
AgentEventSourceinjectedfrom the same runtime owner (
AppServerRuntimeContext), following theapp-server Event Delivery contract.
tui.*wire capability IDsunchanged, so older clients keep negotiating on the same contract; no
persisted shapes change.
(cwd scope). No remote workspace or remote control behavior is added or
claimed.
Checklist