Skip to main content
Glama
temp-files.js6.29 kB
/** * Temporary File Management for Claude UI Images * Handles saving pasted images to temp files for faster uploads */ const fs = require('fs'); const path = require('path'); const os = require('os'); const { logger } = require('./logger'); class TempFileManager { constructor() { this.tempDir = path.join(os.tmpdir(), 'stockspark-images'); this.ensureTempDir(); } /** * Ensure temp directory exists */ ensureTempDir() { try { if (!fs.existsSync(this.tempDir)) { fs.mkdirSync(this.tempDir, { recursive: true }); logger.info('Created temp directory for images', { path: this.tempDir }); } } catch (error) { logger.error('Failed to create temp directory', { error: error.message }); // Fallback to OS temp dir this.tempDir = os.tmpdir(); } } /** * Get file extension from MIME type */ getExtensionFromMimeType(mimeType) { const mimeMap = { 'image/jpeg': 'jpg', 'image/jpg': 'jpg', 'image/png': 'png', 'image/gif': 'gif', 'image/webp': 'webp', 'image/bmp': 'bmp' }; return mimeMap[mimeType] || 'jpg'; } /** * Save base64 image data to temporary file */ saveImageToTempFile(base64Data, mimeType, filename = null) { try { // Clean base64 data const cleanBase64 = base64Data.replace(/^data:image\/(png|jpeg|jpg|gif|webp);base64,/, ''); // Generate filename const timestamp = Date.now(); const extension = this.getExtensionFromMimeType(mimeType); const tempFilename = filename || `claude_image_${timestamp}_${Math.random().toString(36).substr(2, 5)}.${extension}`; const filepath = path.join(this.tempDir, tempFilename); // Convert and save const buffer = Buffer.from(cleanBase64, 'base64'); fs.writeFileSync(filepath, buffer); logger.info('Saved image to temp file', { filepath, size: buffer.length, mimeType }); return { success: true, filepath, size: buffer.length, filename: tempFilename }; } catch (error) { logger.error('Failed to save image to temp file', { error: error.message, mimeType }); return { success: false, error: error.message }; } } /** * Save multiple images to temp files */ saveImagesToTempFiles(imageDataArray) { const results = []; const filepaths = []; for (let i = 0; i < imageDataArray.length; i++) { const imageData = imageDataArray[i]; const result = this.saveImageToTempFile( imageData.data, imageData.mimeType, imageData.filename ); results.push({ index: i, ...result }); if (result.success) { filepaths.push(result.filepath); } } return { results, filepaths, successCount: results.filter(r => r.success).length, totalCount: results.length }; } /** * Process Claude UI image format to temp files */ processClaudeImages(claudeImages) { const imageDataArray = claudeImages.map((img, index) => { if (!img.source || !img.source.data) { throw new Error(`Image ${index + 1}: Invalid Claude image format - missing source.data`); } return { data: img.source.data, mimeType: img.source.media_type || 'image/jpeg', filename: `claude_image_${index + 1}.${this.getExtensionFromMimeType(img.source.media_type || 'image/jpeg')}` }; }); return this.saveImagesToTempFiles(imageDataArray); } /** * Clean up temporary files */ cleanupTempFiles(filepaths) { const cleaned = []; const errors = []; filepaths.forEach(filepath => { try { // Safety check - only delete files in our temp directory if (filepath.startsWith(this.tempDir) && fs.existsSync(filepath)) { fs.unlinkSync(filepath); cleaned.push(filepath); logger.debug('Cleaned up temp file', { filepath }); } } catch (error) { errors.push({ filepath, error: error.message }); logger.warn('Failed to cleanup temp file', { filepath, error: error.message }); } }); return { cleaned, errors, cleanedCount: cleaned.length }; } /** * Clean up old temp files (older than 1 hour) */ cleanupOldTempFiles() { try { const files = fs.readdirSync(this.tempDir); const now = Date.now(); const oneHour = 60 * 60 * 1000; let cleanedCount = 0; files.forEach(filename => { if (filename.startsWith('claude_image_')) { const filepath = path.join(this.tempDir, filename); const stats = fs.statSync(filepath); if (now - stats.mtime.getTime() > oneHour) { fs.unlinkSync(filepath); cleanedCount++; } } }); if (cleanedCount > 0) { logger.info('Cleaned up old temp files', { count: cleanedCount }); } return cleanedCount; } catch (error) { logger.warn('Failed to cleanup old temp files', { error: error.message }); return 0; } } /** * Get temp directory info */ getTempDirInfo() { try { const files = fs.readdirSync(this.tempDir); const imageFiles = files.filter(f => f.startsWith('claude_image_')); let totalSize = 0; imageFiles.forEach(filename => { const filepath = path.join(this.tempDir, filename); const stats = fs.statSync(filepath); totalSize += stats.size; }); return { tempDir: this.tempDir, fileCount: imageFiles.length, totalSize, totalSizeMB: (totalSize / (1024 * 1024)).toFixed(2) }; } catch (error) { return { tempDir: this.tempDir, error: error.message }; } } } // Singleton instance const tempFileManager = new TempFileManager(); // Cleanup old files on startup tempFileManager.cleanupOldTempFiles(); module.exports = { TempFileManager, tempFileManager };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/loukach/stockspark-mcp-poc'

If you have feedback or need assistance with the MCP directory API, please join our Discord server