Skip to content

Commit 5ed7964

Browse files
committed
Clear session auth state on logout
1 parent e81db9b commit 5ed7964

3 files changed

Lines changed: 57 additions & 2 deletions

File tree

src/CodexAcpServer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ export class CodexAcpServer {
162162
this.availableCommands = new CodexCommands(
163163
connection,
164164
codexAcpClient,
165-
(operation) => this.runWithProcessCheck(operation)
165+
(operation) => this.runWithProcessCheck(operation),
166+
() => this.markSessionsLoggedOut()
166167
);
167168
}
168169

src/CodexCommands.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,24 @@ export type CommandHandleOptions = {
2121
onTurnStarted?: (turnId: string, threadId: string) => void;
2222
};
2323

24+
export type LogoutHandler = () => void;
25+
2426
export class CodexCommands {
2527
private readonly connection: AcpClientConnection;
2628
private readonly codexAcpClient: CodexAcpClient;
2729
private readonly runWithProcessCheck: <T>(operation: () => Promise<T>) => Promise<T>;
30+
private readonly onLogout: LogoutHandler;
2831

2932
constructor(
3033
connection: AcpClientConnection,
3134
codexAcpClient: CodexAcpClient,
32-
runWithProcessCheck: <T>(operation: () => Promise<T>) => Promise<T>
35+
runWithProcessCheck: <T>(operation: () => Promise<T>) => Promise<T>,
36+
onLogout: LogoutHandler = () => {}
3337
) {
3438
this.connection = connection;
3539
this.codexAcpClient = codexAcpClient;
3640
this.runWithProcessCheck = runWithProcessCheck;
41+
this.onLogout = onLogout;
3742
}
3843

3944
async publish(sessionId: string): Promise<void> {
@@ -198,6 +203,7 @@ export class CodexCommands {
198203
}
199204
case "logout": {
200205
await this.runWithProcessCheck(() => this.codexAcpClient.logout());
206+
this.onLogout();
201207
const session = new ACPSessionConnection(this.connection, sessionId);
202208
await session.update({
203209
sessionUpdate: "agent_message_chunk",

src/__tests__/CodexACPAgent/CodexAcpClient.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,54 @@ describe('ACP server test', { timeout: 40_000 }, () => {
14191419
await expect(fixture.getAcpConnectionDump(["sessionId"])).toMatchFileSnapshot("data/command-logout.json");
14201420
});
14211421

1422+
it('clears active session auth state when logout command signs out', async () => {
1423+
const mockFixture = createCodexMockTestFixture();
1424+
const codexAcpAgent = mockFixture.getCodexAcpAgent();
1425+
const codexAcpClient = mockFixture.getCodexAcpClient();
1426+
const model = createTestModel();
1427+
const currentModelId = ModelId.create(model.id, model.defaultReasoningEffort).toString();
1428+
1429+
vi.spyOn(codexAcpClient, "authRequired").mockResolvedValue(false);
1430+
vi.spyOn(codexAcpClient, "getAccount").mockResolvedValue({
1431+
account: { type: "apiKey" },
1432+
requiresOpenaiAuth: false,
1433+
});
1434+
vi.spyOn(codexAcpClient, "newSession")
1435+
.mockResolvedValueOnce({
1436+
sessionId: "session-1",
1437+
currentModelId,
1438+
models: [model],
1439+
additionalDirectories: [],
1440+
})
1441+
.mockResolvedValueOnce({
1442+
sessionId: "session-2",
1443+
currentModelId,
1444+
models: [model],
1445+
additionalDirectories: [],
1446+
});
1447+
const logoutSpy = vi.spyOn(codexAcpClient, "logout").mockResolvedValue();
1448+
1449+
const session1 = await codexAcpAgent.newSession({cwd: "/workspace", mcpServers: []});
1450+
const session2 = await codexAcpAgent.newSession({cwd: "/workspace", mcpServers: []});
1451+
expect(codexAcpAgent.getSessionState(session1.sessionId).authConfigured).toBe(true);
1452+
expect(codexAcpAgent.getSessionState(session2.sessionId).authConfigured).toBe(true);
1453+
1454+
await codexAcpAgent.prompt({
1455+
sessionId: session1.sessionId,
1456+
prompt: [{ type: "text", text: "/logout" }],
1457+
});
1458+
1459+
expect(logoutSpy).toHaveBeenCalledOnce();
1460+
expect(codexAcpAgent.getSessionState(session1.sessionId)).toMatchObject({
1461+
account: null,
1462+
authConfigured: false,
1463+
});
1464+
expect(codexAcpAgent.getSessionState(session2.sessionId)).toMatchObject({
1465+
account: null,
1466+
authConfigured: false,
1467+
});
1468+
});
1469+
14221470
it('handles skills command', async () => {
14231471
const codexAcpAgent = fixture.getCodexAcpAgent();
14241472
await codexAcpAgent.initialize({protocolVersion: 1});

0 commit comments

Comments
 (0)