feat(omp): add an omp extension that follows .credentials.json - #27
Merged
Merged
Conversation
omp keeps its own Anthropic login, so sca switch and sca monitor never reached it. The extension serves omp's anthropic provider from Claude Code's .credentials.json, re-read on every request, so omp follows a switch without a restart. It is read-only: it never calls the token endpoint and stores 'external' in place of the refresh token, because a fourth party rotating the same refresh token would invalidate the copy in the slot. An expired file is renewed by one claude -p run, which refreshes under Claude Code's cross-process lock. omp is kept at exactly one enabled row so its own usage-ranked rotation never competes with sca monitor, and the row is written only when the token changed, since omp keeps every replaced row as a disabled one. Verified live against omp 18.4.4: a running omp session followed an sca switch mid-request.
The extension is optional and used from a checkout, so its tests stay a local check rather than spending a three-OS matrix on every pull request. docs/testing.md now says CI does not run them, so nobody reads a green run as covering the extension.
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.
What
Adds
integrations/omp/claude_credentials.ts, an optional extension for omp (oh-my-pi). It serves omp's built-inanthropicprovider from Claude Code's.credentials.json, sosca switchandsca monitorswitch omp as well, with no omp restart. It is used from the git checkout by listing its path underextensions:in~/.omp/agent/config.yml; there is no release asset.getApiKeyreads the file again on every request and returns its token. When a read fails, for example inside sca's atomic replace, it keeps the last good token.refresh: "external"in place of the refresh token. The login is written at session start, oncredential_disabled, and when the file's token differs from the stored one or a second login appears. It is written only when something changed.refreshTokennever calls the token endpoint. A token that is still valid is returned with its real expiry. An expired one triggers oneclaude -prun (--safe-mode, Haiku, 90 s timeout, shared by concurrent refreshes), and then the file is read again.node --testwith an injected file reader, clock andclauderunner, and fake tokens only. CI does not run them, so they are a local check;docs/testing.mdandAGENTS.mdsay to run them whenintegrations/omp/changes.sca helpand in theInvoke-MonitorActioncomment (no logic change); an omp (oh-my-pi) section indocs/claude-code-internals.mdwith the omp 18.4.4 facts the extension relies on and a recipe to re-check them; and how to run the tests indocs/testing.mdandAGENTS.md.Why
omp keeps its own Anthropic login and never reads Claude Code's, so until now sca had no effect on it. Three decisions shape the extension:
Update-SlotTokensand opencode-claude-auth can already rotate the same token without coordinating; only Claude Code serializes its own processes. If omp refreshed on its own, that would make a fourth party, and one lost race leaves the slot holding a dead refresh token.claude -prefreshes under Claude Code's cross-process lock, so the fallback adds no new party.sca monitorhas to stay the only thing that rotates. omp'supsertOAuthadds a new login each time when there is no identity key, so the extension usespool.setinstead.invalid_grant,revoked,refresh token expired, 401/403 and similar patterns, and only blocks it for 5 minutes on anything else. The extension's messages avoid those patterns, and a test enforces that.Verification
node --test "integrations/omp/*.test.ts": 26 pass.tests/Invoke-Tests.ps1: 1073 pass, coverage 98.8% against the 97% gate.sca switchchanged accounts; the run finished, and omp's stored login then matched the new slot. The check compared tokens by hash only.The live run caught one bug before this commit. omp's
pool.setdoes not delete the logins it replaces; it keeps each one, disabled as "replaced by newer credential". The first version re-seeded at every session start and added one disabled entry per omp run. The extension now writes only when the stored login differs, and the internals doc states the corrected fact.Shortcomings
ctx.modelRegistry.authStorage.credentials, the shape ofentries()and the disable patterns are not a public omp API. A new omp release can break them silently. When the credential pool is missing, the extension warns and stores nothing, but a changed disable pattern would only show up as a disabled login.getApiKey's. Between a switch and the next request, they still use the old account. The extension updates the stored login on the firstgetApiKeyafter a switch; it has no way to see a switch before that.claude.cmdon Windows, only the native executable.OMP_AUTH_BROKER_URL,auth.broker.*), which replaces the custom refresh with omp's own. The README says so, but the extension doesn't detect it./login anthropicinside omp adds a second login. The extension replaces it on the next request, rather than preventing it.Feedback wanted
claude -pfallback: is one run of Claude Code the right place to put expiry, or should the extension refuse and leave expiry tosca monitor?getApiKey: they fire on a token mismatch or on more than one enabled login.docs/claude-code-internals.mdmeets that file's admission rule. The re-check recipe greps the omp source rather than running a probe.Not done
CHANGELOG.mdentry; that's written as a release step.