npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@brglng/pi-session-sync

v0.1.0

Published

Sync Pi coding agent session files to an external mirror directory with three-way conflict merging. Per-project session directories use portable names compatible with pi-portable-sessions, but this extension is independent and does not depend on or read t

Readme

@brglng/pi-session-sync

Pi coding agent 的 session 文件 同步到外部镜像目录,并支持三方冲突合并

Pi 默认把每个项目的 session 以 JSONL 文件存放在 ~/.pi/agent/sessions/。本扩展把它们镜像到可配置的外部根目录,并使用可移植 的项目目录名,让同一项目在不同机器上映射到相同位置。可移植命名由本扩展独立 实现——它只依赖普通文件系统,既不创建也不跟随符号链接桥。

工作原理

项目默认 session 目录:

~/.pi/agent/sessions/--Users-alice-my-project--/*.jsonl

被镜像到外部同步根目录下的可移植名:

<syncRoot>/HOME%2Fmy-project/*.jsonl

每个项目的同步状态存放在:

<syncRoot>/.pi-session-sync/sessions/<portable-name>/

包含该项目的 manifest、最近基线快照、冲突归档、备份和锁。按项目分片保存状态, 这样单项目同步就无需全局锁。

同步根目录还包含一个根级元数据文件:

<syncRoot>/.pi-session-sync/global.json

它只保存格式元数据(kind: "global"formatVersion);所有项目状态仍在各自 分片内。该文件在首次同步时创建;已存在但格式损坏或使用不受支持的格式版本时,同步会 在任何写入前拒绝执行,从而及时发现外部目录被误用或损坏。requireGit: true 时, 该文件属于扩展管理路径,会被暂存并提交。

同步时机

  • 退出时同步(默认开启): Pi 退出前,当前项目的 session 文件与镜像对账。
  • 启动时同步(可选、实验性): 运行前拉取外部变更。若当前 session 文件被 替换,内存中的会话已过期,必须重启 Pi(当前公开 API 无法安全地针对外部 cwd 重建 runtime)。
  • /session-sync sync 命令: 按需同步当前项目。

支持 scope: "current"scope: "all"。配置 scope: "all" 时,扩展会枚举 sessions 根目录下的所有项目,在单个全局锁下逐个同步,并在最后对所有受管路径 做一次 Git 提交。

变更检测与合并

每个同步文件都与上次基线快照比较。单侧变更直接复制;只有一侧变更则应用; 两侧都变则对 entry 树做确定性三方合并。当无法在不猜测的情况下合并时,两侧分歧 版本会被快照到该项目的冲突归档,文件本身保持不变。

策略:

  • sameIdConflictconflict | newer-wins):同一 entry.id 的两份分歧版本 如何处理。默认 conflict
  • deletionConflictkeep-modified | keep-deleted | conflict):一侧删除 文件/entry 而另一侧修改时如何处理。默认 keep-modified
  • initializemerge | local-wins | external-wins | abort):首次同步时 两侧持有分歧文件且没有基线快照时如何处理。默认 merge,会尝试对两份 entry 树做确定性的结构合并(分歧的同 entry.id 遵循 sameIdConflict);当合并结果 无法安全解析或校验时,两侧版本归档为冲突且不写入。abort 总是归档; local-winsexternal-wins 用一侧覆盖另一侧。

session 文件在复制或合并前都会经过严格校验:header 必须包含 id、cwd 和 ISO-8601 时间戳(逐项校验日历/时钟分量,不可能的值如 13 月或 2 月 31 日会被拒绝, 而不是被静默归一化),每个 entry 必须包含 id、显式的字符串/null parentId、 严格合法的时间戳和完整的父子树。非法文件会连同原始字节一起归档为冲突,绝不会 被复制进 Pi 或镜像。

镜像、基线快照和冲突归档中的 header cwd 始终保存为可移植路径 pi-session-sync://<portable-name>,绝不把机器绝对路径留在同步目录;写回本地 Pi session 目录时,再反向把该 URI(或旧镜像遗留的机器绝对 cwd)映射为当前机器的 项目 cwd。无法安全验证或映射(机器路径/URI 不属于该项目、格式损坏、空值)时 拒绝写入并归档,不猜测改写。

每个文件的 header cwd 必须通过可移植命名规则映射到它将被同步的项目分片;不 匹配时归档该文件而不是复制。共享侧(镜像、基线快照、冲突归档)存储的是规范可移植 URI;写回本地时再反向映射为当前机器项目 cwd。同一 session.id 出现在 两个不同文件名下(同一侧或本地/镜像两侧)会被归档为身份冲突,而不是静默合并。

所有破坏性步骤都保证旧内容可恢复:替换或删除前先把现有各侧备份(每文件最新备份, 外加多文件事务的逐操作 rollback journal),删除只有在每一侧都留有可恢复副本后才 传播。备份、journal 文件及其路径在每次复制、恢复或删除前都会用相同的符号链接/路径链 防护重新检查,因此操作中途的符号链接替换不能把备份、回滚或清理重定向到受管根目录 之外(非 kernel CAS 仍留有狭窄窗口,属已接受限制)。若回滚或清理无法安全完成,副本 会被原地保留并报告路径供手动恢复,自动 Git checkpoint 也会暂停。失败的冲突归档写入 会移除本次调用创建的快照文件;任何无法安全移除的产物会被保留并随错误一起报告, 因此失败的归档操作既不会遗留无主快照,也不会沿中途被替换的路径删除。

当共享 session 写入本地 Pi session 目录时,如果映射无歧义(父文件的 basename 存在于本项目的本地或镜像目录,且存储路径是绝对 Pi sessions 路径), header.parentSession 会被归一化到当前机器的项目目录,同时 header.cwd 会从 规范 URI 反向映射为当前机器的项目路径(旧镜像遗留的绝对 cwd 同样迁移)。镜像和 基线快照始终保留规范 URI 形式,header.cwd 在共享侧会被改写为可移植 URI,避免机器 路径泄漏。 有歧义或不安全的父引用保持不变。

entry 级 tombstone 在所有写入路径上生效,包括单侧复制和删除解决:被删除的 entry 永远不会复活。如果移除 tombstone 标记的 entry 会导致孤儿节点或破坏树结构, 写入会被拒绝并归档冲突。tombstone 只能通过显式 /session-sync resolve 清除。

Git

requireGit: true(默认)时,同步根目录必须是 Git 仓库。扩展只暂存并提交 自己管理的路径,绝不暂存或提交无关用户文件。它不执行 fetchpullpush。当 requireGit: false 时,同步直接在普通文件系统上进行。

嵌入 Git pathspec 的所有用户派生值(可移植项目目录名)都按字面匹配 (:(literal)),运行时锁目录用 :(exclude,literal) 显式排除,因此标签永远 不能充当 Git pathspec 魔法或 glob 来改变被暂存/提交的文件集。

每次对账之后(以及每次显式冲突解决之后),扩展会在 fail-closed 防护下做一次 checkpoint 提交:受管路径在操作前必须干净;HEAD 在操作期间不得移动;当前唯一受管 改动必须恰好是本次操作写入或删除的文件——porcelain 中触及受管路径的 rename/copy 行始终阻止提交(扩展自己从不暂存或移动文件),被保留的恢复产物同样 阻止提交;本次同步创建的 global.json 仅在仍携带本次写入的完全一致字节时才被提交。 被解决记录的已移除冲突快照文件计入该次 resolve 自身的删除,并随它一起提交。resolve 上的每次跳过或 Git 失败都会作为已成功解决结果的提示信息展示,绝不会把已解决的冲突 重新变成失败。

安装

npm install -g @brglng/pi-session-sync

包通过 pi.extensions 注册,会被自动加载。

配置

扩展读取标准的 Pi 扩展配置文件:

  • 全局:~/.pi/agent/extensions/pi-session-sync/config.json
  • 项目级:<cwd>/.pi/extensions/pi-session-sync/config.json

项目级配置覆盖全局配置;portableName.extraPrefixes 映射会合并。参见 config/config.example.jsonschemas/config.schema.json

{
  "$schema": "https://raw.githubusercontent.com/brglng/pi-packages/main/packages/pi-session-sync/schemas/config.schema.json",
  "syncRoot": "~/session-sync",
  "scope": "current",
  "requireGit": true,
  "syncOnStartup": false,
  "syncOnShutdown": true,
  "sameIdConflict": "conflict",
  "deletionConflict": "keep-modified",
  "portableName": {
    "homeLabel": "HOME",
    "rootLabel": "ROOT",
    "extraPrefixes": {
      "/Volumes/Backup": "BACKUP"
    }
  }
}

syncRoot 必填,可使用 ~。session 根目录与 Pi 自身解析方式一致 (PI_CODING_AGENT_SESSION_DIRsettings.jsonsessionDir → 默认的 <agentDir>/sessions)。

命令

/session-sync status

显示当前同步配置(scope、requireGit、启动/退出同步及各合并策略)、可移植名、 session 根目录、同步根目录、检测到的 session 布局和配置警告,并只读地显示当前 项目的未解决冲突数和 tombstone 数(不会修改任何同步状态)。Conflicts 为冲突 归档中未解决的记录数;Tombstones 为文件级 tombstone 数及 manifest 中跟踪的 entry 级 tombstone 总数。当根元数据、项目 manifest、冲突索引或 session 布局无法 被信任(缺失、损坏、foreign 或符号链接)时,status 会报告不可用原因,而不是显示 具有误导性的零计数。

/session-sync sync

同步当前项目。Pi 忙时拒绝执行。

/session-sync conflicts

列出当前项目冲突归档中的冲突条目。

/session-sync resolve <id> --local|--external|--merge

采用归档的 localexternal 快照,或把两侧归档快照与归档基线快照做 确定性合并来解决一条已记录冲突:

  • --local / --external 直接采用所选快照。写入前会严格校验所选快照。
  • --merge 使用与普通同步相同的确定性三方结构化合并:自基线以来未变的 entry 只保留一份,单侧新增/删除会传播,同一 entry.id 的分歧内容按 sameIdConflict 处理(默认 conflict,不猜测)。三方合并的共同祖先是归档冲突 时保存的基线快照,因此之后对实时基线的修改不会影响本次合并。在基线归档支持 之前创建的记录(或归档基线缺失/损坏)、以及存在歧义的合并,都会报出明确错误 并保持所有文件不变。

成功后原子替换本地 session 文件、镜像文件和基线快照,清除该文件的 tombstone, 并移除冲突记录。失败时各侧会从替换前备份回滚;回滚不完整时恢复副本会被原地保留 并随错误报告路径,冲突记录保持打开。在 Git 管理的同步根上,该次 resolve 会像普通 同步一样做 checkpoint(见 Git 一节):只暂存并提交本次 resolve 写入或移除的受管路径, 任何跳过或失败都会作为已成功 resolve 的提示信息展示。Pi 忙或分片锁被占用时拒绝执行。

/session-sync unlock --force [--global|--git]

强制移除过期锁。默认目标是当前项目的分片锁;--global 目标是全量同步的根锁, --git 目标是当前项目的 Git 操作锁。会先读取锁持有者并检查其 PID 是否存活:同主机 上仍存活的进程所持有的锁会被拒绝移除;移除前会立即重新读取 owner 文件 (compare-before-delete),若锁在检查期间易主则不会删除。同主机身份使用所有锁写入方 统一记录的标准主机名(node:os hostname,回退到 HOSTNAME 环境变量与固定字面量); 使用回退身份时,主机名不匹配并不能证明是另一台机器,因此 force-unlock 仍然执行 PID 存活检查(fail-closed),绝不会仅凭不可靠的主机名比较移除一个可能存活的锁。普通 sync 从不自动抢占已有锁。绝不能在 Pi 写 session 时使用:普通 sync 仍要求 idle, --force 只作用于锁,绝不绕过 idle 门限。

当前局限

  • 支持 scope: "current"scope: "all"scope: "all" 只枚举 sessions 根目录下的逐项目子目录;符号链接项目目录会被直接跳过(绝不跟随)。
  • 扁平的自定义 sessionDir 布局(session 文件直接存放在 sessions 根目录, 例如通过 PI_CODING_AGENT_SESSION_DIRsettings.jsonsessionDir, 而不是每个项目一个 --<cwd>-- 子目录)不受支持。同步会被拒绝并给出可见提示, /session-sync status 也会报告该布局。
  • 启动同步属于实验性,可能让当前内存 session 过期。公开扩展 API 无法用 外部 cwd 重建活跃 SessionManager,因此写入会一直冻结,直到退出并重启 Pi;没有进程内 reload 命令。unlock 只影响锁,绝不解除写入冻结。
  • entry/文件删除会用 tombstone 跟踪;/session-sync resolve <id> --local|--external|--merge 可显式解决冲突(--merge 需要两侧归档快照和有效 基线快照;显式的 tombstone 恢复流程尚未实现;可用 conflicts 检查归档)。
  • session header 中的 parentSession 引用在共享镜像中保留。写入本地目录时, 仅当存储的父路径可证明位于配置的 Pi sessions 根目录下的逐项目 session 目录、 且属于当前项目时,才会把跨机器的绝对路径归一化到当前机器的项目目录; 镜像字节保持不变,不满足条件的引用保持原样。
  • 当前项目同步在 managed-path preflight 与自动提交周围持有短生命周期的根级 Git 操作锁,避免两个项目并发同步时交错执行 git add/git commitscope: "all" 使用全局锁达到同样目的。若 Git 操作锁被其他进程占用, 文件系统同步仍会执行,但自动提交会跳过并给出提示。

License

MPL-2.0