@oldsuns/pi-switch
v0.3.7
Published
A local terminal and Web UI for Pi provider and model configuration
Readme
pi-switch
维护本地 provider 库并按需同步 Pi 配置的终端 TUI 与本地 Web 界面:管理 provider / 模型 / 默认模型,以及 Pi session 的列表、预览与删除。
CLI:pi-switch · npm 包:@oldsuns/pi-switch · Node 薄壳 + Rust/napi 原生核心
功能
- 主页:provider / model 计数、默认模型、关键路径;启动后台静默检查 npm 新版本,有更新时弹出确认对话框可一键安装
- 配置 (Profiles):本地 provider 库与 Pi 启用子集;新建 / 编辑 / 删除 / 复制;在线导入模型
- 会话 (Sessions):按工作目录浏览 JSONL session,使用 Tree/Markdown 视图区分用户/Pi消息,支持筛选、复制与删除
- 设置 (Settings):语言、models.dev 元数据、默认参数、重载、校验、备份、OpenCode 导入、自动检查更新、手动检查更新
- 完整库保存在
~/.pi-switch/providers.json,只有已启用项写入 Pi 的models.json
要求
- Node.js
>= 20 - 预构建原生模块当前仅覆盖 Windows (msvc x64)——直接
npm install -g即装即用 - 本地构建原生模块(仅开发者)需要 Rust 工具链与
@napi-rs/cli
快速开始
# 全局安装
npm install -g @oldsuns/pi-switch
# 打开 TUI
pi-switch首次运行会自动把现有 Pi agent 目录中的 models.json 全量导入本地库,不修改 Pi 配置;随后即可在 Profiles 中勾选要同步到 Pi 的 provider / model。Pi agent 目录使用非空 PI_CODING_AGENT_DIR,未设置时为 ~/.pi/agent。
CLI:
pi-switch # 打开 TUI(等同 tui)
pi-switch tui
pi-switch --web # 启动本地 Web 界面并打开浏览器
pi-switch --web --port 5210 --no-open # 指定端口,只打印访问地址
pi-switch doctor # 校验配置与默认模型
pi-switch --version # / -v
pi-switch --help # / -h / helpWindows 上无需 Rust:预构建原生模块随 npm 包分发,pi-switch 开箱即用。本地开发见下文「开发者」。
Web 界面
pi-switch --web 启动仅监听 127.0.0.1 的本地服务,自动选择空闲端口并打开浏览器;终端会打印访问地址,按 Ctrl+C 停止服务。可使用 --port 指定端口、--no-open 禁用自动打开。
- 主页:本地 provider / 模型计数、Pi 可用模型、直接切换默认模型、最近会话与常用操作。
- 配置:provider / 模型主从分栏、搜索、创建、编辑、复制、删除、同步与默认模型;在线多选导入、元数据来源选择。
- 会话:按工作目录浏览,支持命名筛选、用户消息筛选、分支折叠、树状与 Markdown 阅读、复制和删除。
- 设置:中英文、模型元数据与缺省参数、配置检查、备份恢复、OpenCode 导入、手动检查和安装更新。自动检查更新开关与 TUI 共享,控制 TUI 启动时的检查。
Web 在「设置 → 外观」中提供 Catppuccin Mocha 暗色与 Latte 亮色主题,切换立即生效,并在浏览器中保存选择。沿用 TUI 的四页导航和状态语义,支持窄屏布局、键盘操作与减少动画偏好。按 1–4 切换页面,/ 搜索,? 查看完整快捷键;文本输入期间不触发快捷键。
Web 与 TUI 共用 Rust 文档层,使用相同的文件锁、备份、校验和未知字段保留规则。取消默认 provider 的同步、删除和恢复备份均会明确确认。浏览器不会将 API key 保存到 localStorage;会话 Markdown 中的原始 HTML 以文本展示,图片以替代文字展示,不自动加载远程图片。
开发者
从源码构建:
npm install
npm run build:native:debug
node ./bin/pi-switch.js
# 启动本地 Web 界面
npm run dev:web贡献流程见 CONTRIBUTING.md。
Web 实现与使用说明见 web/README.md。
界面与快捷键
全局导航:j/k 或方向键移动;菜单中 Enter / l 进入内容,各内容页按自己的方向键切换焦点;? 帮助;q 退出(多数界面 Ctrl+C 也退出,会话预览除外)。
配置 (Profiles)
| 键 | 作用 |
|----|------|
| n / e / d / c | 新建 / 编辑 / 删除 / 复制当前焦点(provider 或 model) |
| Space(provider) | 同步到 Pi / 取消同步;[x] 已同步,[ ] 不同步 |
| Space(model) | 设为默认模型(仅已同步到 Pi 的 provider) |
| i | 从当前 provider 在线导入模型 |
| / | 筛选 provider |
| Enter / l | 进入模型列表 |
| Esc / h | 从模型回到 provider,或从 provider 回菜单 |
| r | 从磁盘重载配置 |
| b | 浏览备份 |
| v | 校验配置(doctor) |
新建 provider 默认同步到 Pi,表单可关闭;复制继承源的同步状态。
Provider 表单:baseUrl、api(openai-completions / openai-responses / anthropic-messages / google-generative-ai)、apiKey、authHeader、Headers(独立 User-Agent + 其余 JSON)、compat(含一等开关 Session affinity = sendSessionAffinityHeaders)。
Model 表单:id、name、API override、reasoning、文本/图像输入、context window、max tokens、thinking levels(thinkingLevelMap)。cost、modelOverrides、OAuth 等未知字段会无损保留。字段语义以 Pi Custom Models 为准。
会话 (Sessions)
| 键 | 作用 |
|----|------|
| / | 筛选 |
| n | 仅显示手动命名的 session(不是新建) |
| r | 重新扫描磁盘 |
| u | 预览仅用户消息 |
| Right | 从会话列表进入树预览 |
| 预览 Left | 从树预览返回会话列表 |
| 预览 Up / Down | 按树节点切换,默认定位当前 active leaf |
| 预览 PageUp / PageDown | 按当前可视区域逐页滚动 |
| 预览 v | 切换 Tree 单行预览和完整 Markdown 阅读 |
| 预览 Tab | 折叠或展开当前真实分叉点的全部下级 |
| 预览 Ctrl+Left/Right | 在视觉分支层级间移动:跳过同 lane 的串行消息,进入上一级/下一级分支段 |
| 预览 Alt+Left/Right | 从当前位置切换最近分叉点的相邻分支 |
| 滚轮 | 按行滚动长消息 |
| 预览 Ctrl+C | 复制当前消息到剪贴板(不退出) |
| d | 删除当前 session(确认后优先 trash) |
预览按 Pi 官方 session 的 id / parentId 构建树。默认 Tree 模式参考 Pi 原生 /tree:每个节点只占一行,显示 User: / Pi: 和单行摘要;只有真实分叉才增加缩进并使用 ├─ / └─,串行消息保持同一 lane。深层分支不会为所有消息预留固定宽度;仅在当前节点过深时水平平移树正文,并固定保留左侧光标 gutter。按 v 可切换到完整阅读模式,显示完整 Markdown 正文;每条消息按自身树深度分配前缀,树栏最多约占三分之一宽度。Ctrl+Left/Right 在分支段层级间移动,Alt+Left/Right 切换相邻分支,Tab 仅折叠真实分叉点。可见消息挂到最近可见祖先,session_info、tool 等 bookkeeping 节点不会伪装成对话消息。黄标题 = 手动命名;白标题 = 使用第一条用户消息。
Session 根目录优先级:非空 PI_CODING_AGENT_SESSION_DIR → <Pi agent dir>/sessions。
剪贴板:Windows clip、macOS pbcopy、Linux wl-copy / xclip / xsel。
设置 (Settings)
| 项 | 说明 |
|----|------|
| 语言 | English / 中文(en / zh-CN) |
| 从 models.dev 获取模型信息 | 开关实时元数据(默认开) |
| 自动检查更新 | 启动时后台自动检查 npm @oldsuns/pi-switch 新版本(开关,默认开) |
| 检查更新(手动) | 立即检查新版本(绕过 24h 缓存) |
| 默认模型参数 | 仅关闭实时元数据时显示,用于导入缺省 |
| 重载配置 | 从磁盘重读 |
| 验证配置 | doctor |
| 浏览备份 | 恢复 version 3 或兼容的 version 2 备份 |
| 从 OpenCode 导入 | 只读导入 opencode.json |
Enter / Space 执行当前项。
Provider 库与 Pi 同步
~/.pi-switch/providers.json是完整本地库;<Pi agent dir>/models.json只含当前已同步到 Pi 的子集。- 首次运行把现有
models.json全量导入本地库,不修改 Pi 配置。 - 已同步 provider 的编辑与 model 变更会同步两份文件;不同步项只更新本地库。
- 在线导入 model 不会隐式同步到 Pi。
- 启动或手动重载时,以
models.json中同 ID provider 为准回灌本地库;外部从 Pi 删除的 provider 仍作为不同步项保留。 - 从 Pi 移除当前默认 provider 会先确认并清除默认模型;
d永久删除本地副本,必要时同时从 Pi 删除;若该 provider 在auth.json有api_key凭据,删除时会询问是否一并删除(OAuth 等凭据不会被删除)。 - API 密钥保存位置可在 Settings 里切换:默认写入 Pi 的
auth.json(provider 的 ID 映射到{"type":"api_key","key":...},models.json与providers.json不保存密钥);可切换回旧的models.json行为。provider 重命名时凭据随之迁移(重命名并输入新密钥时源 OAuth 凭据保留原位),目标 ID 已有凭据时先询问。切到models.json后保存密钥会移除同名 ID 上的 auth.json 条目(否则它会继续优先于 models.json);该条目含env等配置时先询问。回到auth.json后保存会清掉 provider 文档里遗留的同名明文密钥。
模型导入与价格
在线导入(Profiles 中 i):
- 按 provider 的
api/baseUrl/ 鉴权请求模型列表。 - NewAPI 网关价格(best-effort):去掉
baseUrl尾部/v1后依次尝试GET /api/ratio_config(可能含create_cache_ratio)GET /api/pricing
成功则按 NewAPI 换算覆盖模型cost(1 USD = 500_000 quota,每 1M tokens 成本 ≈ratio × 2USD);失败静默忽略。
- 若开启 models.dev 元数据:请求
https://models.dev/api.json,补全contextWindow、maxTokens、cost、reasoning 等。- 在线导入遇到同 model ID 多源歧义时:需要选择元数据来源;缺少匹配元数据或 models.dev 不可达时,选中的模型使用 Settings 中的默认参数导入,并明确提示。
- 先选择服务商返回的模型 ID,再为选中项获取元数据和价格;TUI 与 Web 使用相同流程。
- 网关价格在 catalog 元数据之上叠加。
- 关闭实时元数据:使用 Settings 中的默认参数;空字段回落 Pi 官方默认(context window
128000、max tokens16384、cost0)。
OpenCode 导入(Settings):只读 ~/.config/opencode/opencode.json,可全选或勾选 provider;导入项默认同步到 Pi。若 models.dev 仍有歧义,需要用户选择候选。OpenCode 配置本身不会被修改。
配置路径与 Settings 字段
<Pi agent dir> 使用非空 PI_CODING_AGENT_DIR,未设置时为 ~/.pi/agent。PI_CODING_AGENT_SESSION_DIR 只覆盖 Session 根目录,优先级仍高于 <Pi agent dir>/sessions。
| 路径 | 角色 |
|------|------|
| ~/.pi-switch/providers.json | 完整本地 provider 库(version: 1) |
| ~/.pi-switch/settings.json | pi-switch 私有设置;无自定义值时可不存在 |
| <Pi agent dir>/models.json | 已同步到 Pi 的 provider 子集 |
| <Pi agent dir>/settings.json | Pi 设置,包括默认 provider / model |
| ~/.config/opencode/opencode.json | OpenCode 只读导入源 |
| ~/.pi-switch/backups/ | version 3 备份(providers + models + Pi settings + pi-switch settings),最多 10 份 |
| ~/.pi-switch/write.lock | 写入互斥锁 |
| ~/.pi-switch/update.json | npm 新版本检查缓存(lastCheck + latest + dismissed,每 24h 最多联网一次) |
Pi settings.json 中由 pi-switch 管理的字段:
| 字段 | 含义 |
|------|------|
| defaultProvider + defaultModel | 默认模型(成对存在或同时缺省) |
~/.pi-switch/settings.json 的字段:
| 字段 | 含义 |
|------|------|
| language | en | zh-CN |
| fetchModelMetadata | 是否拉 models.dev(默认 true) |
| keyStorage | API 密钥保存位置:auth.json(默认)或 models.json |
| checkForUpdates | 是否启动时检查 npm 新版本(默认 true) |
| modelDefaults | 关闭实时元数据时的导入缺省(context / maxTokens / cost) |
升级时若 Pi settings.json 仍包含旧 piSwitch 对象,启动或重载会自动迁移到 ~/.pi-switch/settings.json,本地已有字段优先,旧对象只补齐缺失字段;迁移成功后从 Pi settings 删除旧对象。两份文件中的未知字段都会保留。
数据安全
- 写前备份
providers.json、Pimodels.json/settings.json和 pi-switchsettings.json到~/.pi-switch/backups/(version 3);最多保留最近 10 份。现有 version 2 备份仍可恢复并自动拆分设置,version 1 备份不支持恢复。 - 写入使用
write.lock互斥;异常残留锁时doctor会提示。 providers.json损坏时归档为corrupt-providers-*.json,再从当前 Pi 配置重建,启动时显示归档路径。auth.json不参与备份;写入前以<auth.json>.lock目录与 Pi 的 proper-lockfile 互斥,并额外持有<auth.json>.lock.guard这个 OS 级锁(进程退出时内核自动释放),因此崩溃遗留的锁目录会在 30 秒后被安全接管,不会永久阻塞写入。新文件权限0600,只修改目标 provider 的条目(OAuth 等其他凭据原样保留)。要替换目标 ID 上不属于该 provider 的凭据(重命名落到已有凭据的 ID、或给已有 OAuth 登录的 ID 写 API 密钥)时会先询问,未确认不写入。- 原子写入,只 patch 目标字段,保留未知 JSON;格式错误时停止写入并显示错误。
- 支持 Pi 的
$ENV/${ENV}插值与$$/$!转义;!command原样保存,在线拉取不会执行它。 - Session 删除只作用于选中的 JSONL,并校验路径必须位于 session 根目录内;优先调用系统
trash,失败后再永久删除。 - 正常退出、错误和 panic 都会恢复 raw mode、alternate screen 与光标。
自动更新检查与安装
启动时若 Settings 中「自动检查更新」开启(默认开),pi-switch 会在后台线程静默访问 npm registry 查询 @oldsuns/pi-switch 的 latest 版本,与当前版本对比:
- 有新版本:弹出确认对话框(显示
当前 → 最新),用户可选择立即安装或跳过。跳过后同一版本不再弹窗,但主页仍显示横幅。手动检查(Settings 中「检查更新」)始终弹窗。 - 无新版本、离线或请求失败:静默无提示,不打扰用户。
安装流程:
- 用户在确认对话框按
Enter/y确认 → 后台执行npm install -g @oldsuns/pi-switch,显示加载动画。 - 安装成功 → 通知「更新已安装,请重启 pi-switch 生效」。
- 安装失败 → 弹出错误通知(如权限不足、网络问题)。
- 用户按
Esc/n跳过 → 记录已跳过的版本到缓存,同一版本不再自动弹窗;主页横幅仍提示有新版本。
检查结果缓存在 ~/.pi-switch/update.json,自动检查每 24h 最多联网一次。后台线程独立于目录导入任务,不阻塞 UI。Settings 中可随时关闭「自动检查更新」。
验证
cargo test --locked --lib
cargo fmt -- --check
cargo clippy --locked --all-targets -- -D warnings
npm run build:native:debug
npm run pack:check许可
致谢
感谢 LINUX DO 社区的讨论与反馈。
