tidyai-ts
Version:
AI-powered cross-platform file organizer using OpenRouter API
196 lines (192 loc) • 8.9 kB
JavaScript
;
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;