dify/cli/src/commands/auth/logout/logout.ts
2026-06-15 02:24:51 +00:00

51 lines
1.7 KiB
TypeScript

import type { Registry } from '@/auth/hosts'
import type { HttpClient } from '@/http/types'
import type { TokenStore } from '@/store/token-store'
import type { IOStreams } from '@/sys/io/streams'
import { AccountSessionsClient } from '@/api/account-sessions'
import { getTokenStore } from '@/store/manager'
import { colorEnabled, colorScheme } from '@/sys/io/color'
export type LogoutOptions = {
readonly io: IOStreams
readonly reg: Registry
readonly http?: HttpClient
/** Optional override for tests; production resolves via `getTokenStore`. */
readonly store?: TokenStore
}
const REVOCABLE_PREFIXES = ['dfoa_', 'dfoe_'] as const
export async function runLogout(opts: LogoutOptions): Promise<void> {
const cs = colorScheme(colorEnabled(opts.io.isErrTTY))
const reg = opts.reg
const active = reg.requireActive()
const store = opts.store ?? getTokenStore(reg.token_storage)
let bearer = ''
try {
bearer = await store.read(active.host, active.email)
}
catch { /* keyring locked — skip remote revocation, local cleanup still runs */ }
let revokeWarning = ''
if (bearer !== '' && revokeAllowed(bearer) && opts.http !== undefined) {
try {
await new AccountSessionsClient(opts.http).revokeSelf()
}
catch (err) {
revokeWarning = `${cs.warningIcon()} server revoke failed (${(err as Error).message}); local credentials cleared anyway\n`
}
}
await reg.forget(active, store)
if (revokeWarning !== '')
opts.io.err.write(revokeWarning)
opts.io.out.write(`${cs.successIcon()} Logged out of ${active.host}\n`)
}
function revokeAllowed(bearer: string): boolean {
return REVOCABLE_PREFIXES.some(p => bearer.startsWith(p))
}