ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

oh-my-opencode-slim 桌面伴侣:基于文件状态共享与 Sprite Sheet 的 Agent 活动可视化实现解析

oh-my-opencode-slim 桌面伴侣:基于文件状态共享与 Sprite Sheet 的 Agent 活动可视化实现解析 人工智能AI AgentAgent 编排AI 技能【免费下载链接】oh-my-opencode-slimLean, fine tuned Opencode multi agent suite · Mix any models · Auto delegate tasks项目地址https://gitcode.com/gh_mirrors/oh/oh-my-opencode-slim点击查看免费下载导读companion/codemap.md描述了 oh-my-opencode-slim 生态中的一个独立桌面组件——companion桌面伴侣一个以 macOS 附属应用accessory app不占 Dock形式运行的 Rust 桌面程序通过文件级状态共享与 OpenCode 宿主运行时通信为每个活跃会话弹出可拖拽、可缩放的浮动动画窗口用idle、question、unknown及各 agent 专属动画直观呈现哪个 Agent 正在干活。读完本文你将掌握 companion 的完整架构启动契约、单例锁、状态文件、动画渲染管线、它的 72 帧 JPEG 精灵表sprite sheet资源模型、窗口几何管理逻辑以及它如何作为 TS 插件的可视化补充层接入 oh-my-opencode-slim 的会话体系。1. 组件定位OpenCode 会话的可视化 UI 层companion 是 oh-my-opencode-slim 多 Agent 套件中的一个桌面可视化层代码位于仓库 companion/ 目录下。它的核心职责可以概括为一句话把每个 OpenCode 会话中正在运行的 Agent 状态翻译成屏幕上一组可移动的动画小窗。从 codemap 的 Responsibility 与 Integration 两节可以提炼出三个关键定位补充而非替代它为 OpenCode 会话充当 UI 可视化层与 TS 插件互补Integration 节原文即插件负责会话编排逻辑companion 负责把谁在活动这件事变得肉眼可见生态视觉锚点codemap 明确将其描述为oh-my-opencode-slim 生态中 agent 活动指示器的视觉锚点独立交付物它是一个独立的 Rust crateoh-my-opencode-slim-companion版本 0.1.4见 companion/Cargo.toml通过 src/cli/companion.ts 的installCompanion()按平台/架构下载对应发布归档并解压安装。companion 的技术栈可以从 companion/Cargo.toml 直接确认eframe/egui0.29 负责窗口与渲染image仅启用jpegfeature负责解码精灵表serde/serde_json负责状态文件序列化dirs定位用户数据目录winit0.30 管理事件循环Unix 平台额外依赖libc用于 PID 存活检测。2. 整体设计文件级状态共享 浮动动画叠加层codemap 的 Design 一节给出了 companion 的设计骨架通过基于文件的 JSON 状态共享与 OpenCode 宿主运行时通信并为活跃 agent 显示动画叠加层。结合源码可以展开为如下数据流TS 侧写入状态oh-my-opencode-slim 插件把会话信息session_id、cwd、active_agents、status 等写入一个 JSON 状态文件companion 侧监听状态Rust 进程通过轮询该文件mtime 变化触发更新感知会话变化companion 渲染动画为每个值得展示的会话窗口渲染对应的 agent 动画帧窗口持久化窗口的位置、尺寸、配置按项目目录记录在隐藏状态文件中重启后可恢复。Design 节列出的四个要点正好对应四个子系统下面分别展开。2.1 动画集合从 MP4 源视频到编译期嵌入的 JPEG 精灵表Design 第 4 条写得很明确Animations are pre-generated as 72-frame JPEG sprite sheets (12x6 grid, 200x200 each frame) from companion/VIDEOS/*.mp4 source videos.即源媒体companion/VIDEOS/ 存放 MP4 源视频council.mp4、designer.mp4、explorer.mp4、fixer.mp4、librarian.mp4、observer.mp4、oracle.mp4、orchestrator.mp4、question.mp4、unknown.mp4、idle.mp4等预处理产物每个视频被离线转成72 帧 JPEG 精灵表以12 列 × 6 行的网格布局、每帧 200×200 像素存放即 companion/animations/ 下的council.jpg、designer.jpg等文件编译期嵌入发布二进制通过 Rust 的include_bytes!宏把精灵表字节直接嵌入可执行文件实现见 companion/src/gifs.rs运行时不解码 MP4发布版本只加载嵌入的 JPEG 精灵表永远不会在运行时解码 MP4——这是 codemap Integration 节特意强调的交付边界release binaries embed the sheets from companion/animations/ via include_bytes! and never decode the MP4s。gifs.rs中Gifs::new()的映射表清楚地展示了动画名与精灵表的一一对应关系companion/src/gifs.rs动画名精灵表用途council/councillorcouncil.jpg议会 / 动态 councillordesignerdesigner.jpgdesigner agentexplorerexplorer.jpgexplorer agentfixerfixer.jpgfixer agentinputquestion.jpg等待用户输入introintro.jpg会话空闲 / 无活跃 agent 时的默认动画librarianlibrarian.jpglibrarian agentobserverobserver.jpgobserver agentoracleoracle.jpgoracle agentorchestratororchestrator.jpgorchestrator agentunknownunknown.jpg未识别 agent 的回退动画2.2 会话状态隐藏状态文件与按项目目录的配置持久化Design 第 3 条提到会话状态按项目目录跟踪窗口位置、尺寸和配置使用一个隐藏状态文件。这个文件的具体路径由 companion/src/state.rs 的state_file_path()决定若设置了环境变量XDG_DATA_HOME则路径为$XDG_DATA_HOME/opencode/storage/oh-my-opencode-slim/companion-state.json否则回退到~/.local/share/opencode/storage/oh-my-opencode-slim/companion-state.jsondirs::home_dir()解析主目录。状态文件的结构companion/src/state.rs由CompanionState承载包含四个顶层字段字段类型说明versionu32状态文件版本号sessionsVecSessionInfo会话列表每项含session_id、cwd、active_agents、active_agent、status、pid、可选的会话级configconfigOptionCompanionConfigState全局 companion 配置window_positionsBTreeMapString, WindowPositionState按规范化项目目录canonical project key保存的窗口坐标{x, y}配套的CompanionConfigStatecompanion/src/state.rs定义了持久化配置项注意其中两个字段在 JSON 里使用驼峰别名pub struct CompanionConfigState { pub enabled: bool, pub position: String, pub size: String, #[serde(default default_gif_pack, rename gifPack)] pub gif_pack: String, #[serde(default default_loop_style, rename loopStyle)] pub loop_style: String, #[serde(default default_speed)] pub speed: f32, }默认值由源码明确给出gifPack default、loopStyle classic、speed 1.0。window_positions上限为MAX_WINDOW_POSITIONS 100条companion/src/state.rs超出时通过prune_window_positions()逐出最旧的、非当前写入项目的条目避免状态文件无限膨胀。2.3 状态写入的并发安全文件锁 原子替换companion 的多个会话窗口会并发写同一个状态文件state.rs为此实现了两层保护companion/src/state.rsStateWriteLock文件锁以companion-state.json.lock目录作为互斥锁create_dir的AlreadyExists即已被占用最多等待 40 次 × 25ms约 1 秒超时返回WouldBlock错误Drop实现保证锁在写完后自动释放临时文件 原子重命名先写入companion-state.json.pid.tmp再用std::fs::rename原子替换正式文件避免读者读到半截 JSON。2.4 状态监听的实现方式250ms 轮询的 mtime 观察者companion 并不使用文件系统事件通知而是start_watcher()启动一个后台线程每250ms轮询状态文件的modified时间戳companion/src/state.rs一旦 mtime 变化就通过 mpsc 通道发信号主线程在poll()中try_recv()收取并重读状态文件。这种轮询 mtime的设计简单、跨平台、无外部依赖代价是最高 250ms 的感知延迟——对动画窗口场景完全可接受。3. 启动流程环境变量、单例锁、无边框透明窗口codemap 的 Flow 一节用 5 步概括了 companion 从启动到渲染的完整链路下面逐条结合源码验证。3.1 第一步通过环境变量识别归属会话Flow 第 1 条Companion reads OH_MY_OPENCODE_SLIM_COMPANION_SESSION_ID environment variable to identify its owner session.在 companion/src/main.rs 中main()首先读取该环境变量若缺失或全空白进程直接退出并记录exit missing owner_session_id。这个归属会话 ID决定了 companion 优先展示哪个会话见下文choose_owned_session。3.2 第二步基于文件的单例锁Flow 第 2 条Acquires a singleton lock via file-based coordination to prevent duplicate instances.实现位于 companion/src/singleton.rs其机制很巧妙锁文件路径为$XDG_DATA_HOME/opencode/storage/oh-my-opencode-slim/companion.safe_owner.pid同样回退~/.local/share其中safe_owner是 owner session id 经字符净化仅保留字母数字与_/-其余替换为_后的安全文件名companion/src/singleton.rs用OpenOptions::create_new(true)原子创建该文件并写入当前 PIDAlreadyExists则读取既有 PID若该 PID 仍存活Unix 下用libc::kill(pid, 0)探测判定为重复实例并返回false主程序随即退出若 PID 已死则视为陈旧锁删除后重试一次companion/src/singleton.rs。这套带 PID 活性检查的文件锁既防止了重复窗口又天然具备崩溃自愈能力——进程挂了锁文件里的 PID 自然失效。3.3 第三步编译期嵌入的动画资源Flow 第 3 条Loads agent sprite sheets embedded at compile time from companion/animations/ (include_bytes! in gifs.rs); state.rs is the Rust state module, not a data file.这条同时澄清了一个常见误解state.rs是 Rust 状态模块源码不是状态数据文件。精灵表在编译期经include_bytes!嵌入见 2.1 节表格并且采用惰性加载lazy-loadensure_texture()只在某个动画第一次被请求时才把对应 JPEG 解码为 egui 纹理并缓存companion/src/gifs.rs从而把启动成本降到最低。3.4 第四、五步Egui 窗口渲染与状态持久化Flow 第 4 步在 Egui 窗口中为每个活跃会话渲染动画叠加层其核心是CompanionAppcompanion/src/app.rs实现eframe::App::update()的每帧逻辑轮询状态 → 选择展示会话 → 计算窗口几何 → 绘制动画帧 → 处理拖拽与右键菜单。Flow 第 5 步的持久化状态按项目目录存储窗口几何与配置即 2.2 节描述的companion-state.json。3.5 关键启动配置Accessory 模式与透明无边框窗口companion/src/main.rs 中eframe::NativeOptions的设置解释了不占 Dock、不抢焦点的体验从何而来with_decorations(false)with_transparent(true)无边框、透明窗口让动画直接浮在桌面上with_always_on_top()置顶显示with_active(false)窗口激活时不抢终端焦点macOS 下通过 winit 的ActivationPolicy::Accessory将应用注册为附属应用无 Dock 图标、不显示在 App 切换器中并with_activate_ignoring_other_apps(false)确保窗口弹出时不打断当前输入初始内尺寸 120×120与中等尺寸预设一致。4. 动画引擎帧索引、循环模式与速度归一化companion 的动画并非真正的 GIF 播放而是按时间计算当前应显示的精灵表子区域UV 矩形。核心函数在 companion/src/gifs.rs4.1 帧索引算法frame_index(time_seconds, speed, loop_style)companion/src/gifs.rs以24 FPSFRAME_RATE 24.0、72 帧FRAME_COUNT 72为基础classic经典循环tick % 72播放到最后一帧后直接回到第 0 帧形成跳跃式循环smooth平滑乒乓周期为72 * 2 - 2 142先正向播到第 71 帧再反向回放且不重复端点71 之后是 70而不是 71实现无缝往返动画。测试 companion/src/gifs.rs 专门断言了该乒乓不重复端点行为。4.2 速度归一化normalized_speed()companion/src/gifs.rs把任意输入速度钳制到 0.254.0非有限值如NaN回退为默认 1.0。测试覆盖了三个典型边界NaN → 1.0、0.1 → 0.25、9.0 → 4.0。4.3 UV 映射与纹理采样frame_uv(index)companion/src/gifs.rs按 12 列 6 行把精灵表切成 72 个归一化 UV 矩形decode_sprite_sheet()用imagecrate 把 JPEG 解码为 eguiColorImage交给ctx.load_texture上传为 GPU 纹理使用TextureOptions::LINEAR平滑采样。渲染时render_session()按网格布局逐帧绘制AnimationFrame纹理 ID UV并在窗口底部叠加一个半透明黑色标签条显示项目名companion/src/app.rs。4.4 Agent 名解析规则resolve_name(agent, gif_pack)companion/src/gifs.rs决定了这个 agent 该放哪段动画gif_pack ! default→ 一律回退到orchestrator动画agent 名以councillor-前缀开头动态 councillor如councillor-alpha→ 使用council动画在精灵表映射中精确命中的已知 agentorchestrator、explorer、librarian、observer、oracle、designer、fixer等→ 使用各自动画其余未知 agentplan、build、general、explore、custom-agent等→ 使用unknown动画。对应测试分布在 companion/src/gifs.rsunknown_agents_use_the_unknown_animation、known_agents_keep_their_own_animations、dynamic_councillors_use_the_council_animation以及断言unknown与observer动画确实不同的unknown_animation_is_distinct_from_observer。5. 窗口几何管理锚定、拖拽、尺寸预设与多会话堆叠app.rs是窗口行为的中枢包含大量可直接引用的设计常量与算法。5.1 尺寸预设全局常量companion/src/app.rs预设单格尺寸S80 pxM120 pxL160 pxXL200 px配置字符串到像素的换算由size_from_config()完成companion/src/app.rs支持small/medium/large/xl|xlarge未知值回退默认 120px。注意右键菜单里 S/M/L/XL 的选择属于本地临时状态存放在 egui 的临时数据中只有当配置文件中的配置元组position/size/gifPack/loopStyle/speed发生变化时才会被配置覆盖——测试config_tuple_change_detection_preserves_local_picker_on_session_updatescompanion/src/app.rs验证了这一行为。5.2 锚定布局place_window(position, screen, win)companion/src/app.rs把窗口按锚点放置四周保留GAP 10pxbottom-left→(GAP, screen_h - win_h - GAP)top-right→(screen_w - win_w - GAP, GAP)top-left→(GAP, GAP)其余含默认bottom-right→(screen_w - win_w - GAP, screen_h - win_h - GAP)坐标最终会被钳制到[GAP, screen - win - GAP]区间即使窗口比屏幕还大也会尽力贴边。app.rs中的多组测试bottom_right_stays_anchored_when_height_grows、top_right_moves_left_when_width_grows、oversized_window_uses_best_effort_gap_anchor等见 companion/src/app.rs严格验证了窗口尺寸变化时锚点始终贴边这一特性。5.3 多会话堆叠多个会话同时展示时stack_window_position()companion/src/app.rs按会话在列表中的排名施加最多 8 级、每级 18px的偏移右下锚点向左上堆叠、左上锚点向右下堆叠等方向与锚点相反避免窗口完全重叠。5.4 会话选择优先级choose_session()companion/src/app.rs决定展示哪个会话优先级依次为状态为waiting-input等待用户输入的会话存在非intro活跃 agent 的会话状态为busy的会话兜底最新保留的会话。而choose_owned_session()companion/src/app.rs会优先匹配OH_MY_OPENCODE_SLIM_COMPANION_SESSION_ID指定的归属会话归属会话缺失时才回退到上述全局选择。对应的waiting_input_wins、non_intro_active_agents_win_over_idle_intro、busy_wins_when_no_active_agents、falls_back_to_newest_retained_session等测试companion/src/app.rs逐一锁定了这些规则。5.5 会话存活清理poll()中companion/src/app.rs会对会话按 PID 做存活清理现代配置模式下has_modern_config为 true以is_pid_alive()Unix 用libc::kill(pid, 0)筛掉已退出会话对应的窗口没有现代配置时保留所有会话避免误杀。当归属会话消失或会话列表为空时窗口自动关闭companion/src/app.rs。5.6 拖拽与右键菜单拖拽左键按下菜单未打开时触发egui::ViewportCommand::StartDrag启动系统级窗口拖动companion/src/app.rs释放时把窗口outer_rect的左上角坐标写入状态文件的项目条目companion/src/app.rs并清空已应用的几何缓存以便下次重定位。测试drag_gesture_via_context_emits_exactly_one_start_drag验证了按住期间不会重复触发StartDragdrag_start_suppressed_when_menu_open_via_context验证了菜单打开时拖拽被抑制。右键菜单右键弹出 S/M/L/XL 尺寸选择器render_size_pickercompanion/src/app.rsEsc或点击菜单外区域关闭Close按钮触发companion_quit退出整个应用。5.7 双显示器位置的智能恢复restore_window_position()companion/src/app.rs处理了一个常见痛点egui 0.29 只暴露显示器尺寸而不暴露原点若保存的位置落在原点为零的视口之外例如副屏坐标 2200×80 或负原点屏 -900×40原样保留而不强行拉回主屏只有落在原点零视口内才做钳制。测试restore_preserves_negative_origin_monitor_positions与restore_preserves_positive_offset_secondary_monitor_positionscompanion/src/app.rs锁定了这一策略。6. 平台适配macOS 屏幕尺寸探测与 Niri 浮动窗口回退companion 针对桌面 Linux 与 macOS 各有一套平台适配逻辑。6.1 macOS通过 Finder 获取桌面尺寸companion/src/screen.rs 在 macOS 上执行osascript调用 Finder 获取桌面窗口边界解析出主屏逻辑尺寸[width, height]其他平台无法探测时回退默认[1440.0, 900.0]。6.2 Niri 合成器浮动窗口位置的外部修正在基于 Niri最多重试 6 次、每次间隔 150ms期间若代数generation已更新新一轮几何变化则放弃避免竞态调用niri msg --json windows与niri msg --json outputs查询当前窗口与输出布局按 PID app_id/title is_floating匹配 companion 窗口matches_windowcompanion/src/niri.rs根据锚点或保存的目标位置在对应输出显示器上计算期望坐标再换算为move-floating-window --id id -x dx -y dy的相对位移命令build_move_argscompanion/src/niri.rs正增量用前缀位移超过max(output.width, output.height) * 2视为异常而放弃防止误操作。niri.rs内置了 7 窗口 双输出布局的 JSON fixture 测试companion/src/niri.rs覆盖 PID/身份匹配、非浮动窗口拒绝、跨输出坐标计算、增量钳制等场景包括resolve_move_uses_niri_output_bounds、custom_position_uses_target_output、non_zero_origin_output_places_relative_to_that_output等。7. 配置体系从 TS Schema 到状态文件的参数映射companion 的配置并非只在 Rust 侧定义TS 插件的配置 Schema 才是用户入口。在 src/config/schema.ts 中CompanionConfigSchema定义了以下可用参数参数类型 / 取值默认说明enabledboolean—是否启用 companionbinaryPathstring—自定义 companion 二进制路径替代自动下载安装positionbottom-right/bottom-left/top-right/top-leftbottom-right窗口锚定位置sizesmall/medium/largemedium窗口尺寸档位另有菜单 XL 档gifPackdefaultdefault内置动画包选择当前仅defaultloopStyleclassic/smoothclassic动画循环方式经典循环或平滑乒乓speednumber0.254.01.0动画播放速度倍率debugboolean—是否输出 native companion 调试日志参数取值范围与 Rust 侧实现严格对齐position的四档与place_window()的四种锚点一一对应见 5.2 节size的small/medium/large对应 80/120/160pxspeed的 0.254.0 与normalized_speed()的钳制区间一致见 4.2 节loopStyle的classic/smooth分别映射frame_index()的两种循环算法见 4.1 节配置解析后经 TS 插件写入状态文件Rust 侧由config_for_owner()优先取归属会话的会话级config否则回退全局configcompanion/src/app.rs最终经apply_config()应用到运行时companion/src/app.rs。调试开关方面Rust 侧日志由环境变量OH_MY_OPENCODE_SLIM_COMPANION_DEBUG1控制日志写入$XDG_DATA_HOME/opencode/log/oh-my-opencode-slim-companion.pid.logcompanion/src/log.rs。8. 安装与运行二进制发布、目标探测与集成注入8.1 发布形态与自动安装companion 以预编译二进制发布清单文件 src/companion/companion-manifest.json 记录了版本0.1.3、对应 tag 以及五个平台的 SHA-256 校验和x86_64-apple-darwin、aarch64-apple-darwin、x86_64-pc-windows-msvczip、x86_64-unknown-linux-gnu、aarch64-unknown-linux-gnutar.gz。仓库内源码版本为 0.1.4见 companion/Cargo.toml发布清单会随迭代更新。安装逻辑位于 src/cli/companion.ts 的installCompanion()先通过getCompanionTarget()探测当前platform/arch是否有对应归档不支持的平台直接失败再按oh-my-opencode-slim-companion-vversion-target.ext拼接归档名下载经校验后解压到getCompanionBinaryPath()指定的位置支持--dry-run预览。companion/Cargo.toml 的 release 配置opt-level 3、lto true、codegen-units 1、strip true说明发布二进制经过了体积与性能优化。8.2 宿主集成方式companion 通过OH_MY_OPENCODE_SLIM_COMPANION_SESSION_ID环境变量注入 OpenCode 运行时codemap Integration 节实现随会话而生、随会话而终会话启动时插件拉起 companion 进程并注入归属会话 IDcompanion 启动后即可定位自己的状态与配置3.1 节归属会话结束后companion 因归属会话缺失或会话列表清空自动关闭窗口退出5.5 节。整个过程用户无需手动开关——它在幕后作为 oh-my-opencode-slim 会话活动的视觉锚点持续工作。9. 总结一条从会话状态到桌面动画的完整链路把 codemap 的 Flow 与源码对应关系汇总成一张对照表可以一目了然地看到每步的落点codemap 步骤源码落点关键机制1. 读取 owner session 环境变量companion/src/main.rsOH_MY_OPENCODE_SLIM_COMPANION_SESSION_ID缺失即退出2. 获取单例锁companion/src/singleton.rsPID 文件锁 陈旧锁自愈3. 加载编译期嵌入精灵表companion/src/gifs.rsinclude_bytes! 惰性纹理解码4. Egui 窗口渲染动画companion/src/app.rs帧索引 UV 采样 网格布局5. 状态持久化companion/src/state.rscompanion-state.json 文件锁 原子替换companion 的价值在于把多 Agent 会话的后台活动变成桌面上可感知、可交互的动画层文件级状态共享让它与 TS 插件解耦且跨语言协作编译期嵌入精灵表让发布产物零运行时媒体依赖单例锁、PID 存活检查、原子写入让它在多会话并发的真实环境中稳定运行而 Niri 回退与 macOS Accessory 模式则保证了跨平台体验的一致性。对于希望为桌面 Agent 工具构建类似活动可视化浮窗的开发者companion/src/app.rs 的窗口几何管理、companion/src/gifs.rs 的精灵表动画引擎与 companion/src/state.rs 的并发安全状态写入都是可直接借鉴的实现蓝本。赞分享人工智能AI AgentAgent 编排AI 技能【免费下载链接】oh-my-opencode-slimLean, fine tuned Opencode multi agent suite · Mix any models · Auto delegate tasks项目地址https://gitcode.com/gh_mirrors/oh/oh-my-opencode-slim点击查看免费下载相关推荐oh-my-opencode-slim 桌面伴侣 Companion浮动 Agent 状态叠加层配置、安装与自更新机制全解oh my opencode slim 桌面伴侣 Companion浮动 Agent 状态叠加层配置、安装与自更新机制全解 导读Companion 是 oh人工智能AI AgentAgent 编排AI 技能oh-my-opencode-slim Codemap Skill 实战指南基于 Hash 状态实现仓库地图的增量维护与 Agent 协同oh my opencode slim Codemap Skill 实战指南基于 Hash 状态实现仓库地图的增量维护与 Agent 协同 导读 面对一个陌生人工智能AI AgentAgent 编排AI 技能oh-my-opencode-slim Companion 动画资产流水线从 MP4 源视频到 12×6 嵌入式精灵图Sprite Sheetoh my opencode slim Companion 动画资产流水线从 MP4 源视频到 12×6 嵌入式精灵图Sprite Sheet 本篇指南聚人工智能AI AgentAgent 编排AI 技能上一篇iii Workers 接入指南从脚手架到优雅下线的完整生命周期下一篇turbovec 的 LangChain 集成以 TurboQuantVectorStore 作为 InMemoryVectorStore 的高压缩替代方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表