Compare commits

..
Author SHA1 Message Date
iris
9d58ec391b agent: logout confirm dialog accurate about --continue session loss (#576 argus nit)
argus #583 review caught: the prior dialog wording said 'prior
--continue context is not affected (only the OAuth creds)', but
that's false — paths::claude_dir() is /root/.claude and
remove_dir_all wipes the projects/<hash>/*.jsonl session history
along with the OAuth creds.

Updated both the overflow-menu confirm and the /logout slash
command confirm to explicitly say the projects/*.jsonl session
history (--continue context) goes too, plus a 'no undo' tail so
the operator can't read past the consequence list. SLASH_COMMANDS
desc + overflow-item title also corrected so /help + tooltips
match.
2026-05-29 16:56:47 +02:00
iris
259b236ad1 agent: 🔓 logout in overflow menu + /logout slash command (#576 frontend half)
Pairs with damocles PR #582 (POST /api/logout backend on the
per-agent web UI). Three additions to the agent's app.js:

- New `postLogout` helper next to postCancelTurn / postCompact /
  postNewSession. Same postSimple shape.
- New entry in SLASH_COMMANDS so /help lists /logout.
- New /logout case in handleSlashCommand with window.confirm.
- New '🔓 logout' item in populateOverflowMenu's overflow popover,
  mirroring the new-session item's pattern (confirm before POST,
  disable button while in-flight, closeOverflowMenu before fire).

Confirm dialog spells out the consequences (SIGINT, creds wiped,
park in needs-login) so the operator doesn't accidentally drop a
production session. Tooltip on the menu item links the action back
to the credentials directory + post-logout state.

Wire-level: POST /api/logout, no body. Backend returns 200 with a
text body describing the wipe outcome — postSimple ignores it
(success → no terminal note; failure → red turn-end-fail row).
2026-05-29 16:56:47 +02:00

View file

@ -218,6 +218,36 @@ window.marked = marked;
});
menu.append(newSessBtn);
// 🔓 logout (#576) — SIGINTs claude, wipes the credentials dir,
// flips the harness LoginState to NeedsLogin. The turn loop's
// next iteration parks in wait_for_login; a fresh `claude auth
// login` from the dashboard re-arms it (#542 mtime resumption).
// Destructive — the operator has to re-paste OAuth creds on the
// login screen after — so we confirm with a clear-eyed prompt.
const logoutBtn = el('button', {
type: 'button',
class: 'overflow-item overflow-item-logout',
role: 'menuitem',
id: 'logout-btn',
title: 'wipe ~/.claude/ entirely (OAuth creds + projects/*.jsonl --continue history) + park in needs-login until a fresh `claude auth login` runs',
},
el('span', { class: 'overflow-item-icon', 'aria-hidden': 'true' }, '🔓'),
'logout',
);
logoutBtn.addEventListener('click', () => {
if (!window.confirm(
`log ${label} out? this SIGINTs any running claude turn, then WIPES the entire ` +
`~/.claude/ directory — that includes OAuth credentials AND the projects/*.jsonl ` +
`session history (--continue context). the agent then parks in 'needs login' until ` +
`you paste a fresh OAuth code from the login screen. all prior conversation state ` +
`for this agent is lost. no undo.`
)) return;
logoutBtn.disabled = true;
closeOverflowMenu();
postLogout().finally(() => { logoutBtn.disabled = false; });
});
menu.append(logoutBtn);
overflowMenuPopulated = true;
}
@ -363,6 +393,7 @@ window.marked = marked;
{ name: '/compact', desc: 'compact the persistent claude session' },
{ name: '/model', desc: '/model <name> — switch claude model for future turns' },
{ name: '/new-session', desc: 'next turn runs without --continue (fresh claude session)' },
{ name: '/logout', desc: 'wipe ~/.claude/ (creds + --continue history) + park in needs-login' },
];
async function postModel(name) {
@ -402,6 +433,7 @@ window.marked = marked;
const postCancelTurn = () => postSimple('/api/cancel', '/cancel');
const postCompact = () => postSimple('/api/compact', '/compact');
const postNewSession = () => postSimple('/api/new-session', '/new-session');
const postLogout = () => postSimple('/api/logout', '/logout');
function handleSlashCommand(line) {
if (!termAPI) return false;
@ -430,6 +462,17 @@ window.marked = marked;
postNewSession();
}
return true;
case '/logout':
if (window.confirm(
`log out? this SIGINTs any running claude turn, then WIPES the entire ` +
`~/.claude/ directory — that includes OAuth credentials AND the projects/*.jsonl ` +
`session history (--continue context). the agent then parks in 'needs login' until ` +
`you paste a fresh OAuth code from the login screen. all prior conversation state ` +
`for this agent is lost. no undo.`
)) {
postLogout();
}
return true;
case '/model': {
const parts = trimmed.split(/\s+/);
if (parts.length < 2 || !parts[1]) {