fix(cli): honor agent for model auth logout (#85326)

This commit is contained in:
Peter Steinberger
2026-05-22 13:46:07 +01:00
committed by GitHub
parent 4a9138556e
commit fc47c1f55e
3 changed files with 43 additions and 4 deletions

View File

@@ -40,6 +40,7 @@ Docs: https://docs.openclaw.ai
- Cron: honor `cron.retry.retryOn: ["network"]` for common network error codes such as `EAI_AGAIN`, `EHOSTUNREACH`, and `ENETUNREACH`.
- Agents/OpenAI: preserve structured provider error code, type, and redacted body metadata on boundary-aware transport failures.
- Doctor/Codex: point native Codex asset warnings at the canonical `openclaw migrate plan codex` preview command. Fixes #84948. Thanks @markoa.
- CLI/models: make `capability model auth logout --agent` remove auth profiles from the selected non-default agent store. Fixes #85092. Thanks @islandpreneur007.
- CLI/agents: retry transient normal-close Gateway handshakes before falling back to embedded `openclaw agent` execution.
- CLI/update: keep managed Gateway service stop/restart status lines out of `openclaw update --json` stdout so package-update automation can parse the JSON payload.
- Plugins: resolve OpenClaw plugin SDK subpaths for native external plugin runtimes without mutating package installs or broadening process-wide module resolution.

View File

@@ -24,6 +24,7 @@ const mocks = vi.hoisted(() => ({
setRuntimeConfigSnapshot: vi.fn(),
loadAuthProfileStoreForRuntime: vi.fn(() => ({ profiles: {}, order: {} })),
listProfilesForProvider: vi.fn(() => []),
resolveAgentDir: vi.fn((_cfg: unknown, agentId: string) => `/tmp/agent-${agentId}`),
updateAuthProfileStoreWithLock: vi.fn(
async ({ updater }: { updater: (store: any) => boolean }) => {
const store = {
@@ -170,7 +171,7 @@ vi.mock("./command-config-resolution.js", () => ({
vi.mock("../agents/agent-scope.js", () => ({
resolveDefaultAgentId: () => "main",
resolveAgentDir: () => "/tmp/agent",
resolveAgentDir: mocks.resolveAgentDir,
resolveAgentConfig: () => ({}),
resolveAgentEffectiveModelPrimary: () => undefined,
resolveAgentModelFallbacksOverride: () => [],
@@ -401,6 +402,7 @@ describe("capability cli", () => {
.mockResolvedValue([{ id: "gpt-5.4", provider: "openai", name: "GPT-5.4" }] as never);
mocks.loadAuthProfileStoreForRuntime.mockReset().mockReturnValue({ profiles: {}, order: {} });
mocks.listProfilesForProvider.mockReset().mockReturnValue([]);
mocks.resolveAgentDir.mockClear();
mocks.getRuntimeConfigSourceSnapshot.mockReset().mockReturnValue(null);
mocks.setRuntimeConfigSnapshot.mockClear();
mocks.updateAuthProfileStoreWithLock
@@ -2176,6 +2178,37 @@ describe("capability cli", () => {
provider: "openai",
removedProfiles: ["openai:default", "openai:secondary"],
});
expect(mocks.updateAuthProfileStoreWithLock).toHaveBeenCalledWith(
expect.objectContaining({ agentDir: "/tmp/agent-main" }),
);
});
it("removes model auth profiles from the selected agent store", async () => {
mocks.listProfilesForProvider.mockReturnValue(["openai:default"] as never);
await runRegisteredCli({
register: registerCapabilityCli as (program: Command) => void,
argv: [
"capability",
"model",
"auth",
"logout",
"--provider",
"openai",
"--agent",
"poe",
"--json",
],
});
expect(mocks.loadAuthProfileStoreForRuntime).toHaveBeenCalledWith("/tmp/agent-poe");
expect(mocks.updateAuthProfileStoreWithLock).toHaveBeenCalledWith(
expect.objectContaining({ agentDir: "/tmp/agent-poe" }),
);
expect(mocks.runtime.writeJson).toHaveBeenCalledWith({
provider: "openai",
removedProfiles: ["openai:default"],
});
});
it("fails logout if the auth store update does not complete", async () => {

View File

@@ -920,9 +920,10 @@ async function runModelAuthStatus() {
return raw ? (JSON.parse(raw) as Record<string, unknown>) : {};
}
async function runModelAuthLogout(provider: string) {
async function runModelAuthLogout(provider: string, agent?: string) {
const cfg = getRuntimeConfig();
const agentDir = resolveAgentDir(cfg, resolveDefaultAgentId(cfg));
const agentId = agent?.trim() || resolveDefaultAgentId(cfg);
const agentDir = resolveAgentDir(cfg, agentId);
const store = loadAuthProfileStoreForRuntime(agentDir);
const profileIds = listProfilesForProvider(store, provider);
const updated = await updateAuthProfileStoreWithLock({
@@ -1882,10 +1883,14 @@ export function registerCapabilityCli(program: Command) {
.command("logout")
.description("Remove saved auth profiles for one provider")
.requiredOption("--provider <id>", "Provider id")
.option("--agent <id>", "Agent id (default: configured default agent)")
.option("--json", "Output JSON", false)
.action(async (opts) => {
await runCommandWithRuntime(defaultRuntime, async () => {
const result = await runModelAuthLogout(String(opts.provider));
const result = await runModelAuthLogout(
String(opts.provider),
typeof opts.agent === "string" ? opts.agent : undefined,
);
emitJsonOrText(defaultRuntime, Boolean(opts.json), result, (value) =>
JSON.stringify(value, null, 2),
);