import { readProducts } from "./reader.ts"; import { connectCache, disconnectCache } from "./cache.ts"; import { printResults, writeResultsToDb, writeResultsWorkbook, } from "./writer.ts"; import { initDb, closeDb } from "./database.ts"; import { chunkArray, processProductChunk, type SellabilityFilter, } from "./analysis-pipeline.ts"; import path from "node:path"; import type { AnalysisResult } from "./types.ts"; const DB_PATH = process.env.RESULTS_DB_PATH || path.join("db", "results.db"); const INPUT_BATCH_SIZE = 50; function parseSellabilityArg(args: string[]): SellabilityFilter { const sellabilityArg = args.find((a) => a.startsWith("--sellability=")); const sellabilityValueFromEquals = sellabilityArg?.split("=")[1]; const sellabilityIdx = args.indexOf("--sellability"); const sellabilityValueFromNext = sellabilityIdx !== -1 ? args[sellabilityIdx + 1] : undefined; const rawSellability = sellabilityValueFromEquals ?? sellabilityValueFromNext; if (!rawSellability) return "available"; const normalized = rawSellability.toLowerCase(); if (normalized === "available" || normalized === "all") { return normalized; } console.error( `Invalid --sellability value: \"${rawSellability}\". Use \"available\" or \"all\".`, ); process.exit(1); } function parseArgs(): { inputFile: string; outputFile?: string; sellability: SellabilityFilter; useClaude: boolean; } { const args = process.argv.slice(2); const outputFile = readFlagValue(args, "--out", "--output"); const useClaude = args.includes("--claude"); const inputFile = readInputFileArg( args, "--out", "--output", "--sellability", ); const sellability = parseSellabilityArg(args); if (!inputFile) { console.error( "Usage: bun run src/index.ts [--out results.xlsx|--output results.xlsx] [--sellability available|all] [--claude]", ); process.exit(1); } return { inputFile, outputFile, sellability, useClaude }; } function readFlagValue(args: string[], ...flags: string[]): string | undefined { for (const flag of flags) { const equalsArg = args.find((arg) => arg.startsWith(`${flag}=`)); if (equalsArg) { const value = equalsArg.slice(flag.length + 1); if (value) return value; } const flagIdx = args.indexOf(flag); if (flagIdx !== -1) { return args[flagIdx + 1]; } } return undefined; } function readInputFileArg( args: string[], ...flagsWithValues: string[] ): string | undefined { for (let i = 0; i < args.length; i++) { const arg = args[i]!; if (flagsWithValues.includes(arg)) { i++; continue; } if (flagsWithValues.some((flag) => arg.startsWith(`${flag}=`))) { continue; } if (!arg.startsWith("--")) { return arg; } } return undefined; } function resolveBaseOutputPath(inputFile: string, outputFile?: string): string { if (outputFile) return outputFile; const parsedInput = path.parse(inputFile); return path.join("output", `${parsedInput.name}_results.xlsx`); } async function main() { const { inputFile, outputFile, sellability, useClaude } = parseArgs(); console.log(`Sellability filter: ${sellability}`); console.log(`LLM provider: ${useClaude ? "claude" : "local"}`); console.log("Connecting to Redis..."); await connectCache(); console.log("Initializing SQLite database..."); initDb(DB_PATH); try { console.log(`\nReading ${inputFile}...`); const products = readProducts(inputFile); if (products.length === 0) { console.error("No valid products found in input file."); process.exit(1); } const productChunks = chunkArray(products, INPUT_BATCH_SIZE); const allResults: AnalysisResult[] = []; const resolvedBaseOutputPath = resolveBaseOutputPath(inputFile, outputFile); if (productChunks.length > 1) { console.log( `\nLarge input detected (${products.length} products). Processing in chunks of ${INPUT_BATCH_SIZE}.`, ); console.log(`Output base path: ${resolvedBaseOutputPath}`); } for (let chunkIndex = 0; chunkIndex < productChunks.length; chunkIndex++) { const chunk = productChunks[chunkIndex]!; console.log( `\n=== Input chunk ${chunkIndex + 1}/${productChunks.length} (${chunk.length} products) ===`, ); const chunkResults = await processProductChunk(chunk, { sellability, useClaude, }); allResults.push(...chunkResults); } printResults(allResults); writeResultsWorkbook(allResults, resolvedBaseOutputPath); writeResultsToDb(allResults, DB_PATH, inputFile, resolvedBaseOutputPath); } finally { await disconnectCache(); closeDb(); } } main().catch((err) => { console.error("Fatal error:", err); process.exit(1); });