UNPKG

tidyai-ts

Version:

AI-powered cross-platform file organizer using OpenRouter API

196 lines (192 loc) 8.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenRouterClient = void 0; const https = __importStar(require("https")); const url_1 = require("url"); const cli_utils_1 = require("./cli-utils"); // Try to get API key from environment, with better error handling const OPENROUTER_API_KEY = process.env.TIDYAI_API_KEY || process.env.OPENROUTER_API_KEY || ""; const OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions"; const DEFAULT_MODEL = "openai/gpt-oss-20b:free"; const IS_DEVELOPMENT = process.env.NODE_ENV === "development"; class OpenRouterClient { constructor() { this.apiKey = OPENROUTER_API_KEY; } async getSuggestions(fileNames) { // If no API key is set, use default suggestions if (!this.apiKey) { (0, cli_utils_1.displayWarning)("TIDYAI_API_KEY not set, using default folder suggestions"); // Create a log entry await (0, cli_utils_1.createLogEntry)(process.cwd(), "TIDYAI_API_KEY not set, using default folder suggestions", ".tidyai/logs"); return this.getDefaultSuggestions(fileNames); } (0, cli_utils_1.displayInfo)("Sending request to OpenRouter API..."); // Prepare the prompt for the AI const fileList = fileNames.join("\n"); const prompt = `You are an AI assistant that helps organize files. Based on the filenames provided, suggest appropriate folder names to organize them. File names: ${fileList} For each file, suggest one logical folder name based on its content or type. Respond ONLY with a JSON object where keys are the filenames and values are the suggested folder names. Do not include any other text in your response. Example response format: { "document.pdf": "Documents", "photo.jpg": "Images", "script.js": "Scripts" }`; try { const response = await this.makeRequest({ model: DEFAULT_MODEL, messages: [{ role: "user", content: prompt }], }); // Check if the response has the expected structure if (!response || !response.choices || response.choices.length === 0) { (0, cli_utils_1.displayWarning)("Unexpected API response structure, using default folder suggestions"); // Create a log entry await (0, cli_utils_1.createLogEntry)(process.cwd(), "Unexpected API response structure, using default folder suggestions", ".tidyai/logs"); return this.getDefaultSuggestions(fileNames); } const content = response.choices[0].message.content; // Parse the JSON response return JSON.parse(content); } catch (error) { (0, cli_utils_1.displayWarning)(`Error getting suggestions from OpenRouter API: ${error.message}`); // Create a log entry await (0, cli_utils_1.createLogEntry)(process.cwd(), `Error getting suggestions from OpenRouter API: ${error.message}`, ".tidyai/logs"); // Return default folder names if API fails return this.getDefaultSuggestions(fileNames); } } makeRequest(data) { return new Promise((resolve, reject) => { const postData = JSON.stringify(data); const url = new url_1.URL(OPENROUTER_API_URL); // Log request in development mode if (IS_DEVELOPMENT) { console.log("\n=== OPENROUTER API REQUEST ==="); console.log("URL:", OPENROUTER_API_URL); console.log("Method: POST"); console.log("Headers:", { "Authorization": "Bearer [REDACTED]", "Content-Type": "application/json", "Content-Length": Buffer.byteLength(postData), }); console.log("Body:", JSON.stringify(data, null, 2)); console.log("===============================\n"); } const options = { hostname: url.hostname, port: 443, path: url.pathname, method: "POST", headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", "Content-Length": Buffer.byteLength(postData), }, }; const req = https.request(options, (res) => { let responseBody = ""; res.on("data", (chunk) => { responseBody += chunk; }); res.on("end", () => { try { // Log response in development mode if (IS_DEVELOPMENT) { console.log("\n=== OPENROUTER API RESPONSE ==="); console.log("Status Code:", res.statusCode); console.log("Headers:", res.headers); console.log("Body:", responseBody); console.log("================================\n"); } // Check if the response status indicates an error if (res.statusCode && res.statusCode >= 400) { (0, cli_utils_1.displayWarning)(`API request failed with status ${res.statusCode}: ${responseBody}`); reject(new Error(`API request failed with status ${res.statusCode}`)); return; } const response = JSON.parse(responseBody); resolve(response); } catch (error) { (0, cli_utils_1.displayWarning)(`Error parsing API response: ${responseBody}`); reject(error); } }); }); req.on("error", (error) => { // Log error in development mode if (IS_DEVELOPMENT) { console.log("\n=== OPENROUTER API ERROR ==="); console.log("Error:", error); console.log("============================\n"); } reject(error); }); req.write(postData); req.end(); }); } getDefaultSuggestions(fileNames) { const suggestions = {}; for (const fileName of fileNames) { // Simple default logic based on file extensions const ext = fileName.split(".").pop()?.toLowerCase() || ""; if (["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(ext)) { suggestions[fileName] = "Images"; } else if (["pdf", "doc", "docx", "txt", "rtf"].includes(ext)) { suggestions[fileName] = "Documents"; } else if (["mp4", "avi", "mov", "wmv", "flv"].includes(ext)) { suggestions[fileName] = "Videos"; } else if (["mp3", "wav", "ogg", "flac"].includes(ext)) { suggestions[fileName] = "Audio"; } else if (["js", "ts", "jsx", "tsx", "html", "css", "scss"].includes(ext)) { suggestions[fileName] = "Code"; } else { suggestions[fileName] = "Other"; } } return suggestions; } } exports.OpenRouterClient = OpenRouterClient;