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

cctra

v0.8.1

Published

Local LLM provider protocol converter + plugin host. Runs a local HTTP server on 127.0.0.1:3133 that translates between OpenAI Chat / OpenAI Responses / Anthropic Messages, with a global alias table (`cctra switch` to rebind without restarting the client)

Readme

cctra

Local LLM provider protocol converter + plugin host

cctra runs a local HTTP server on 127.0.0.1:3133 that translates between OpenAI Chat Completions / OpenAI Responses / Anthropic Messages, with a global alias table (rebind without restarting the client) and local-path plugin support for non-standard upstream auth (OAuth, mTLS, etc.).

Quick start

# install (once)
bun add -g cctra
# or npm i -g cctra

# add a provider (interactive wizard)
cctra add

# start the server (foreground)
cctra serve

Endpoints

cctra exposes exactly 3 protocol endpoints on 127.0.0.1:3133:

| Protocol | Path | |---|---| | Anthropic Messages | POST /v1/messages | | OpenAI Chat Completions | POST /v1/chat/completions | | OpenAI Responses | POST /v1/responses | | OpenAI Models | GET /v1/models | | Health | GET /healthz |

Aliases — the only short-name system

cctra has one place where every short name lives: the [aliases] table in ~/.config/cctra/config.toml. An alias is a name → provider/model pointer; clients send the alias as their model field and cctra routes to the upstream.

Three things to know:

  1. Auto-generated: when you cctra add a provider, every model whose id is globally unique gets aliases[id] = "provider/id" for free — clients can use the short id immediately, no provider/ prefix needed.
  2. Manual slots for stable client config: cctra pre-seeds three empty aliases — cctra-pro / cctra-flash / cctra-vision. Bind them with cctra switch <name> and your Claude Code / Codex configs can hard-code those names forever; switching upstreams is a one-line CLI call that hot-reloads (no client restart).
  3. Add your own: cctra alias add <name> for empty slots, cctra alias <name> <target> to set in one shot.
cctra add                       # walks the wizard, auto-aliases unique ids
cctra alias                     # list all aliases (bound + unbound)
cctra switch cctra-pro          # interactive: pick a model from the dropdown
cctra switch cctra-pro ark/doubao-seed-1-6   # non-interactive
cctra alias rm cctra-vision     # remove a slot you don't use

cctra ls shows everything at a glance:

cctra ls
# ALIASES
#   cctra-pro         → ark-sub/doubao-seed-1-6   [Ark Sub]
#   doubao-seed-1-6   → ark-sub/doubao-seed-1-6   [Ark Sub]
#   sonnet            → or/anthropic/claude-3.5   [OpenRouter]
#
# UNBOUND
#   cctra-flash
#   cctra-vision
#
# OTHER MODELS
#   ark-sub/doubao-1-5-pro    [Ark Sub]
#   ark-sub/doubao-1-5-vision [Ark Sub]

Client integration

Each client picks its baseURL + model field to hit the right protocol endpoint. Two clients that speak the same protocol (e.g. Claude Code and any other Anthropic-SDK-based client) use the same baseURL — only the model field varies.

⚠️ baseURL must include the right prefix for the SDK. Anthropic SDK appends /v1/messages internally (set baseURL=http://127.0.0.1:3133). OpenAI SDK appends /chat/completions and /responses (set baseURL=http://127.0.0.1:3133/v1).

Claude Code

export ANTHROPIC_BASE_URL=http://127.0.0.1:3133
export ANTHROPIC_AUTH_TOKEN=anything  # cctra 不验 Anthropic 客户端的 token;填任意占位即可

model field can be any alias (cctra-pro / cctra-flash / provider-unique-id) or full name (provider/id). The [1m] context suffix is processed client-side by Claude Code — cctra never sees it.

Codex (and OpenAI SDK / Cursor)

export OPENAI_BASE_URL=http://127.0.0.1:3133/v1
export OPENAI_API_KEY=anything  # 同上,cctra 不验 OpenAI 客户端的 key

Codex defaults to the Chat Completions path (/v1/chat/completions). For the Responses path (/v1/responses), the client must be configured to send there explicitly — cctra will route whichever path the client picks.

任意 OpenAI 兼容客户端(opencode / 其他)

baseURL = http://127.0.0.1:3133/v1. The client picks the path:

  • requests to /v1/chat/completions → Chat path
  • requests to /v1/responses → Responses path

If the client allows custom paths, prefer /v1/responses — it's closer to cctra's canonical model and carries more forward-compat extras (per 0.6.0 parity work). opencode / Continue.dev / Aider / any custom OpenAI-SDK wrapper fall into this category.

Model field: 全名 vs alias

  • Alias 短名cctra-pro / cctra-flash / provider-unique-id)— 推荐
    • 客户端写死 model: cctra-pro,服务端 cctra switch cctra-pro <new-target> 热切上游,client 不需要改
    • 3 个预置空槽(cctra-pro / cctra-flash / cctra-vision)走 cctra alias addcctra switch 绑定
  • 全名provider/id,如 or/anthropic/claude-3.5)— 锁定上游时
    • 不依赖 alias 表,配置文件丢了也能 resolve
    • 切上游要改 client 的 model 字段

Known inter-protocol incompatibilities

cctra's canonical layer is best-effort, not lossless. When you mix protocols, expect these:

  1. document blocks 丢 — Anthropic document (PDF/图片) → Chat/Responses upstream 静默丢。仅 Anthropic↔Anthropic round-trip 安全。
  2. thinking / signature deltas 丢 — Chat 路径无对应字段。Anthropic↔Responses round-trip 文本保留,signature 丢。
  3. redacted_thinking 降级文本 — 在 Chat/Responses 客户端呈现为 [redacted_thinking] literal 字符串(0.5.1 实现)。
  4. refusal 块变文本 — Chat 上游的 refusal → Anthropic 客户端看到 [refusal] … 普通 text block,Claude Code refusal 分支不会触发。
  5. image parts 在 Chat/Responses 出站被 re-encode 成 data: URL — 即使入站是 URL 也重编码,payload 涨;远程大图要走 Chat 上游时尤其明显。
  6. 未知 block / item 占位 — Anthropic 未知 block → [unknown_block:<type>];Responses 未知 item → [unknown_input_item:<type>](含 web_search_call / mcp_call 等 5 个内置 tool)。原 payload 保留在 extras 里。
  7. stop_reason: "error""refusal" — 0.5.1 修复;Chat 上游 content_filter 让 Anthropic 客户端看到 refusal 事件。
  8. Anthropic system 数组(带 cache_control)丢 cache_control — Chat 顶层 system 是 string 无元数据空间,cache_control 元信息无法承载。

Plugin system

Add custom JS plugins for non-standard upstream auth:

cctra plugin add my-internal /path/to/my-internal.js
cctra plugin ls
cctra plugin enable my-internal
cctra plugin disable my-internal
cctra plugin rm my-internal

A plugin exports:

export default {
  name: "my-internal-llm",
  displayName: "My Company LLM",
  async getConfig(ctx) {
    // OAuth / mTLS / custom header logic
    return { baseUrl: "...", path: "/v1/chat/completions", apiFormat: "openai-chat", authHeader: { /* ... */ }, modelId: "..." };
  },
  async listModels(ctx) { return [{ id: "..." }, { id: "..." }]; },
};

See examples/plugins/ for working examples.

Rectifiers (vendor-quirk workarounds)

Some upstream providers have idiosyncratic request-shape requirements that aren't part of any standard protocol. Classic example: Kimi's Anthropic-compatible endpoint (api.moonshot.cn/anthropic) only accepts thinking: { type: "enabled", budget_tokens: N } — it 400s on the effort shorthand ("high"/"medium"/"low") that Claude Code sends, and on boolean true/false. Without rectification, the request goes through cctra unchanged and Kimi rejects it.

cctra's rectify subsystem lets you attach named, composable rules to specific providers. A rule rewrites the upstream wire body right before the HTTP fetch. Currently bundled:

| Rule id | Effect | |---|---| | normalize-thinking-type | Coerce Anthropic thinking.type to "enabled"/"disabled" string. Any non-literal-"disabled" value (including "high"/"medium"/"low"/"xhigh"/"max"/boolean true) → "enabled". Only fires for anthropic-messages upstream. |

Two-level control: global on/off per rule + per-provider attach (whitelist). Provider not in [rectify.providers.X] → no rules run, regardless of global toggle. Rules throw → logger.warn + skip; request continues.

cctra rectify                                     # list rules + attachments + per-rule status
cctra rectify enable normalize-thinking-type      # global toggle
cctra rectify disable normalize-thinking-type
cctra rectify attach kimi normalize-thinking-type # whitelist a provider
cctra rectify detach kimi normalize-thinking-type

TOML equivalent (manual edit ~/.config/cctra/config.toml):

[rectify.rules]
"normalize-thinking-type" = true

[rectify.providers]
"kimi" = ["normalize-thinking-type"]

Scope notes (v1):

  • Only TS-built-in rules ship — adding a new rule is a code change (drop a file in src/convert/upstream/rectify/rules/ and add it to the registry).
  • Plugin upstreams do NOT participate in rectify. Plugins are arbitrary JS and handle their own quirks via getConfig. If you need plugin-level transforms later, that's a transformRequest hook on the plugin API.
  • One built-in rule today. The architecture is "onion-style" internally so new rules compose left-to-right without touching the dispatcher.

CLI

cctra add                       # interactive provider wizard
cctra edit <name>               # edit models on a provider (multiselect)
cctra alias                     # list all aliases (bound + unbound)
cctra alias <name>              # show what an alias points to
cctra alias <name> <target>     # set/create alias (target is `provider/model` or another alias)
cctra alias add <name>          # create an empty alias slot
cctra alias rm <name>           # remove an alias
cctra switch [<name>] [<tgt>]   # interactive switch (prompts when args omitted)
cctra ls                        # list aliases + models
cctra show <name>               # show provider / plugin details
cctra rm <name>                 # remove provider / plugin / model (unbinds related aliases)
cctra rename <old> <new>        # rename provider (updates alias values automatically)
cctra plugin add <name> <path>
cctra plugin ls / show / enable / disable / rm
cctra serve [--port N]          # foreground HTTP server

Configuration

Persisted at ~/.config/cctra/config.toml (TOML format, edited via CLI).

Plugin configs go in ~/.config/cctra/plugins/<name>/config.json.

Architecture

  • src/canonical/ — protocol-agnostic internal types
  • src/convert/ — bidirectional protocol conversions
  • src/server/ — Bun.serve() routes, upstream forwarding
  • src/plugin/ — local-path plugin loader + author contract
  • src/core/resolve.tsprovider/model and global [aliases] table resolution
  • src/core/alias.ts — auto-alias decision (id globally unique → silent aliases[id] = "provider/id")

For design rationale, request lifecycle, and subsystem deep-dives, see ARCHITECTURE.md.

Credits

The vendor preset list (src/providers/presets.ts) — provider names and endpoint URLs — is derived from cc-switch (MIT, Copyright (c) 2025 Jason Young). Thanks to Jason and the cc-switch contributors for maintaining this comprehensive registry.

License

MIT — see LICENSE.