* cli: publish freellmapi to npm (#671) The README and docs/clients.md have documented `npx freellmapi setup-claude` since the CLI workspace landed, but the package was never pushed to the registry — every documented command failed with npm E404. The CLI itself is fine; only the release path was missing. This adds it: - cli-release.yml publishes on pushes to main that touch cli/**, guarded by a registry version check so ordinary source changes are a no-op and only a version bump ships. workflow_dispatch covers the first publish. Publishes with --provenance so the tarball is traceable to this repo and commit. - The workflow installs the packed tarball and runs the binary before publishing, which catches a `files` array that drops a runtime import — something the unit tests cannot see. - cli/README.md and cli/LICENSE: the package shipped neither, so the npm page would have been blank for a package we ask people to npx. Both are added to `files`, along with homepage/bugs/keywords. - The documented setup command omitted the credential and exited with "No API key supplied", so README and docs/clients.md now show --api-key and the FREELLMAPI_API_KEY alternative. Publishing needs an NPM_TOKEN repository secret; the workflow fails with an explicit message when it is absent rather than erroring inside npm publish. * cli-release: skip instead of failing when NPM_TOKEN is absent As written, merging this workflow would have put a red X on main: the push trigger fires, 0.3.0 is unpublished, and the token check exits 1. The secret cannot be added until the npm account is available, so the first consequence of landing the fix would have been a broken main. An automatic run now warns and skips. A manual run still fails loudly, since explicitly asking for a publish and silently getting none is worse than an error. The step summary says which case it hit. * cli-release: use trusted publishing (OIDC) instead of NPM_TOKEN npm revoked every classic token in December 2025, so the "classic automation token" this workflow was written around no longer exists. The replacement, granular access tokens, default to enforcing 2FA on write and the "bypass 2FA" escape hatch is being wound down through 2026 — it loses sensitive management actions this August and direct publish around January 2027. A token-based workflow would have been deprecated before its first run. Trusted publishing mints a short-lived OIDC credential per job, so there is no long-lived secret in the repository at all, and npm attaches provenance without being asked. Node moves to 24 because OIDC needs npm >= 11.5.1 / Node >= 22.14.0; that is still inside the repo's engines range. Trusted publishing cannot bootstrap a package that does not exist — npm has no package settings page to configure a publisher against until a first version exists — so 0.3.0 is published by hand and every later version goes through this workflow. The npmjs.com setup is documented in the header. * cli: drop the leading ./ from the bin path Publishing 0.3.0 warned: npm warn publish "bin[freellmapi]" script name dist/index.js was invalid and removed The wording is alarming but benign — npm normalises "./dist/index.js" to "dist/index.js" and publishes the corrected form, so 0.3.0 on the registry does have a working bin. Writing it normalised keeps the warning out of future releases and stops the next person having to verify the registry to find out whether the binary survived.
7.4 KiB
Clients & coding agents
← Back to README · Documentation index
- OpenAI-compatible clients
- Coding agents
- Native Gemini clients
- Ollama clients
- Headerless clients
- MCP server
- VS Code ghost-text autocomplete (Continue)
- Context Handoff
OpenAI-compatible clients
Any client that can target an OpenAI-compatible base URL can use FreeLLMAPI:
- LangChain, LlamaIndex, official OpenAI SDKs: set
base_urltohttp://localhost:3001/v1and use the unified key from the dashboard. - Local GPU boxes: add custom OpenAI-compatible endpoints for Ollama, llama.cpp, LM Studio, vLLM, or an internal gateway.
Coding agents
Use the generator instead of hand-editing a client configuration:
export FREELLMAPI_API_KEY=<unified-key> # or pass --api-key on each command
npx freellmapi setup-claude --url http://localhost:3001 --dry-run
npx freellmapi setup-claude --url http://localhost:3001
--dry-run prints a diff. Real writes merge with the existing configuration
and create a timestamped backup first. --profile <name> creates a named
Claude/Codex profile. The live /v1/models catalog supplies the model ids and
context windows.
| Agent | Automated command | Manual base URL | Wire |
|---|---|---|---|
| Claude Code | setup-claude or credential-free-on-disk launch |
http://localhost:3001 |
Anthropic Messages |
| Codex CLI | setup-codex or launch-codex |
http://localhost:3001/v1 |
Responses (wire_api = "responses") |
| Cline | setup-cline |
http://localhost:3001/v1 |
OpenAI Chat |
| Continue | setup-continue |
http://localhost:3001/v1 |
OpenAI Chat / legacy Completions |
| Aider | setup-aider |
http://localhost:3001/v1 |
OpenAI Chat |
| OpenCode | setup-opencode |
http://localhost:3001/v1 |
OpenAI Chat |
| Goose | setup-goose |
http://localhost:3001/v1 |
OpenAI Chat |
| Qwen Code | setup-qwen |
http://localhost:3001/v1 |
OpenAI Chat (native Gemini also works) |
| Roo Code | setup-roo |
http://localhost:3001/v1 |
OpenAI Chat |
| Kilo Code | setup-kilo |
http://localhost:3001/v1 |
OpenAI Chat |
| Crush | setup-crush |
http://localhost:3001/v1 |
OpenAI Chat |
| Cursor | setup-cursor prints the guide |
public https://…/v1 |
OpenAI Chat |
| Anything else | setup-generic prints a ready block |
http://localhost:3001/v1 |
OpenAI Chat |
The root-vs-/v1 distinction matters: Claude Code expects the server root
because it appends the Anthropic Messages path. OpenAI-compatible clients in
this table—including Cline, Aider, Goose, Codex, Continue, OpenCode, Qwen,
Roo, Kilo, and Crush—expect their configured base URL to include /v1.
Native Gemini clients
Gemini CLI and Gemini-lineage clients can speak Google's wire format directly:
export GOOGLE_GEMINI_BASE_URL=http://localhost:3001
export GEMINI_API_KEY=freellmapi-your-unified-key
gemini
The native surface implements GET /v1beta/models, model metadata,
generateContent, streamGenerateContent (including ?alt=sse), and
countTokens. Authentication accepts x-goog-api-key, Bearer, or Gemini's
?key= fallback. Prefer the header: query credentials leak into history and
proxy logs.
The Keys → Agents tab maps Gemini Pro, Flash, and Flash-Lite family names to Auto or a pinned catalog model.
Ollama clients
Ollama emulation is off by default. Enable one of these modes on Keys → Agents:
open-loopback: no key on this machine only. The socket peer must be127.0.0.1/::1; enabling desktop LAN access does not widen it. Docker note: inside a container the socket peer is the Docker bridge IP, not loopback, so this mode refuses even host-local traffic through a published port — usekey-requiredfor Docker deployments.key-required: clients must sendAuthorization: Bearer <unified-key>.
The exact endpoints are /api/tags, /api/chat, /api/generate, /api/show,
/api/version, /api/embed, and legacy /api/embeddings. Streaming uses
newline-delimited JSON, not SSE. Point Zed, JetBrains AI Assistant, or another
Ollama-capable client at http://localhost:3001.
Headerless clients
If a client cannot set headers, create a separately revocable token on Keys → Agents and use:
http://localhost:3001/v1/t/<token>/chat/completions
http://localhost:3001/v1/t/<token>/responses
http://localhost:3001/v1/t/<token>/models
The same prefix exposes /api/chat and /api/tags. Never put the unified API
key in a URL. URL tokens have independent hashes and immediate revocation
because URLs routinely leak into shell history, reverse-proxy logs, and
telemetry.
MCP server
On top of inference, the router is an MCP server: agents can introspect it mid-session (usable models and the params each one honors, provider health, usage and cache stats, routing strategy). For Claude Code:
claude mcp add --transport http freellmapi http://localhost:3001/mcp \
--header "Authorization: Bearer freellmapi-your-unified-key"
Any MCP client that speaks Streamable HTTP works the same way: point it at /mcp with the
unified key as a Bearer token.
FreeLLMAPI is local-first and single-user by design. Your provider keys stay in your SQLite database, encrypted at rest, and requests go from your machine to the upstream providers you enabled.
VS Code ghost-text autocomplete (Continue)
FreeLLMAPI exposes /v1/completions for editor autocomplete clients that send legacy OpenAI prompt/suffix requests. Example Continue config:
models:
- name: FreeLLMAPI Autocomplete
provider: openai
model: auto
apiBase: http://localhost:3001/v1
apiKey: freellmapi-your-unified-key
useLegacyCompletionsEndpoint: true
roles:
- autocomplete
Context Handoff
When FreeLLMAPI falls over to a different model mid-conversation (quota, rate limit, cooldown), the new model has no idea it is picking up someone else's task. Context handoff adds a single compact system message to the outbound request that tells the new model exactly that:
FreeLLMAPI context handoff:
You are taking over an ongoing conversation from another model (groq:llama-3 → google:gemini-flash).
Continue the user's task using the conversation context already provided in this request.
Do not restart the task, re-ask already answered setup questions, or discard prior tool results.
Respect the user's latest message as the highest-priority instruction.
Recent session summary:
User: …
Assistant: …
Enable it in .env:
FREELLMAPI_CONTEXT_HANDOFF=on_model_switch
How it works:
- Messages per session are stored in memory (TTL: 3 hours).
- Only injected when the selected model changes for a given session key.
- Not injected on the first request, on same-model continuations, or if a handoff message is already present.
- Session key:
X-Session-Idheader if present, otherwise SHA-1 of the first user message (same as sticky sessions). - Storage is in-memory only. Nothing is written to disk or logged.
Important: Context Handoff improves continuity for conversations routed through FreeLLMAPI. It cannot recover provider-internal hidden state or messages that were never sent to the proxy.