openclaw/.github
Ayaan Zaidi 09e2b15466
fix(models): share API-key editing and removal (#144420)
Related: #136257
Supersedes #142851. Builds on #144181.

## What Problem This Solves

Fixes an issue where API keys saved on the Models page followed a different persistence path from CLI keys, and CLI logout refused to remove profiles referenced by provider configuration.

## Why This Change Was Made

Models and CLI key operations now share credential selection, persistence, and removal. Removal clears references for the same captured credential plan that the store deletes; concurrent replacements are preserved. Both paths use the Gateway auth-refresh owner and retain recovery guidance after a committed write.

If another client changes the provider binding while the CLI key prompt is open, the saved key does not replace the newer binding. The CLI reports the committed key and tells the user to reopen Models. Internal write controls stay outside the public plugin interface.

Reference-backed profiles cannot be converted to inline keys by replacement. If a concurrent credential generation rejects removal after config cleanup, the remover restores only cleanup-owned config values that no later writer changed.

If removal throws or commits only some owner stores, the removal owner reads the targeted stores again. It restores references only for credentials that still exist and keeps successful deletions removed.

Recovery replays only the captured config delta for targeted survivors. A retained token or external-secret profile keeps its provider binding.

## User Impact

- Edit or remove saved API keys through Models or the CLI with the same stored result.
- Keep model defaults, connection settings, account metadata, credential copy restrictions, tokens, and reference-backed keys intact.
- Explicit CLI profile selection still supports named backups without changing the active connection.
- Agent-local overrides remain intact; a conflicting shared-key replacement reports how to resolve the override.

## Evidence

- Baseline CLI: removing a configured profile failed with the provider-reference refusal.
- Baseline browser: Models reported “Secret saved,” while the key remained inline in provider configuration and the profile store stayed empty.
- Real Gateway/browser and CLI proof: save and removal produce identical credential/configuration state; both refresh auth successfully and preserve the selected model.
- Targeted owner, CLI, Gateway, UI, portability, and plugin-interface tests cover persistence, reference cleanup, concurrent credential replacement, retained credential kinds, explicit backups, administrator scope, and committed-write warnings.
- Independent real-browser acceptance passed UI/CLI state parity, metadata and credential-kind preservation, administrator-scope enforcement, config-write failure recovery, and active-run preservation during targeted removal. Full-provider logout closed the matching real local-provider streams.
- Final rebased candidate: 230 targeted owner, CLI, and Gateway tests passed. The rebuilt Gateway/UI passed the real-browser and CLI save/remove parity test. UI and portability checks passed on the integrated candidate, whose Auth B production files match the final candidate.
- Corrective focused checks: 80 tests passed for the binding race, public plugin boundary, CLI test types, and UI end-to-end inventory. Protocol generation passed.
- Real CLI race proof: a second CLI changed the provider from `fixture:manual` to `fixture:secondary` while key entry waited. The first CLI then preserved `fixture:secondary`, returned the saved-key recovery message, and exited 1.
- Final review-fix checks: 151 owner and Gateway tests passed. They cover configured and default reference-backed profiles plus API-key-only and full-provider removal races that preserve credential, profile metadata, order, and provider binding.
- Final public CLI proof: reference-backed replacement rejected without state change, then logout and save succeeded. A same-ID concurrent replacement completed while logout waited on the config lock; the rejected logout preserved credential and config, then retry and save succeeded.
- Rebased-head checks: 291 focused owner, CLI, Gateway, SDK, and UI-inventory tests plus 50 Models-page tests passed. Protocol generation, full build, the public CLI campaign, and real Models-page UI/CLI parity passed again after the Models login work landed.
- Incomplete-removal checks: 154 owner and Gateway tests passed. They cover thrown store errors, incomplete removal, partial multi-store deletion, surviving-reference restoration, and successful retry.
- Untargeted-binding checks: 155 owner and Gateway tests passed. Failed API-key-only removal preserves the retained token binding, and retry removes only the targeted key.
- Proof limit: the running Gateway's internal refresh exception was not live-injected. Gateway tests cover the failure branches and UI tests cover the resulting warning; remote-target and absent-local-Gateway CLI outcomes were observed separately.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-11 05:12:54 +05:30
..
actions fix(ci): accept native iOS keychain filenames (#142968) 2026-09-09 23:59:42 +09:00
codeql fix(qa): keep leases when Gateway startup teardown fails (#131740) 2026-08-28 14:31:29 -07:00
codex/prompts fix(docs): rerun failing validation after translation repair (#133945) 2026-08-31 01:47:51 -07:00
images/live-media-runner
instructions fix(runtime): require Node builds with lossless SQLite reads (#140672) 2026-09-07 10:31:31 -07:00
ISSUE_TEMPLATE docs(templates): ask feature requesters whether they plan to implement (#109258) 2026-07-16 18:44:41 -07:00
pr-proof fix(ui): center the working claw in chat layout (#133682) 2026-08-30 20:12:14 -07:00
release fix: retain proxy DNS checks with Undici security updates (#139056) 2026-09-05 05:07:19 -07:00
workflows fix(models): share API-key editing and removal (#144420) 2026-09-11 05:12:54 +05:30
actionlint.yaml feat(qa): add Convex-leased Telegram userbot proof (#131715) 2026-08-29 07:56:30 +05:30
CODEOWNERS docs: split gateway/sandboxing into a directory of child pages (#143522) 2026-09-10 09:11:27 +09:00
dependabot.yml chore(deps): enforce seven-day npm cooldowns and refresh eligible pins (#132385) 2026-08-29 02:07:05 -07:00
labeler.yml docs: split gateway/sandboxing into a directory of child pages (#143522) 2026-09-10 09:11:27 +09:00
package-trusted-sources.json
pull_request_template.md docs: require maintainer edits on pull requests 2026-07-10 09:18:28 -05:00
retired-sticky-disks.json ci: replace dependency sticky disk with exact cache (#123040) 2026-08-13 01:13:25 -07:00
zizmor.yml chore(compat): date the annotated deprecation families and expose removal-pending debt (#114002) 2026-07-25 21:01:20 -07:00