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

tb-npmswitch

v0.6.0

Published

Echo desktop plugin: install and update AI CLIs, manage Claude Code / Codex / Gemini CLI providers, and report token usage per Echo message.

Readme

tb-npmswitch

从 tb_switch(CC Switch 分支)抽取的核心能力,按 Echo 插件协议打包成一个 npm 包。包里同时有两部分:

  • 插件前端(dist/renderer.js + dist/renderer.css):Echo 桌面端里的「AI 工具」页面,包括概览、AI CLI、供应商、Token 用量四个标签页和一个设置分区。
  • 本机服务(dist/service.js 由 Echo 桌面端运行,或终端里的 tb-switch serve):在用户电脑上运行,负责检测和更新 AI CLI、写各 CLI 的配置文件、用本地代理和 Codex 会话日志统计 Token。

Token 用量最终汇总到 echo_admin(/api/token-usage/*、/api/workspaces/:id/analytics/tokens*),按工作区、项目、工位、成员、模型、供应商统计。

数据目录是 ~/.tb-switch,和桌面版的 ~/.cc-switch 互不影响。

架构

flowchart LR
  subgraph Echo 桌面端
    UI[插件前端<br/>dist/renderer.js]
  end
  subgraph 用户电脑
    SVC[本机服务<br/>tb-switch serve]
    CLI[Claude Code / Codex / Gemini CLI]
  end
  API[(echo_admin)]
  UI -- "HTTP JSON-RPC + SSE<br/>127.0.0.1:15822(桌面端令牌 / 连接码)" --> SVC
  CLI -- "请求经本地代理 :15821" --> SVC
  UI -- "ctx.core.request(用户登录态)<br/>POST /token-usage/reports" --> API
  UI -- "GET /workspaces/:id/analytics/tokens" --> API
  • 本机模式(默认):清单声明 service:local 和 entries.service: ./dist/service.js(esbuild 打成单文件,依赖全打进去)。插件在插件中心启用后,前端通过 ctx.core.service.start() 让桌面端用自带的 Node 在后台运行它,不用开终端、不用连接码:
    • 前端用 status().token(桌面端每次启动生成、通过 TOC_PLUGIN_SERVICE_TOKEN 传给服务)访问本机 HTTP 接口;服务读完即从环境变量删除。终端里的 tb-switch 命令仍用 ~/.tb-switch/api-token,两个令牌都有效。
    • 跟随桌面端:stdin 关闭或父进程(TOC_PLUGIN_PARENT_PID)退出时停止并恢复 CLI 配置;插件被禁用 / 卸载时桌面端也会停止它。开关状态存在 ctx.storage 的 service.local(on / off,默认 on),关掉后不再随桌面端启动。
    • 一台电脑只跑一个:端口被占用(终端里已有 tb-switch serve)时,它在接管任何配置之前以退出码 3 退出,前端改用连接码连接那个服务;那个服务关掉后自动改用桌面端运行的服务。
    • 由桌面端管理时不支持 system.restart(服务和插件同包同版本,不会出现版本不一致)。
  • 旧版桌面端(没有 ctx.core.service):仍按原方式,在终端运行启动命令,把终端显示的 6 位连接码输入插件。令牌保存在 ctx.secureStorage 的 sidecar.http 里,之后自动重连。
  • 清单里的 entries.node 和 sidecar 是更早预留的侧车字段,当前宿主会忽略它们。

安装与构建

依赖 echo_web 仓库里的插件 SDK(file:../../echo/echo_web/packages/plugin-sdk),构建前需要把两个仓库放在约定的相对位置。

npm install --ignore-scripts
npm run typecheck    # Node 部分 + 前端部分
npm run build        # esbuild 打包 + 类型声明 + 语言包检查 + SDK 包校验
npm run validate     # 只做清单和语言包校验
npm run dev:renderer # 前端监听构建

前端的 React、React Query、React Router 和 SDK UI 组件由宿主共享(SDK 的 sharedModulesPlugin),不会打进包里;用到的 lucide 图标按需打包。发布的包只有 dist/、assets/、locales/,没有运行时依赖,也没有安装脚本。

插件清单

| 字段 | 内容 | |------|------| | id | official.tb-switch,apiVersion 1.x,只支持 desktop | | 权限 | storage:plugin、secureStorage:plugin、notifications:publish、api:user、service:local | | 本机服务 | entries.service: ./dist/service.js(需要支持 service:local 的 Echo 桌面端;先发桌面端再发插件) | | 菜单与路由 | tbswitch.nav / tbswitch.home → /plugins/tb-switch(/tools、/providers、/usage 子路径对应标签页) | | 设置 | tbswitch.settings:本机服务连接、自动上报、统计方式 | | 命令 | tbswitch.open、tbswitch.tools.detect、tbswitch.report.flush | | 语言 | zh-CN、en,清单里的 titleKey 都在语言包里 |

命令行

npx -y tb-npmswitch@latest serve   # 启动本机服务,终端显示连接码
tb-switch pair                                # 连接码过期后获取新码(服务需在运行)

tb-switch tools list                          # 已装 / 最新版本
tb-switch tools install codex
tb-switch tools update claude [--yes]         # 多处安装时需要 --yes
tb-switch provider add claude --name 中转A --base-url https://x.example.com --api-key sk-... [--model ...] [--switch]
tb-switch provider list claude
tb-switch provider switch claude <id>
tb-switch provider import codex               # 把当前配置导入为一个供应商
tb-switch proxy restore                       # 服务异常退出后,把 CLI 配置恢复为真实地址
tb-switch report status | messages | flush | retry <msgId>
tb-switch mcp set public --url echoadmin.paperfold.cn --token <access token>
tb-switch mcp set lan --url 192.168.89.88:3000 --token <access token>
tb-switch mcp switch lan                      # 删除各 CLI 的 doer,再按局域网重新添加
tb-switch mcp status
tb-switch settings set report.delivery direct
tb-switch rpc <method> '<paramsJson>'         # 直接调用任意接口

API Key 也可以用环境变量 TB_SWITCH_API_KEY 传入,避免留在 shell 历史里。

MCP 一键切换

每个 CLI 只配置一个名为 doer 的 MCP,指向 Echo 后台的 /mcp。插件的「MCP」标签页里分别填公网、局域网的地址和 Access Token(令牌在各自服务器的「安全与访问」里生成),点「公网 / 局域网」即可切换。

  • 切换时对每个勾选的 CLI 先删除原来的 doer 条目,再按所选服务器重新添加(url + Authorization: Bearer),其他 MCP 和配置不动;写入前备份到 ~/.tb-switch/backups/mcp-<cli>/。
  • 地址可以写 echoadmin.paperfold.cn、192.168.89.88:3000 或完整 URL;没写协议时,局域网地址用 http,其余用 https,末尾自动补 /mcp。
  • 两套令牌保存在 ~/.tb-switch/mcp.json(权限 0600),接口只返回「是否已保存」,不返回令牌本身。
  • 目前支持 Codex(~/.codex/config.toml)。其他 CLI 通过 src/core/mcp/adapters 里的 McpClientAdapter(read / remove / add)接入,并在 MCP_CLIENT_IDS 里登记。
  • 已打开的 CLI 会话不会重新读取配置,新开的会话生效。

切换时会一起改掉本机所有保存过这个 MCP 的地方,不只是 CLI 的 live 配置:

  • CC Switch / TB Switch 桌面版的数据库(~/.cc-switch/cc-switch.db):每个 Codex 供应商保存的整份 config.toml、接管前的 live 备份、它自己的 MCP 表、profiles 和 opencode 公共配置。这些内容会在那边切换供应商时覆盖 CLI 配置,所以不一起改就会切回旧地址。
  • TB Switch 插件自己保存的供应商(~/.tb-switch/providers.json)。

只改已存在的条目,不新增;其他 MCP 和供应商配置不动;写库前把数据库(含 -wal/-shm)备份到 ~/.tb-switch/backups/mcp-ccswitch/。「MCP」页会列出每处有多少条、分别指向哪个环境,可以单独取消勾选。

地址和 Token 旁边有「测试连通」:用当前填写的值向 POST <地址>/mcp 发一次 MCP initialize,区分四种结果——连通正常(显示服务端名称和耗时)、Token 无效(401/403)、地址上没有 MCP 接口(404/405,反向代理没把 POST 透传给后端时也是这个)、无法连通(DNS / 连接 / TLS / 超时)。

接口:mcp.get、mcp.test({ profile, url?, token? })、mcp.update({ profiles?: { public|lan: { url?, token? } }, clients?: { codex: boolean } })、mcp.switch({ profile: "public"|"lan", clients? }),事件 mcp.changed。

获取模型列表

供应商表单和多供应商路由里的模型输入框都带「获取模型列表」按钮,逻辑移植自 tb_switch(CC Switch)的 model_fetch.rs:

  • 调用 OpenAI 兼容的 GET /v1/models。地址已以 /v{N} 结尾时改拼 /models(非 /v1 再补 /v1/models 兜底);以 /anthropic、/api/coding、/claude 等兼容子路径结尾时,追加剥离后缀的候选。按顺序尝试,404/405 换下一个。
  • 默认 Authorization: Bearer,apiFormat 为 anthropic-messages / google-generative-ai 时改用 x-api-key / x-goog-api-key;单次 15 秒超时;结果按 id 排序,按 owned_by 分组、可搜索。
  • 编辑已保存的供应商时不必重填 Key:传 { app, providerId },由本机服务用保存的 Key 请求,Key 不回传前端。错误信息里的 Key 会被替换成 [REDACTED]。

测试连接

供应商列表每行和新增 / 编辑弹窗里都有「测试连接」,分两步(第一步移植自 tb_switch stream_check.rs):

  1. 地址可达:GET base_url,收到任意 HTTP 响应即算可达,延迟按响应头到达计时;只有 DNS / 连接被拒 / TLS / 超时算不通。8 秒超时,超时类失败重试一次,超过 6000ms 标「较慢」。
  2. 密钥有效:带 API Key 请求模型列表接口。2xx 为有效(并显示可用模型数),401/403 为无效,没有该接口则为「无法确认」。

结果既弹提示也留在行上。官方登录的供应商不显示这个按钮(没有可探测的地址)。

接口:providers.test({ app, id? | baseUrl?, apiKey? })。

本地代理(与 tb_switch 对齐)

转发逻辑按 tb_switch(CC Switch)的代理流程移植,分四层,所有 CLI 共用,差异只在适配器:

  1. 路由表(src/core/proxy/adapters/<app>.ts 的 route):Codex 的 /responses、/v1/responses、/v1/v1/responses 等别名统一成 /responses,另有 /responses/compact、/chat/completions、/alpha/search、/images/generations|edits;/models 本地返回模型目录,不打上游。WebSocket 升级和方法不对返回 405,Codex 会改用 HTTP + SSE。
  2. 请求上下文(controller.resolve):多供应商模式下,请求模型等于某条路由的客户端模型名时,固定到该路由的供应商并换成实际模型;否则走当前供应商、模型不变。当前供应商是官方登录无法代理时,退回默认路由。
  3. 适配器(ProxyAdapter):取供应商地址和 Key、拼上游 URL(只填域名时补 /v1,已带 /v1 或自定义前缀原样拼接)、生成认证头。
  4. 转发器(forwarder.ts):
    • 请求头保持客户端顺序:认证头原位替换成供应商 Key,丢掉代理 / CDN / 链路追踪头;流式请求要求上游 accept-encoding: identity。
    • 请求体:先解压(Codex 登录 ChatGPT 后发 zstd),再换路由模型、去掉 [1m] 后缀、过滤 _ 开头的私有字段、按键名排序。
    • 响应:SSE 原样回传,去掉逐跳头;上游 4xx/5xx 按 Codex 的错误格式返回,写明供应商、模型和上游状态码。

Claude、Gemini 目前走透传适配器(路径原样、使用客户端自己的 Key),后续按同样方式移植 tb_switch 的适配器即可,Grok 也是加一个适配器。

检查:npm run smoke 用本地假上游验证以上规则(发布前自动运行);RELAY_URL=… RELAY_KEY=… RELAY_MODEL=… npm run check:relay 经系统代理对真实中转站做测试连接、获取模型和一次多供应商流式对话。

出站代理

获取模型、测试连接、MCP 测试、npm 版本查询、用量上报和转发代理的上游请求,都走同一个出站通道。Node 的 fetch 不认 HTTPS_PROXY,所以这里自己实现了代理支持:

  • 默认跟随 HTTPS_PROXY / HTTP_PROXY / ALL_PROXY(遵守 NO_PROXY),都没有时读 macOS 系统代理。
  • settings.network.proxyUrl 可以显式指定(如 http://127.0.0.1:7890),填 off 强制直连。只支持 http(s) 代理,不支持 socks。
  • 本机和内网地址(127.x、10.x、172.16-31.x、192.168.x、*.local)永不走代理。
  • https 通过代理的 CONNECT 隧道转发,TLS 仍然是本机到上游端到端。

版本一致性

本机服务是独立的长驻进程,和插件界面分别更新。两者版本不一致时新接口会报 Unknown method,所以插件顶部会显示提示,并提供「更新本机服务」按钮:

  • 优先用开发者模式安装的本地构建启动;
  • 否则用 npm 上的同版本(npx -y tb-npmswitch@<版本> serve);
  • 旧服务太旧(没有 system.restart)时给出手动命令。

重启由本机服务自己完成:先优雅退出(恢复被接管的 live 配置、保存待上报数据),再由一个脱离终端的进程拉起新版本,日志写到 ~/.tb-switch/logs/serve.log。连接页给出的启动命令也固定为插件自身版本,避免 @latest 的 npx 缓存拿到旧版本。

接口:system.restart({ version? })。

接口:models.fetch({ baseUrl?, apiKey?, app?, providerId?, isFullUrl?, modelsUrl?, customUserAgent?, apiFormat?, requestHeaders? } → [{ id, ownedBy }])。

单供应商 / 多供应商

「供应商」页每个 CLI 上方可以切换工作方式:

  • 单供应商:和以前一样,启用哪个供应商就把它写进 CLI 配置。
  • 多供应商:CLI 指向本机路由服务,模型列表由「模型路由」组成,每条路由 = 供应商 + 实际模型 + 客户端模型名 + 显示名称。请求按模型名转发到对应供应商,并换上该供应商的 Key 和实际模型名。

Codex(移植自 tb_switch「Codex 模型路由」):

  • config.toml 写入 model_provider = "custom",[model_providers.custom] 指向 http://127.0.0.1:15821/codex、wire_api = "responses"、experimental_bearer_token = "PROXY_MANAGED";model 为默认路由的客户端模型名;model_catalog_json = "tb-switch-model-catalog.json"(以 Codex models_cache.json 里的 gpt-5.5 为模板,没有时用内置模板)。
  • 客户端模型名只能用官方名(gpt-5.6-sol、gpt-5.6-terra、gpt-5.6-luna、gpt-5.5、gpt-5.4、gpt-5.4-mini、gpt-5.2,最多 7 条),因为 ChatGPT 桌面端会隐藏非官方名;它只作路由键,不影响实际模型。
  • auth.json 不动,ChatGPT 登录保留;chatgpt-account-id 等头不会转发给第三方。未匹配的模型走默认路由。
  • 需要本机服务一直运行(多供应商模式下启动时会自动开启代理)。切回单供应商时删除路由目录、恢复当前供应商配置,代理恢复到切换前的状态;写入前备份到 ~/.tb-switch/backups/multi-codex/。
  • 被路由引用的供应商不能删除;编辑供应商的 Key 立即生效。模式与路由保存在 ~/.tb-switch/routes.json(0600)。

其他 CLI:实现 src/core/multi/types.ts 的 MultiProviderAdapter(normalize / apply / release / modelsResponse / upstreamHeaders),在 src/core/multi/adapters/index.ts 登记即可,接口和前端组件通用。

接口:multi.get({ app })、multi.save({ app, routes, currentRouteId? })、multi.setMode({ app, mode: "single"|"multi" }),事件 multi.changed。

Token 上报

统计

  • 代理:proxy.enabled 默认开启,接管 claude、gemini。代理运行期间,当前供应商的地址会改写成 http://127.0.0.1:15821/<app>,请求原样转发。密钥仍由 CLI 自己发送,代理不保存也不注入密钥。停止服务时恢复真实地址;异常退出后执行 tb-switch proxy restore,或者下次启动时自动恢复。官方登录的供应商不会被接管。
  • Codex 会话:读取 ~/.codex/sessions 和 archived_sessions 下的 JSONL,首次启动只回放最近 replayDays 天。Codex 走代理时会自动停用会话来源,避免重复计数。
  • msg_id 识别:依次看请求头 x-echo-msg-id、请求体字段、最新一条真实用户消息(跳过 tool_result)里的 ---payload---{"msg_id":...}。msg_id 由 echo_admin 下发任务时生成。

投递方式(report.delivery)

| 方式 | 说明 | |------|------| | host(默认) | 插件前端每 15 秒调用 report.pending 领取一批快照(90 秒租约),用 ctx.core.request 以当前登录用户身份 POST 到 echo_admin,再调用 report.ack 回写结果 | | direct | 本机服务自己 POST 到 report.endpoint,带 Authorization: Bearer <report.token>。用于没有打开 Echo 桌面端的场景;也可以用环境变量 TB_SWITCH_REPORT_ENDPOINT / TB_SWITCH_REPORT_TOKEN 注入 |

同一个 msg_id 静默 quietSeconds(默认 30 秒)后生成完整快照,report_version 递增。echo_admin 按消息整份替换,旧版本返回 stale 并视为已送达。失败按指数退避重试;rejected(如消息不存在或不属于当前用户)不再自动重试,可在「Token 用量 › 本机上报」里手动重新上报。

echo_admin 接口

// POST /api/token-usage/reports(最多 50 份,每份最多 100 个分桶)
{ "reports": [{
  "msg_id": "…", "report_version": 3, "source": "tb_switch",
  "buckets": [{ "provider_id": "…", "provider_name": "智谱", "provider_type": "custom", "provider_host": "open.bigmodel.cn",
                "app_type": "claude", "model": "glm-4.6", "request_count": 4,
                "input_tokens": 1200, "output_tokens": 300, "cache_read_tokens": 800, "cache_write_tokens": 0,
                "cost_usd": null, "first_request_at": "…", "last_request_at": "…" }]
}]}
// → data: { results: [{ msg_id, report_version, status: "applied|stale|rejected", code?, message? }], applied, stale, rejected }
  • GET /api/token-usage/messages?ids=a,b / GET /api/token-usage/messages/:message_id:单条消息的用量。
  • GET /api/workspaces/:workspace_id/analytics/tokens:汇总、趋势、按 CLI / 模型 / 供应商 / 项目 / 工位 / 成员拆分。成员只能看自己的用量,工作区所有者和管理员可以看全部(mine=true 或 user_id 缩小范围)。
  • GET /api/workspaces/:workspace_id/analytics/tokens/messages:按任务消息分页。

完整参数见 echo_admin 的 docs/API.md「AI Token 用量」一节。

对接接口(JSON-RPC 2.0)

所有通道使用同一套消息格式:

{ "jsonrpc": "2.0", "id": 1, "method": "providers.switch", "params": { "app": "claude", "id": "..." } }
{ "jsonrpc": "2.0", "id": 1, "result": { ... } }
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32001, "message": "...", "data": { ... } } }
// 服务主动推送
{ "jsonrpc": "2.0", "method": "ready", "params": { "version": "0.2.0", "protocolVersion": "1.0", "pid": 123, "api": null } }
{ "jsonrpc": "2.0", "method": "event", "params": { "type": "tools.job.log", "data": { ... } } }

通道

| 通道 | 启动方式 | 说明 | |------|----------|------| | HTTP | tb-switch serve 默认开启,或 --http / settings.api.enabled | 当前插件前端使用的通道 | | Electron utilityProcess | utilityProcess.fork(dist/node.js) | 预留给宿主侧车,自动识别 process.parentPort | | Node IPC | child_process.fork(dist/node.js) | 自动识别 process.send | | stdio | node dist/node.js --stdio | 每行一个 JSON,日志走 stderr |

服务参数:--transport=auto|parentPort|ipc|stdio|none、--http、--http-port=N、--no-proxy、--no-codex-sessions。

HTTP 接口

  • 只监听 127.0.0.1,默认端口 15822,地址写在 ~/.tb-switch/api.json。校验 Host 头,防止 DNS 重绑定。
  • 令牌优先取 TB_SWITCH_API_TOKEN,否则读 ~/.tb-switch/api-token(首次启动自动生成,权限 0600)。
  • GET /health:免鉴权,返回版本和是否支持配对。
  • POST /pair:{ "code": "123456" } 换取 { token, url }。连接码 10 分钟有效,同一时间只有一个,输错 5 次作废,成功后立即失效;错误返回 403 code_wrong / code_expired。
  • POST /rpc:Authorization: Bearer <token>,支持单条或批量。
  • GET /events:SSE 事件流,每帧 data 是 {type, data};鉴权同上,也支持 ?token=(给 EventSource 用)。
  • 跨域:默认放行 http://127.0.0.1|localhost|[::1]:<端口> 来源(Echo 桌面端从 http://127.0.0.1 加载页面),其他来源需要加到 api.allowedOrigins。放行跨域不代替鉴权,除 /health 和 /pair 外都要令牌。

方法

| 方法 | 参数 | 返回 | |------|------|------| | system.info / system.health / system.ping | – | 版本与通道 / 健康检查 / pong | | system.pairingCode | – | 新连接码 { code, expiresAt } | | system.shutdown | – | 优雅退出:先恢复被代理接管的配置,再保存待上报数据 | | settings.get / settings.update | – / 部分设置 | 设置(令牌显示为 ***) | | tools.list | { tools?, withLatest?, refresh? } | ToolStatus[] | | tools.probe | { tool } | 所有安装位置和将要执行的更新命令 | | tools.install / tools.update | { tool } / { tool, confirm? } | ToolJob(立即返回,进度走事件) | | tools.job / tools.jobs / tools.cancel | { jobId } | ToolJob | | providers.list / get / current | { app, id?, includeSecrets? } | 默认遮蔽密钥 | | providers.add | { app, name, quick? \| settingsConfig? \| category:"official", switchTo? } | Provider | | providers.update | { app, id, name?, quick?, settingsConfig? } | 被遮蔽的值原样传回时保留原值 | | providers.delete / switch | { app, id } | 当前供应商不能删除 | | providers.importLive / providers.live | { app, name? } / { app } | 导入现有配置 / 当前配置摘要 | | proxy.status / proxy.start / proxy.stop | { apps? } | ProxyStatus | | report.status / report.messages / report.retry / report.flush | 分页、msgId | 上报状态与本机记录 | | report.pending / report.ack | { limit? } / { leaseId, results } | 宿主转发模式:领取快照 / 回写 echo_admin 的结果 | | usage.recent | { limit?, msgId? } | 最近的用量记录 | | codexSessions.status / codexSessions.scan | – | Codex 会话扫描状态 | | models.fetch | { baseUrl?, apiKey?, app?, providerId?, ... } | [{ id, ownedBy }] | | multi.get / multi.save / multi.setMode | { app } / { app, routes, currentRouteId? } / { app, mode } | 工作方式与模型路由 | | providers.test | { app, id? , baseUrl?, apiKey? } | 地址可达性 + API Key 有效性 | | mcp.test | { profile, url?, token? } | MCP initialize 探测 | | system.restart | { version? } | 以指定版本重启本机服务 |

完整类型见 src/shared/protocol.ts(TbSwitchMethods)和 src/shared/types.ts。quick 是最常用的「地址 + 密钥」写法:{ baseUrl, apiKey, model?, haikuModel?, sonnetModel?, opusModel?, reasoningEffort?, wireApi? }。

事件:tools.job、tools.job.log、providers.changed、proxy.status、usage.recorded、report.status、settings.changed、service.health。

错误码

| 码 | 含义 | |----|------| | -32602 | 参数错误 | | -32601 | 方法不存在 | | -32001 | 冲突(如删除当前供应商、代理端口被占用) | | -32002 | 不存在 | | -32003 | 需要确认:tools.update 遇到多处安装,data 里是安装清单,带 confirm: true 重试 | | -32004 | 同一工具已有任务在执行 | | -32005 | HTTP 令牌无效 | | -32006 | 上游不可用(npm 仓库、上报接口) | | -32007 | 本地配置文件读写失败 |

自己写前端

import { createClient, createHttpTransport, pairLocalService } from "tb-npmswitch/client";

const { token, url } = await pairLocalService("http://127.0.0.1:15822", "123456");
const client = createClient(createHttpTransport({ baseUrl: url, token }));

const tools = await client.tools.list();
const job = await client.tools.update("claude", true);
await client.tools.waitForJob(job.id, line => console.log(line.line));
await client.providers.add({ app: "codex", name: "中转A", quick: { baseUrl: "https://x/v1", apiKey: "sk-..." }, switchTo: true });
client.on("usage.recorded", record => console.log(record.msgId));

注意事项

  • 切换供应商前,会把 CLI 配置备份到 ~/.tb-switch/backups/<app>/。写入时只替换供应商相关的字段,其他用户设置保留。Codex 的 config.toml 会被重新格式化,注释不会保留。
  • 从官方登录切到第三方时,Codex 的 ChatGPT 登录信息会先保存到 ~/.tb-switch/codex/official-auth.json,切回官方时恢复。
  • 预设供应商只收录能直接透传的服务:代理不做协议转换,Codex 自定义供应商必须支持 Responses 接口。
  • Node 自带的 fetch 不读取 HTTP(S)_PROXY。上游必须走代理时,启动服务前设置 NODE_USE_ENV_PROXY=1(Node ≥ 22.21)。
  • 不要同时运行桌面版 CC Switch 的代理和上报,否则同一台电脑的用量会被统计两次。
  • Windows 下的检测和执行逻辑已移植,但还没有在真机上验证。