Contextify 为本地的 Claude Code 和 Codex 会话建立 SQLite/FTS 索引,方便你搜索和阅读 AI 编程历史记录。它可以纯本地运行;你也可以选择通过托管或自托管的云同步,在另一台机器上查看这些记录。
有些用户想要 Windows 客户端,因为他们的自托管环境里同时有 Mac、Linux 和 Windows 机器在产生 CLI AI 对话记录,有时还是无界面(headless)运行。Contextify 最初是一个 macOS 应用;1.8.0 把它带到了 Windows(x64 和 ARM64),并扩展了 Linux CLI。在 Windows 上,它由一个后台进程和一个 CLI 组成,暂时还没有浏览历史记录的界面。
做成这件事需要大量有意思的工作,其中包括一个共享的 Swift 核心,而且还要保证它能像发布之前一样方便地更新。下面是其中比较难的部分。
共享的 Swift 核心
解析器、数据库结构(schema)与迁移、SQLite + GRDB + FTS5、查询/导出以及云同步,都属于同一个 Swift 代码库,由 macOS 应用、Linux CLI 和 Windows 客户端共享。在 Windows 上,这个核心以常驻引擎(contextify-windows-engine.exe)运行,.NET/Avalonia 托盘应用只需通过 stdio 上的换行分隔 JSON 与它通信。
我最初并不是这样构建的。第一个 Windows 客户端其实是用 C# 完整重写的,有自己的解析器、SQLite 和同步。这是个错误。我意识到各平台的实现会不断出现偏差(drift),而且 Linux 和 macOS 之间已经共享了 Swift 代码,把 Windows 也纳入进来要好得多。
我做了一次技术验证(spike),证明 Swift 6.3.3 + GRDB + 随项目打包的 SQLite 能在 Windows 11 ARM64 上完成 FTS5 写入和搜索的往返测试,然后基本上推倒重来。这次调整很有价值,让我走上了构建真正共享核心的路。(ARM64 与 x64 的问题后面再说。)
一路发现的缺口
有一件事拉长了开发时间:专注于 Windows 版本的构建和验证时,我发现了之前遗漏的 macOS 与 Linux 应用之间的行为差异。用户还没有报告这些问题,但我看到智能体在 Linux 上运行 Contextify CLI 时遇到意外错误,心里直犯嘀咕:嗯???
所以有一阵子,我一边往 Swift 核心里加东西,一边把它们反向移植到 Linux 应用,还把专门为这些缺口创建、却淹没在待办列表(backlog)里的工单重新翻了出来。
在 Mac 上构建 Windows x64(手头没有 x64 机器)
刚开始时,我没意识到 x64 Windows 执行环境和 macOS 上 Rosetta 运行 x64 应用的那种魔法完全不是一回事。我认识到,在 Apple 芯片上通过模拟编译 x64 根本不现实:构建时间太慢,没法迭代。
我给真正的构建机器询过价,但构建和 QA 的需求时有时无,我说服不了自己去买一台。所以我改为编写 DevOps 工具,按需分配和释放 Azure 虚拟机实例,用于 QA 和构建。
转向 Azure 之前,我先用了手头现成的东西:GitHub 托管的 runner。但冷启动和性价比都不理想,光是启动和销毁就要 40 多分钟。我现在用的 Azure x64 机器在空闲时会释放(deallocate),只在实际编译时计费。包括启动和销毁在内,一个完整的冷启动周期大约 13 分钟。
摆脱 GitHub 的 runner
这段时间 GitHub 还出了两次严重故障,让 Windows 版的发布之路又拉长了一截。我彻底重构了 CI,完全不再依赖托管 runner。除 x64 之外,现在所有构建和签名都在我自己的硬件上完成:一台基础款 Mac mini M4(很快会升级为 M5 Pro)和我的 MacBook Air M5。唯一的例外是 x64,它使用上面提到的空闲时释放的 Azure 机器。
为 Windows 版本签名
未签名的 Windows 应用会给用户弹出警告,让他们不要运行。Contextify 至少必须获得 Windows 11 的信任。
Microsoft 推荐的低成本 Azure 签名服务纯属浪费时间。它要求先通过第三方身份核验,而这项核验就是不接受我的身份,反复失败,既不给原因,也没有错误代码。Microsoft 的文档由某种 AI 驱动,根本不好用。唯一能免费联系到 Microsoft 的途径是他们的支持论坛,我到现在还没收到回复。一些付费购买支持的人也发过类似的问题,同样没有回应。
次优的选择似乎是 SSL.com 的证书,通过他们的云服务签名。我纠结过要不要自己买一个 YubiKey,最后还是直接为他们的云密钥付了钱。一旦我认命付了钱,身份审核几乎不费吹灰之力就通过了。
SSL.com 的网站烂得离谱。且不说设计和导航,他们的界面展示的信息不完整,而支持机器人显然早就准备好了对应的变通办法。他们不去修网站,而是把变通办法放进了支持聊天里。SSL.com 在产品维护和支持成本控制上达到了新的高度。
好笑的是,完成身份验证后,你看到的第一样东西,就是如何注册成为推广联盟成员、创建推广链接,再转售他们的产品。
Microsoft 要么管好自己的签名渠道,要么要求合作伙伴提供更好的体验。
一个有意思的 Swift bug
Swift 在 Windows 上真的能跑(!),但它的并发运行时还没有 Mac 上成熟。
常驻引擎在 Windows x64 上崩溃了,在 swift_task_localValuePush 内部发生访问冲突(0xC0000005),当时它连一个参数都还没解析。同样的 task-local 代码在 macOS 上运行正常,在 Windows x64 上却直接硬崩溃:没有错误,直接访问冲突。就我的情况而言,原因是通过嵌套的 @TaskLocal 值传递引擎状态。我的修复办法是沿调用链向下传递一个显式的 Sendable 上下文。
总之我了解到,在 Windows 上遇到 async Swift 的访问冲突并不罕见,而这些问题在 Darwin 上从不出现。
应用本身
安装程序按用户安装,无需提权:不需要管理员权限,不安装 Windows 服务,不用 MSI,而且会自动更新。我已经提交了 PR,申请加入 winget;目前请从网站或 GitHub 下载已签名的安装程序。
它是本地优先的:引擎是唯一写入数据库的组件,云同步需要登录后才能选择开启。我最希望收到的反馈是:在真实的 Windows 环境中,索引或同步是否会出问题、何时出问题,以及对应用整体用户体验的任何意见。