feat(polli-cli): add Codex and Claude Code router harnesses - #15249
Open
aikhusus2025-ctrl wants to merge 2 commits into
Open
aikhusus2025-ctrl wants to merge 2 commits into
aikhusus2025-ctrl wants to merge 2 commits into
Conversation
Adds `polli harness codex` (via duolahypercho/codex-router) and `polli harness claude-code` (via musistudio/claude-code-router). Fixes pollinations#15218
Strix Security ReviewWarning This pull request has 6 commits after the last Strix review ( No security issues found. Updated for Reviewed by Strix |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR — feat(polli-cli): add Codex and Claude Code router harnesses
Fixes #15218
Summary
Two
polli harnessadapters, one per required integration, built on the shared harnesslifecycle (
resolveHarnessKey,fetchHarnessModels,applyWithSnapshot/restoreOrStrip,prerequisite check before login,
--model, smoke check beforeonsucceeds).Codex — Codex Router (
duolahypercho/codex-router). Everything goes through the router'sown commands:
providers generic add pollinations --base-url https://gen.pollinations.ai/v1 --adapter openai-chat, the curated live catalog (curate-models pollinations --models … --apply),the Codex wiring (
enable, the same stepcodex-router setupruns), and the routed default(
control model-set). The router'scredential setcommand reads a hidden TTY prompt, so thededicated key is written with the router's own credential code driven with the secret on stdin
(never argv, never the environment, never shell history, never
config.toml); if that module pathmoves, the adapter writes the documented protected key file and opaque reference itself and
refuses to continue unless the router's own
credential statusagrees. Native ChatGPT login,native models, other providers, and profiles are untouched.
Claude Code — Claude Code Router (
musistudio/claude-code-router). The router ownsconfig.sqliteand exposes it only through its authenticated management API, so the adapter usesthat API — the one its own UI uses — discovered from the
service.jsonthe router writes(
{pid, url}, token in the URL,x-ccr-web-authheader). It adds apollinationsprovider and anisolated
Pollinationsprofile for theclaude-codeagent withscope: ccr, so nothing touches~/.claudeor the native login until the user starts Claude Code through that profile. If therouter is not running,
onstops before login/key creation and says so.Both adapters take models from the live catalog (
/v1/models, first-party tool-calling textmodels) and never ship a model registry;
--model <id>picks the default. Both follow theexisting adapter conventions (bloom/dsh/opencode/pi/prime).
Outcome checklist
polli harness codex on|off|statusvia Codex Routercodex.tspolli harness claude-code on|off|statusvia Claude Code Routerclaude-code.tsrequireRouter/requireServiceresolveHarnessKey+ router credential writer / RPC provider entry--model, no stale registryfetchHarnessModelsscope: ccrprofilestatusreports install/readiness/provider/key/model/missing prerequisitecodexStatus,claudeCodeStatusoffrestores byte-for-byte where possible, else removes only owned entriesrestoreOrStrip+ router-nativeremove/disablein both adaptersonfinishes only when the router's owncredential statusreports the key configured;codex-router test-modeldocumented as the optional end-to-end checkcodex.test.ts,claude-code.test.tsCODING_HARNESSES.mdedits (the page theharnesshelp already links to)Notes for the reviewer
CODING_HARNESSES.mdalready exists onmainand is the Scalar page for this tag; this PRedits it in place. (PRs that add that file replace the existing page instead.)
harnesses/fs.tsgains two small helpers (captureFiles,restoreCapturedFiles) rather thaneach async adapter growing its own copy of a "read these before I change them" routine. The
diff there is append-only.
onis atomic in both directions: a run that fails after registering the Codex providerwithdraws the provider it created (a pre-existing one is left alone) and restores
config.toml; a Claude Code run whose RPC write rewrote~/.claude/settings.jsonbeforefailing puts the user's bytes back. Both are covered by tests.
node:child_process,node:crypto,node:fs, globalfetch. Thepackage still supports Node 20, which is why the Claude Code adapter goes through the router's
API instead of writing
config.sqlite.config.tomlblock, and that claim is sticky across re-runs: otherwise a second
on(a new--model)would drop it, and a later
offafter a user edit would leave the block behind.Evidence
npx vitest run src/harnesses/codex.test.ts src/harnesses/claude-code.test.ts(26 tests)npx biome checkclean on the four files;npm run buildpolli harness codex on→ quit/reopen Codex → pick apollinations/*model → one-word reply;
off→ previousconfig.tomlbackccr start,polli harness claude-code on,ccr Pollinations→ one-word reply through
:3456;offremoves only the two entriesThe two suites were also executed end to end here (transpiled TypeScript over an in-memory
filesystem, with the fake routers driving both adapters); all 26 pass. See
README.md→ "How these tests were exercised" for exactly what that does and does not cover.