WIP: A simple cli for daily tangled use cases and AI integration. This is for my personal use right now, but happy if others get mileage from it! :)
at pull-request-format-testing 96 lines 2.9 kB view raw
1import type { AtpSessionData } from '@atproto/api'; 2import { AsyncEntry } from '@napi-rs/keyring'; 3 4const SERVICE_NAME = 'tangled-cli'; 5 6export interface SessionMetadata { 7 handle: string; 8 did: string; 9 pds: string; 10 lastUsed: string; // ISO timestamp 11} 12 13/** 14 * Store session data in OS keychain 15 * @param sessionData - Session data from AtpAgent 16 */ 17export async function saveSession(sessionData: AtpSessionData): Promise<void> { 18 try { 19 const accountId = sessionData.did || sessionData.handle; 20 if (!accountId) { 21 throw new Error('Session data must include DID or handle'); 22 } 23 24 const serialized = JSON.stringify(sessionData); 25 const entry = new AsyncEntry(SERVICE_NAME, accountId); 26 await entry.setPassword(serialized); 27 } catch (error) { 28 throw new Error( 29 `Failed to save session to keychain: ${error instanceof Error ? error.message : 'Unknown error'}` 30 ); 31 } 32} 33 34/** 35 * Retrieve session data from OS keychain 36 * @param accountId - User's DID or handle 37 */ 38export async function loadSession(accountId: string): Promise<AtpSessionData | null> { 39 try { 40 const entry = new AsyncEntry(SERVICE_NAME, accountId); 41 const serialized = await entry.getPassword(); 42 if (!serialized) { 43 return null; 44 } 45 return JSON.parse(serialized) as AtpSessionData; 46 } catch (error) { 47 throw new Error( 48 `Failed to load session from keychain: ${error instanceof Error ? error.message : 'Unknown error'}` 49 ); 50 } 51} 52 53/** 54 * Delete session from OS keychain 55 * @param accountId - User's DID or handle 56 */ 57export async function deleteSession(accountId: string): Promise<boolean> { 58 try { 59 const entry = new AsyncEntry(SERVICE_NAME, accountId); 60 return await entry.deleteCredential(); 61 } catch (error) { 62 throw new Error( 63 `Failed to delete session from keychain: ${error instanceof Error ? error.message : 'Unknown error'}` 64 ); 65 } 66} 67 68/** 69 * Store metadata about current session for CLI to track active user 70 * Uses a special "current" account in keychain 71 */ 72export async function saveCurrentSessionMetadata(metadata: SessionMetadata): Promise<void> { 73 const serialized = JSON.stringify(metadata); 74 const entry = new AsyncEntry(SERVICE_NAME, 'current-session-metadata'); 75 await entry.setPassword(serialized); 76} 77 78/** 79 * Get metadata about current active session 80 */ 81export async function getCurrentSessionMetadata(): Promise<SessionMetadata | null> { 82 const entry = new AsyncEntry(SERVICE_NAME, 'current-session-metadata'); 83 const serialized = await entry.getPassword(); 84 if (!serialized) { 85 return null; 86 } 87 return JSON.parse(serialized) as SessionMetadata; 88} 89 90/** 91 * Clear current session metadata 92 */ 93export async function clearCurrentSessionMetadata(): Promise<void> { 94 const entry = new AsyncEntry(SERVICE_NAME, 'current-session-metadata'); 95 await entry.deleteCredential(); 96}