source dump of claude code
at main 104 lines 3.0 kB view raw
1import { createHash } from 'crypto' 2import { mkdir, readdir, readFile, stat, unlink, writeFile } from 'fs/promises' 3import { join } from 'path' 4import { logForDebugging } from './debug.js' 5import { getClaudeConfigHomeDir } from './envUtils.js' 6import { isENOENT } from './errors.js' 7 8const PASTE_STORE_DIR = 'paste-cache' 9 10/** 11 * Get the paste store directory (persistent across sessions). 12 */ 13function getPasteStoreDir(): string { 14 return join(getClaudeConfigHomeDir(), PASTE_STORE_DIR) 15} 16 17/** 18 * Generate a hash for paste content to use as filename. 19 * Exported so callers can get the hash synchronously before async storage. 20 */ 21export function hashPastedText(content: string): string { 22 return createHash('sha256').update(content).digest('hex').slice(0, 16) 23} 24 25/** 26 * Get the file path for a paste by its content hash. 27 */ 28function getPastePath(hash: string): string { 29 return join(getPasteStoreDir(), `${hash}.txt`) 30} 31 32/** 33 * Store pasted text content to disk. 34 * The hash should be pre-computed with hashPastedText() so the caller 35 * can use it immediately without waiting for the async disk write. 36 */ 37export async function storePastedText( 38 hash: string, 39 content: string, 40): Promise<void> { 41 try { 42 const dir = getPasteStoreDir() 43 await mkdir(dir, { recursive: true }) 44 45 const pastePath = getPastePath(hash) 46 47 // Content-addressable: same hash = same content, so overwriting is safe 48 await writeFile(pastePath, content, { encoding: 'utf8', mode: 0o600 }) 49 logForDebugging(`Stored paste ${hash} to ${pastePath}`) 50 } catch (error) { 51 logForDebugging(`Failed to store paste: ${error}`) 52 } 53} 54 55/** 56 * Retrieve pasted text content by its hash. 57 * Returns null if not found or on error. 58 */ 59export async function retrievePastedText(hash: string): Promise<string | null> { 60 try { 61 const pastePath = getPastePath(hash) 62 return await readFile(pastePath, { encoding: 'utf8' }) 63 } catch (error) { 64 // ENOENT is expected when paste doesn't exist 65 if (!isENOENT(error)) { 66 logForDebugging(`Failed to retrieve paste ${hash}: ${error}`) 67 } 68 return null 69 } 70} 71 72/** 73 * Clean up old paste files that are no longer referenced. 74 * This is a simple time-based cleanup - removes files older than cutoffDate. 75 */ 76export async function cleanupOldPastes(cutoffDate: Date): Promise<void> { 77 const pasteDir = getPasteStoreDir() 78 79 let files 80 try { 81 files = await readdir(pasteDir) 82 } catch { 83 // Directory doesn't exist or can't be read - nothing to clean up 84 return 85 } 86 87 const cutoffTime = cutoffDate.getTime() 88 for (const file of files) { 89 if (!file.endsWith('.txt')) { 90 continue 91 } 92 93 const filePath = join(pasteDir, file) 94 try { 95 const stats = await stat(filePath) 96 if (stats.mtimeMs < cutoffTime) { 97 await unlink(filePath) 98 logForDebugging(`Cleaned up old paste: ${filePath}`) 99 } 100 } catch { 101 // Ignore errors for individual files 102 } 103 } 104}