Fuse rmux terminal gateway into lucarned#36
Conversation
终端监控子系统(rmux feature 门控,默认构建不拉入):
- lucarne-term: rmux-free 终端值类型 + 自写 snapshot differ + 会话注册表
- lucarne-rmux: rmux-sdk adapter + monitor(监控系统 daemon,Adopted/Managed)
- lucarne-termgw: axum 网关(ws 镜像 + /api 控制面 + 静态资源)
- lucarne-termctl: 薄 CLI(ls/new/attach/detach/kill + go-public)
- lucarne-web: web 双模客户端(终端镜像 + chat)
P5 远程可达(可插拔隧道适配器):
- lucarne-remote: RemoteAccessProvider trait + RemoteRegistry + Cloudflared 后端;
FRP/relay/其它穿透留 trait seam + 通用 providers.<id> 配置占位
- lucarned: remote cargo feature 接线 + 通用 provider_fields 配置 + 控制面(127.0.0.1:7801)/网关(127.0.0.1:7800)分端口隔离
- 公网鉴权(落 gateway/web 层):access token(256-bit/常量时间) + 单次短 TTL ws ticket +
AccessScope 只读档 + default-deny + loopback-only + 失败限流
- 一键开启 CLI:term go-public(选后端→提示→起隧道→public URL + QR + access key)
- 安全加固(经 codex 多维 review 三轮闭环):readonly 越权、/chat 绕过鉴权/限流、
cloudflared stderr drain/health/reaper、锁跨 await、daemon idle-exit、provider 边界回归等
ADR: docs/decisions/2026-05-30-{rmux-terminal-monitor-subsystem,remote-access-tunnel-adapter}.md
默认 lucarned 构建零警告且 cargo tree 不含 remote crates;cargo +nightly test 全绿。
冷启动一键化(懒启动 gateway): - 控制面 127.0.0.1:7801 在 --features remote 下常驻(boot 即可用,返回 token/running:false),不依赖 rmux - gateway + monitor + tunnel 改懒启动:仅首次 control.start(term go-public 或 autostart)才连 rmux/绑 gateway/起隧道,once-guard 防双绑 - remote.enabled→autostart:true 则 boot 自动起隧道(保持原行为),false 则控制面就绪、隧道空闲等 term go-public - 无 rmux 环境:go-public 返回明确错误而非 daemon 崩溃;feature-build 因控制面常驻而保活 - 全部既有安全闭环保留(控制面独立端口/loopback/default-deny/readonly scope/permit先于ticket/forwarded-identity) 其它: - fix(termctl): 修过期注释(daemon 现经 RemoteStartParams 使用 CLI 字段,G3) - chore(clippy): lucarne-term derive + lucarne-agentbind sort_by_key 清零 - test: 加 default_config_parses_including_remote_providers 回归守护(证伪 review 误报的 null 解析) 默认 lucarned 零警告且 cargo tree 不含 remote crates;cargo +nightly test 全绿。
经 4 路并行子代理对照原项目(AGENTS.md/既有 crate 约定)审查后修复: provider 边界(AGENTS.md): - lucarne-agentbind 彻底接入 agent-sessions provider descriptor 契约: 删除 Claude 专属硬编码(~/.claude/projects 布局、kind:"claude" 字面量、 手解 jsonl schema),改由 discovery/parse 契约驱动,kind=provider.id(); bounded line-window 读取。消除 public 层 provider 细节泄漏。 日志/任务生命周期: - tracing target 对齐:lucarne-termgw/rmux/web 改用 target:"lucarne_xxx", 去掉消息连字符前缀(恢复按 crate 分级过滤) - detached 任务可收口:RmuxMonitor source loops + WebChat 事件泵改 Drop-abort (持 JoinHandle/AbortHandle),对齐 AdapterSupervisorHandle 模式 测试: - lucarned/src/remote.rs 补 17 内联测试(默认拒绝/状态机/错误映射/G3 合并) - lucarne-term/rmux 新增 contract_boundary 集成测试(守护 rmux_sdk 隔离不变量) - lucarne-web 测试 3→15(出站帧映射/FrameRate/open-ack 超时) 结构/元数据/文档: - lucarne-web 单文件拆分为 state/router/session 三模块(对齐 channel 惯例) - 8 个新 crate 补 Cargo description;ADR/注释 rmux→remote feature 名修正 - axum 上提 [workspace.dependencies];CLI bin 改名 lucarne-term→term(消除与库 crate 同名) - ADR 修正 web 为 ws 桥(非 Channel trait)措辞;README 双语同步新子系统+Roadmap - clippy 清零:lucarne-term/agentbind/archive 的 sort_by_key 默认 lucarned 零警告且 cargo tree 不含 remote/axum;cargo +nightly test 全绿。
实机测试发现:lucarned.yaml 的 `remote.providers.<id>.{token,public_url}: null`
经 serde 反序列化成字符串 "null"(非空),漏过空值过滤后传给 provider;cloudflared
据此误判为 named tunnel,再把 public_url="null" 当 URL 解析 → "invalid public_url
for named tunnel: relative URL without a base",隧道起不来。examples/lucarned.yaml
默认就写 null,照抄者必中招。
- RemoteFileConfig.providers 内层值 String → Option<String>:YAML null → None → 视为 absent
- resolve_provider_fields 跳过 None/空值(quick 模式回归)
- 更新 remote_file_config_parses_full_section 断言;default_config_parses_… 增回归断言
(null 字段 resolve 后必须 absent)
经真实 cloudflared quick 隧道端到端验证:公网可达 + 鉴权全闭环(无/错 token→401,
带 token→换 ticket→200 列出 3 会话,手机实连成功);lucarned --features remote 91 测试全绿。
新增唯一交互入口 `lucarned tui`(feature-gated 全屏 ratatui TUI,opencode 式方向键导航),彻底移除独立 `term` 二进制,其可复用逻辑迁入 lucarned,不重写。 - 三面板:Sessions(rmux 会话 列表/attach 弹出/detach/kill/archive)、Go Public(经既有 loopback /api/remote/* 启停隧道 + 高对比 QR 模态 + 凭证)、Config(lucarne_remote 描述符驱动的 provider 字段表单 → 带备份写回 lucarned.yaml) - feature `tui`(implies remote);ratatui 0.30 + crossterm 0.29 仅在该 feature 下,默认构建零 ratatui/crossterm/rmux(tests/default_build_purity.rs 守护) - 终端恢复:raw mode/alternate screen + 进程级 panic hook;attach 用 挂起→exec rmux→重入 交接,RAII 守卫防 raw mode 泄漏 - fix: TUI 在 #[tokio::main] 内经 reqwest::blocking drop 嵌套运行时 panic → 改在无运行时上下文的独立 OS 线程执行(含回归测试) - 安全:lucarned.yaml + 备份写入 0o600;access token 仅显示"已设置"+ 经 QR 分享;控制面不可达时给出可操作提示(引导启动守护进程) - 零新建 daemon 控制 IPC;不在 common 层硬编码 provider(守 AGENTS.md 边界) - docs: README/README.cn/commands.md 改用 lucarned tui;新增 ADR 2026-06-01-lucarned-tui-frontend 锁定决策:经用户授权放宽合并范围 tuchg#5(允许薄 TUI),tuchg#6 薄包装/零新 IPC 精神保留。
介绍从独立 term CLI 到唯一 `lucarned tui` 入口的改动,以及三面板用法: - 构建/启动(`--features tui`,默认构建不含 ratatui) - 各面板键位(Sessions / Go Public / Config) - Go Public 需 lucarned 守护进程在跑(守护进程拥有隧道生命周期,控制面 127.0.0.1:7801)+ 端到端两终端示例 - 架构边界(零新建 IPC、非实时镜像、provider 边界、feature 隔离) - `term` → `lucarned tui` 迁移对照表 README.md / docs/commands.md 各加一行指向 docs/tui.md。
PART 1 — TUI 内"配置 + 起隧道",免先编辑 yaml: - ConfigPanel::start_params() 返回当前 provider + 非空字段(纯函数) - GoPublicPanel::start_with(provider, fields) 用给定参数组装 plan;保留零参 start() 作守护进程默认 - App 桥接:Go Public 面板按 s 时读 config.start_params() → go_public.start_with(...) - 发送前经 provider 描述符 validate_config 校验,失败内联提示且不发送 - Go Public 正文显示起源行(start uses Config: provider=… / daemon default) - docs/tui.md 说明 s 使用 Config 面板实时字段 PART 2 — 6 项评审 Low 清理: - COR-003: 先绘制首帧再做阻塞 refresh;事件循环改 event::poll(1s) 就绪才 read(无忙等) - COR-004: QR 回退模态宽度用 chars().count() 而非字节 len() - COR-005: 区分 rmux 不可达(rmux_unreachable)与空列表,状态提示不同 - MNT-003: 抽 send_control 复用 send→状态校验→解析;call_remote_* 变薄包装 - MNT-005: 移除单变体 GoPublicAction,handle_key 返回 () - MNT-007: 新增 tui::nav 共享 step/clamp + EmptyPolicy,Sessions/Config/ui 复用 验证:cargo +nightly test -p lucarned --features tui 142+2 purity 绿;默认 67+2 绿;默认构建无 ratatui/crossterm/rpassword;clippy 干净。
|
Review position: significant progress, but still do not merge as-is. 最新 head 但这些改动还没有把架构收成一条清晰主线。尤其需要强调:release 入口对齐不等于产品能力应该默认融合。该有 feature 还是要有 feature。 默认 What improved
Those are real improvements. They do not yet resolve the main architecture issues below. Remaining architecture issues
Required before merge
Bottom lineThis head is materially better than the earlier one, and several previous objections should be downgraded or removed. But the current “Fuse rmux terminal gateway into lucarned” direction overcorrects: it aligns the release entrypoint by fusing product capabilities into the default daemon instead of preserving explicit features and an access/operator architecture. I would still hold merge until features, access-plane ownership, operator API, bounded transcript reads, and real live E2E journeys are in place. |
|
已按 review 总评修复并推送到 head 处理内容:
验证已通过:
|
Summary
lucarnedruntime and TUI entrypoint instead of requiring a separate feature-gated launch path.have_revcorrectly.Testing
cargo +nightly fmt --checkgit diff --check HEADbash -n scripts/remote-quick-tunnel-e2e.sh && scripts/remote-quick-tunnel-e2e.shcargo +nightly clippy -Zbuild-dir-new-layout --workspace --all-targets --all-features --exclude agent-sessions --no-deps -- -D warningscargo +nightly test -Zbuild-dir-new-layout -p lucarne --test live_unit codex_live_preflight_accepts_minimal_turn -- --exactcargo +nightly test -Zbuild-dir-new-layout --workspace --all-features --exclude agent-sessionsNotes
mainat670e403/ releasev0.4.3, with lockfile versions synchronized after merge.