"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;
    };
})();
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginBase = exports.ModulesService = exports.ai = exports.processors = exports.types = exports.embeddings = exports.services = exports.agents = exports.clients = void 0;
exports.embed = embed;
exports.purge = purge;
exports.upload = upload;
exports.generate = generate;
exports.handleMultiOutputGeneration = handleMultiOutputGeneration;
exports.handleSingleOutputGeneration = handleSingleOutputGeneration;
exports.download = download;
const ai_1 = require("./ai");
const hashes_1 = require("./hashes");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const glob_1 = require("glob");
const types_1 = require("./types");
const utils_1 = require("./utils");
const config_1 = require("./config");
const embeddings_1 = require("./embeddings");
const conversion_1 = require("./conversion");
const services_1 = require("./services/");
exports.clients = __importStar(require("./clients"));
exports.agents = __importStar(require("./agents"));
exports.services = __importStar(require("./services"));
exports.embeddings = __importStar(require("./embeddings"));
exports.types = __importStar(require("./types"));
exports.processors = __importStar(require("./processors"));
exports.ai = __importStar(require("./ai"));
__exportStar(require("./services/modules/types"), exports);
var modules_1 = require("./services/modules");
Object.defineProperty(exports, "ModulesService", { enumerable: true, get: function () { return modules_1.ModulesService; } });
var PluginBase_1 = require("./plugins/PluginBase");
Object.defineProperty(exports, "PluginBase", { enumerable: true, get: function () { return PluginBase_1.PluginBase; } });
async function embed() {
    const config = await (0, config_1.getConfig)();
    const ignorePattern = await (0, config_1.getIgnorePattern)();
    const defaultModel = config.embeddingModel || types_1.EmbeddingModels.openai.EmbeddingAda2;
    if (!config.embedSources) {
        return;
    }
    for (const source of config.embedSources) {
        await (0, embeddings_1.embedSource)(defaultModel, source, ignorePattern);
    }
}
async function purge(globPath) {
    const files = (0, glob_1.globSync)(globPath);
    const embeddings = await (0, embeddings_1.getConfiguredEmbeddingMap)();
    const config = await (0, config_1.getConfig)();
    const chunkSizes = config.embedSources.reduce((acc, source) => {
        acc[source.output] = source.chunkSize;
        return acc;
    }, {});
    for (const file of Object.keys(embeddings)) {
        let pruned = embeddings[file];
        for (const filePath of files) {
            const before = pruned.length;
            pruned = pruned
                .filter((e) => !filePath || !e.id.startsWith("./" + filePath))
                .filter((e) => e.text.length <= chunkSizes[file]);
            const after = pruned.length;
            if (after < before) {
                console.log("Purging", filePath);
            }
        }
        await (0, embeddings_1.saveEmbedding)(file, pruned);
    }
}
async function upload() {
    const config = await (0, config_1.getConfig)();
    const { AwsS3, Embeddings, knowhowApiClient } = (0, services_1.services)();
    for (const source of config.embedSources) {
        const bucketName = source.remote;
        if (!source.remoteType) {
            console.log("Skipping", source.output, "because no remoteType is configured");
            continue;
        }
        const items = JSON.parse(await (0, utils_1.readFile)(source.output, "utf8"));
        const { name: embeddingName } = path.parse(source.output);
        const data = {
            embeddingName,
            items,
        };
        if (Embeddings.hasResolver(source.remoteType) && source.remoteType !== "knowhow") {
            console.log("Uploading", source.output, "to", `${bucketName}/${embeddingName}.json`);
            const remoteKey = `${embeddingName}.json`;
            await Embeddings.upload(source.remoteType, source.output, bucketName, remoteKey);
        }
        else if (source.remoteType === "knowhow") {
            if (!source.remoteId) {
                throw new Error("remoteId is required for knowhow uploads");
            }
            try {
                const remoteEmbedding = await knowhowApiClient.getOrgEmbedding(source.remoteId);
                const localModel = config.embeddingModel || types_1.EmbeddingModels.openai.EmbeddingAda2;
                const remoteModel = remoteEmbedding?.modelName;
                if (remoteModel && remoteModel !== localModel) {
                    console.warn(`⚠️  WARNING: Embedding model mismatch for "${remoteEmbedding.name}" (remoteId: ${source.remoteId}).\n` +
                        `   Local config.embeddingModel:  ${localModel}\n` +
                        `   Backend embedding modelName:  ${remoteModel}\n` +
                        `   Vectors generated with different models are not comparable — search results will be incorrect.\n` +
                        `   Update your config.embeddingModel to "${remoteModel}" or update the backend embedding to "${localModel}".`);
                }
            }
            catch (e) {
            }
            const url = await knowhowApiClient.getPresignedUploadUrl(source);
            console.log("Uploading to", url);
            await AwsS3.uploadToPresignedUrl(url, source.output);
            await knowhowApiClient.updateEmbeddingMetadata(source.remoteId, {
                inputGlob: source.input,
                outputPath: source.output,
                chunkSize: source.chunkSize,
                remoteType: source.remoteType,
            });
            console.log("Synced metadata for", source.remoteId);
        }
        else {
            console.log("Skipping upload to", source.remoteType, "for", source.remote);
        }
    }
}
function normalizeInputPattern(input) {
    if (input.includes("{") || input.includes("*") || input.includes("?")) {
        return input;
    }
    if (input.includes(",")) {
        const parts = input.split(",").map((p) => p.trim());
        return `{${parts.join(",")}}`;
    }
    return input;
}
async function generate() {
    const config = await (0, config_1.getConfig)();
    for (const source of config.sources) {
        console.log("Generating", source.input, "to", source.output);
        if (source.kind === "file" || !source.kind) {
            const files = (0, glob_1.globSync)(normalizeInputPattern(source.input));
            const prompt = await (0, config_1.loadPrompt)(source.prompt);
            if (source.output.endsWith("/")) {
                await handleMultiOutputGeneration(source.model, source.input, files, prompt, source.output, source.outputExt, source.outputName, source.kind, source.agent);
            }
            else {
                await handleSingleOutputGeneration(source.model, files, prompt, source.output, source.kind, source.agent);
            }
        }
        else {
            await handleAllKindsGeneration(source);
        }
    }
}
async function handleAllKindsGeneration(source) {
    const { Plugins } = (0, services_1.services)();
    const { kind, input } = source;
    if (Plugins.isPlugin(kind)) {
        const data = await Plugins.call(kind, input);
        if (source.output.endsWith("/")) {
            throw new Error(`Plugin ${kind} can only output to a single file`);
        }
        await (0, utils_1.writeFile)(source.output, data);
    }
    return handleFileKindGeneration(source);
}
async function handleFileKindGeneration(source) {
    const prompt = await (0, config_1.loadPrompt)(source.prompt);
    const files = (0, glob_1.globSync)(normalizeInputPattern(source.input));
    console.log("Analyzing files: ", files);
    if (source.output.endsWith("/")) {
        await handleMultiOutputGeneration(source.model, source.input, files, prompt, source.output, source.outputExt, source.outputName, source.kind, source.agent);
    }
    else {
        await handleSingleOutputGeneration(source.model, files, prompt, source.output, source.kind, source.agent);
    }
}
async function handleMultiOutputGeneration(model, inputPattern, files, prompt, output, outputExt = "mdx", outputName, kind, agent) {
    const promptHash = crypto.createHash("md5").update(prompt).digest("hex");
    const hashes = await (0, hashes_1.getHashes)();
    const inputPath = inputPattern.includes("**")
        ? inputPattern.split("**")[0]
        : "";
    for (const file of files) {
        const fileContent = await (0, conversion_1.convertToText)(file);
        const fileHash = crypto.createHash("md5").update(fileContent).digest("hex");
        if (!hashes[file]) {
            hashes[file] = { promptHash: "", fileHash: "" };
        }
        const { name, ext, dir } = path.parse(file);
        const nestedFolder = inputPath ? (dir + "/").replace(inputPath, "") : "";
        const outputFolder = path.join(output, nestedFolder);
        if (!fs.existsSync(outputFolder)) {
            fs.mkdirSync(outputFolder, { recursive: true });
        }
        const outputFileName = outputName || name;
        const outputFile = path.join(outputFolder, outputFileName + "." + outputExt);
        console.log({ dir, inputPath, nestedFolder, outputFile });
        const toCheck = [file, outputFile];
        const noChanges = await (0, hashes_1.checkNoFilesChanged)(toCheck, promptHash, hashes);
        if (noChanges) {
            console.log("Skipping file", file, "because it hasn't changed");
            continue;
        }
        console.log("Summarizing", file);
        const summary = prompt
            ? await (0, ai_1.summarizeFile)(file, prompt, model, agent)
            : fileContent;
        console.log("Writing summary to", outputFile);
        await (0, utils_1.writeFile)(outputFile, summary);
        await (0, hashes_1.saveAllFileHashes)(toCheck, promptHash);
    }
}
async function handleSingleOutputGeneration(model, files, prompt, outputFile, kind, agent) {
    const hashes = await (0, hashes_1.getHashes)();
    const promptHash = crypto.createHash("md5").update(prompt).digest("hex");
    const filesToCheck = [outputFile, ...files];
    const noChanges = await (0, hashes_1.checkNoFilesChanged)(filesToCheck, promptHash, hashes);
    if (noChanges) {
        console.log(`Skipping ${files.length} files because they haven't changed`);
        return;
    }
    console.log("Summarizing", files.length, "files");
    const summary = prompt
        ? await (0, ai_1.summarizeFiles)(files, prompt, model, agent)
        : (await Promise.all(files.map(conversion_1.convertToText))).join("\n\n");
    const fileHash = crypto.createHash("md5").update(summary).digest("hex");
    console.log("Writing summary to", outputFile);
    await (0, utils_1.writeFile)(outputFile, summary);
    await (0, hashes_1.saveAllFileHashes)(filesToCheck, promptHash);
}
async function download() {
    const config = await (0, config_1.getConfig)();
    const { AwsS3, Embeddings, knowhowApiClient } = (0, services_1.services)();
    for (const source of config.embedSources) {
        const { remote, remoteType } = source;
        if (!remoteType) {
            console.log("Skipping", source.output, "because no remoteType is configured");
            continue;
        }
        const { name } = path.parse(source.output);
        const fileName = `${name}.json`;
        const destinationPath = source.output;
        if (Embeddings.hasResolver(remoteType)) {
            console.log("Downloading", fileName, `from ${remoteType}`, remote, "to", destinationPath);
            const embeddingPath = ".knowhow/embeddings/" + fileName;
            await Embeddings.download(remoteType, remote, embeddingPath, destinationPath);
        }
        else if (remoteType === "knowhow") {
            if (!source.remoteId) {
                throw new Error("remoteId is required for knowhow downloads");
            }
            console.log("Downloading", fileName, "from Knowhow", "to", destinationPath);
            const preSignedUrl = await knowhowApiClient.getPresignedDownloadUrl(source);
            const outputDir = path.dirname(destinationPath);
            if (!fs.existsSync(outputDir))
                fs.mkdirSync(outputDir, { recursive: true });
            await AwsS3.downloadFromPresignedUrl(preSignedUrl, destinationPath);
        }
        else {
            console.log("Unsupported remote type for", source.output);
        }
    }
}
//# sourceMappingURL=index.js.map