博客
从原型到产品:Tactic Remote 的开发历程
8 周内完成 13 个里程碑,将 Tactic Remote 从概念验证的 tmux 封装工具打造为生产就绪的远程开发工具——这是完整的开发历程。
继续查看该栏目下的产品与工程更新。
浏览此分类Tactic Remote 起源于 2026 年 1 月初的一个问题:如果你能在手机上查看正在运行的 Claude Code 会话会怎样?这个问题在一个周末变成了原型,两周内变成了一个可用的工具,并在我们发布第一个版本号之前完成了 13 个里程碑。这是那段开发弧线的故事——我们构建了什么、丢弃了什么,以及我们从快速发布而不发布有问题的产品中学到了什么。
周末原型(2026 年 1 月初)
Tactic Remote 的第一个版本大约只有 200 行代码。一个 shell 脚本在 tmux 会话中启动 Claude Code,一个 Python Flask 服务器暴露了一个端点来执行 tmux capture-pane 并以纯文本返回输出,一个基础的 iOS 应用每 2 秒轮询该端点,并将结果显示在等宽文本视图中。
它又丑又慢。轮询模型意味着每次更新有 2 秒的延迟。纯文本捕获丢失了所有格式。没有认证——同一 Wi-Fi 网络上的任何人都可以读取你的终端。但它验证了概念:你确实可以从手机屏幕上有意义地跟踪一个 AI 编程会话。
那个原型大约存活了 4 天,然后我们开始重建。
里程碑式架构
我们采用了基于里程碑的开发模式,而非日历冲刺。每个里程碑都有一个特定的技术目标、一个完成定义,没有固定的截止日期。这让我们可以在准备好时发布,而不是在冲刺结束时发布。实际上,每个里程碑平均耗时 4-5 天,尽管有些只用了 2 天,有一个则用了将近两周。
以下是浓缩版的进展:
里程碑 1-3:基础建设。 用 Swift 原生嵌入式服务器替换 Flask。建立 WebSocket 进行实时流传输以取代 HTTP 轮询。实现基于令牌的认证。完成里程碑 3 后,我们有了一个工作系统,iPhone 在 200ms 内接收终端更新,未经授权的访问被拦截。
里程碑 4-5:会话管理。 支持多个并发 tmux 会话。构建会话生命周期(创建、监控、空闲、完成、清理)。通过在启动时重新附加到现有 tmux 会话,增加跨伴侣应用重启的会话持久性。这是 Tactic Remote 从演示变为可用于实际工作的转折点。
里程碑 6-7:人在回路中。 集成 Claude Code 的 hook 系统以拦截审批请求。在 iOS 上构建审批界面——显示 AI 想要做什么、展示相关上下文、提供批准/拒绝操作。添加推送通知,使审批不需要应用处于打开状态。这两个里程碑是转折点。在此之前,Tactic Remote 是一个终端查看器。在此之后,它成为了一个控制台。
里程碑 8-9:连接健壮性。 处理 Wi-Fi 到蜂窝网络的切换。实现带指数退避的自动重连。添加连接质量指示器。构建二维码快速连接流程以消除手动输入 IP 和令牌。里程碑 9 是最难的单个里程碑——iOS 上的网络边缘情况众多且文档不足。
里程碑 10-11:Cloudflare Tunnel。 将 cloudflared 集成为托管子进程。构建双模式连接架构(本地和隧道)。处理隧道健康监控、自动重启和优雅降级。将隧道 URL 添加到二维码生成中。
里程碑 12-13:生产就绪。 本地化(7 种语言)。Mac 休眠防止。菜单栏界面润色。崩溃报告和诊断。性能优化,将空闲时的内存占用从约 60 MB 降低到 25 MB 以下。认证和传输层的最终安全审计。
关键技术决策
开发过程中的几个决策定义了产品的特质:
tmux 而非自定义 PTY 管理
我们在里程碑 1 期间对此进行了广泛讨论。自定义 PTY 管理将给我们更精细的终端捕获控制,并消除 tmux 依赖。但这也意味着重新实现会话持久性、终端模拟和进程生命周期管理——这些问题 tmux 已经解决了 15 年。我们选择站在这个基础之上,而不是重建它。
WebSocket 而非 Server-Sent Events
对于实时流传输,两种协议都可行。我们选择 WebSocket 是因为我们需要双向通信——iPhone 不仅接收终端输出,还需要向 Mac 发送命令和审批。SSE 需要一个单独的通道用于上行通信,增加了复杂性。
嵌入式服务器而非独立守护进程
从 macOS 架构的角度来看,由 launchd 管理的独立守护进程会更"正统"。但这会使安装复杂度翻倍,并产生一类"服务器在运行但应用没在运行"的问题。将服务器嵌入菜单栏应用意味着只需安装一个东西、运行一个东西、退出一个东西。
静态令牌而非 OAuth/基于会话的认证
Tactic Remote 的威胁模型很直接:防止来自网络的未经授权访问。存储在钥匙串中的 256 位加密随机令牌可以实现这一点,而无需令牌刷新、会话过期或登录流程的复杂性。对于需要按用户认证的企业用例,我们建议将 Cloudflare Access 作为外部层。
轮询 tmux 而非文件系统监听
我们以 200ms 的间隔轮询 tmux capture-pane,而不是使用文件系统通知 API(kqueue/FSEvents)监听 PTY。这听起来不够优雅,但 tmux 的 capture 命令极快(亚毫秒级),而在我们的测试中,PTY 设备上的文件系统监听在不同 macOS 版本间被证明不够可靠。轮询方式在 macOS 13、14 和 15 上都非常稳定。
我们丢弃的东西
并非所有东西都保留下来了。一些值得注意的删减:
基于 Web 的客户端。 我们在里程碑 6 期间构建了一个可在任何浏览器中运行的响应式 Web 界面。它能工作,但体验明显不如原生应用——没有推送通知、没有后台重连、审批时没有触觉反馈。我们搁置了它,专注于原生 iOS 体验。
终端颜色和格式。 早期版本尝试保留 ANSI 颜色代码并在 iPhone 上渲染。渲染效果不一致,且给流传输协议增加了显著复杂性。我们切换到一种简化的渲染模型,保留结构但不追求像素级精确的终端模拟。完整的 ANSI 支持仍在路线图上。
自动任务建议。 我们原型了一个功能,Tactic Remote 根据你最近的 git 活动来建议 Claude Code 任务。技术上很有趣,但感觉过于自以为是——开发者知道他们想做什么。我们在任何测试用户看到之前就移除了它。
版本管理转型
在里程碑 13 之后,我们从基于里程碑的追踪转向语义化版本管理。第一个带版本号的发布是 1.2.0(不是 1.0.0,因为我们在封闭测试期间有编号为 1.0 和 1.1 的内部构建,我们不想造成混淆)。
此后的版本进展:1.2.0、1.3.0、1.4.0、1.4.2 和 1.5.0-beta.1。每个版本都对应一组明确的面向用户的变更,记录在我们的更新日志中。里程碑体系在快速原型阶段为我们服务得很好,但语义化版本管理能更清晰地传达给需要评估升级风险的用户。
我们学到的
发布最小的有用之物。 那个周末原型按任何工程标准来看都很粗糙,但它回答了唯一重要的问题:从手机上监控 Claude Code 真的有帮助吗?之后的每个里程碑都是对"下一个最有用的改进是什么?"的渐进式回答。
网络是最难的部分。 我们在连接可靠性上花费的时间超过了产品的任何其他方面。Wi-Fi 到蜂窝网络的切换、休眠后的 WebSocket 重连、Cloudflare Tunnel 健康监控——这些每一项都消耗了一个完整的里程碑。实际的终端捕获和会话管理相比之下反而简单直接。
依赖应该是无聊的。 tmux、cloudflared、钥匙串、IOKit——Tactic Remote 依赖的技术都是稳定且文档完善的。每次我们考虑采用更新或更新颖的东西时,我们都会问现有的"无聊"选项是否足够好。通常答案是肯定的。
基于里程碑的开发适合小团队。 没有冲刺计划、站会和速度追踪的开销,我们行动更快。每个里程碑都有明确的终点线,我们可以在实施过程中根据所学实时调整范围。这种模式在较大的团队规模下会失效,但对我们的早期开发来说恰到好处。
时间线概要
| 时期 | 发生了什么 |
|---|---|
| 2026 年 1 月初 | 周末原型:shell 脚本 + Flask + 基础 iOS 应用 |
| 1 月 6-20 日 | 里程碑 1-5:基础建设、WebSocket、会话 |
| 1 月 20-29 日 | 里程碑 6-9:审批、推送通知、二维码连接 |
| 1 月 29 日 - 2 月 10 日 | 里程碑 10-11:Cloudflare Tunnel 集成 |
| 2 月 10-18 日 | 里程碑 12-13:本地化、润色、安全审计 |
| 2 月 18 日起 | 语义化版本:1.2.0 至 1.5.0-beta.1 |
从一个想法到公开测试版,八周时间。不是因为我们偷工减料,而是因为我们无情地削减范围,并在经过验证的基础上构建。
有关每个版本发布的详细信息,请查看我们的更新日志。