UNPKG

@wityai/root2-cli

Version:

Command-line interface for Root2 vector memory layer

423 lines • 15.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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.importDirCommand = importDirCommand; const chalk_1 = __importDefault(require("chalk")); const inquirer_1 = __importDefault(require("inquirer")); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const glob_1 = require("glob"); const root2_api_client_1 = require("@wityai/root2-api-client"); const config_1 = require("../utils/config"); const formatting_1 = require("../utils/formatting"); const spinner_1 = require("../utils/spinner"); const memory_blocks_1 = require("./memory-blocks"); function importDirCommand(program) { program .command("import-dir") .alias("id") .description("Import a directory and its contents into Root2 knowledge base") .argument("<dirPath>", "Path to the directory to import") .option("--memory-block <id>", "Memory block ID to import into") .option("-r, --recursive", "Include subdirectories recursively", false) .option("--include <pattern>", 'File pattern to include (e.g., "*.{pdf,txt,md}")') .option("--exclude <pattern>", "File pattern to exclude") .option("--max-files <number>", "Maximum number of files to import", "100") .option("--metadata <metadata>", "JSON string of metadata to attach to all files") .option("--dry-run", "Preview what would be imported without actually importing") .option("-v, --verbose", "Show detailed progress information") .action(async (dirPath, options) => { try { const config = await (0, config_1.validateConfig)(); const apiClient = new root2_api_client_1.Root2Api({ clientId: config.clientId, apiKey: config.apiKey, apiHost: config.apiHost, enableLogging: config.verbose || options.verbose || false, }); await importDirectory(apiClient, dirPath, options); } catch (error) { console.error(formatting_1.format.error(error instanceof Error ? error.message : "Unknown error")); process.exit(1); } }); program .command("import-dir-interactive") .alias("idi") .description("Import a directory with interactive prompts") .action(async () => { try { const config = await (0, config_1.validateConfig)(); const apiClient = new root2_api_client_1.Root2Api({ clientId: config.clientId, apiKey: config.apiKey, apiHost: config.apiHost, enableLogging: config.verbose || false, }); await interactiveDirImport(apiClient); } catch (error) { console.error(formatting_1.format.error(error instanceof Error ? error.message : "Unknown error")); process.exit(1); } }); } async function importDirectory(apiClient, dirPath, options) { let memoryBlockId; if (options.memoryBlock) { if (!(0, memory_blocks_1.isValidMemoryBlockId)(options.memoryBlock)) { throw new Error(`Invalid memory block ID format: ${options.memoryBlock}`); } memoryBlockId = options.memoryBlock; } else { memoryBlockId = await (0, memory_blocks_1.selectMemoryBlock)(apiClient); } const resolvedPath = path.resolve(dirPath); if (!(await fs.pathExists(resolvedPath))) { throw new Error(`Directory not found: ${dirPath}`); } const stats = await fs.stat(resolvedPath); if (!stats.isDirectory()) { throw new Error(`Path is not a directory: ${dirPath}`); } let metadata; if (options.metadata) { try { metadata = JSON.parse(options.metadata); } catch (error) { throw new Error("Invalid metadata JSON format"); } } console.log(chalk_1.default.cyan("\nšŸ“ Scanning directory...")); const files = await findFilesToImport(resolvedPath, { recursive: options.recursive, includePattern: options.include, excludePattern: options.exclude, maxFiles: parseInt(options.maxFiles), }); if (files.length === 0) { console.log(formatting_1.format.warning("No files found matching the criteria.")); return; } console.log(chalk_1.default.cyan("\nšŸ“‹ Import Summary:")); console.log(` ${chalk_1.default.blue("Directory")}: ${formatting_1.format.filePath(resolvedPath)}`); console.log(` ${chalk_1.default.blue("Files Found")}: ${files.length}`); console.log(` ${chalk_1.default.blue("Recursive")}: ${options.recursive ? "Yes" : "No"}`); if (options.include) { console.log(` ${chalk_1.default.blue("Include Pattern")}: ${options.include}`); } if (options.exclude) { console.log(` ${chalk_1.default.blue("Exclude Pattern")}: ${options.exclude}`); } if (metadata) { console.log(` ${chalk_1.default.blue("Metadata")}: ${JSON.stringify(metadata, null, 2)}`); } const fileTypes = getFileTypeBreakdown(files); if (Object.keys(fileTypes).length > 0) { console.log("\nšŸ“Š File Types:"); Object.entries(fileTypes).forEach(([ext, count]) => { console.log(` ${ext || "No extension"}: ${count} file(s)`); }); } if (options.verbose) { console.log("\nšŸ“„ Files to import:"); files.forEach((file, index) => { console.log(` ${index + 1}. ${path.relative(resolvedPath, file)}`); }); } if (options.dryRun) { console.log(formatting_1.format.info("\nšŸ” Dry run mode - no actual import will be performed")); console.log(formatting_1.format.success("āœ… Directory scan completed. Ready for import.")); return; } const { shouldProceed } = await inquirer_1.default.prompt([ { type: "confirm", name: "shouldProceed", message: `Proceed with importing ${files.length} file(s)?`, default: true, }, ]); if (!shouldProceed) { console.log(formatting_1.format.info("Import cancelled.")); return; } const result = await (0, spinner_1.withSpinner)(`Importing directory: ${path.basename(dirPath)}...`, () => apiClient.importDir(memoryBlockId, { dirPath: resolvedPath, recursive: options.recursive, includePattern: options.include, excludePattern: options.exclude, metadata, }), { successText: "Directory import completed", errorText: "Directory import failed", }); if (result.success && result.data) { const successCount = Array.isArray(result.data) ? result.data.length : 1; console.log(formatting_1.format.success(`\nāœ… Import completed successfully!`)); console.log(` ${chalk_1.default.blue("Files Processed")}: ${successCount}`); if (options.verbose && Array.isArray(result.data)) { console.log("\nšŸ“Š Import Results:"); result.data.forEach((item, index) => { console.log(` ${index + 1}. ${item.id || "N/A"} - ${item.status || "N/A"}`); }); } } else { throw new Error(result.error || "Import failed with unknown error"); } } async function interactiveDirImport(apiClient) { console.log(chalk_1.default.cyan("\nšŸ“ Interactive Directory Import\n")); const memoryBlockId = await (0, memory_blocks_1.selectMemoryBlock)(apiClient); const answers = await inquirer_1.default.prompt([ { type: "input", name: "dirPath", message: "Enter the directory path to import:", validate: async (input) => { if (!input.trim()) { return "Directory path is required"; } const resolvedPath = path.resolve(input.trim()); if (!(await fs.pathExists(resolvedPath))) { return `Directory not found: ${input}`; } const stats = await fs.stat(resolvedPath); if (!stats.isDirectory()) { return `Path is not a directory: ${input}`; } return true; }, }, { type: "confirm", name: "recursive", message: "Import subdirectories recursively?", default: true, }, { type: "input", name: "includePattern", message: 'File pattern to include (optional, e.g., "*.{pdf,txt,md}"):', default: "", }, { type: "input", name: "excludePattern", message: "File pattern to exclude (optional):", default: "", }, { type: "input", name: "maxFiles", message: "Maximum number of files to import:", default: "100", validate: (input) => { const num = parseInt(input); if (isNaN(num) || num < 1 || num > 1000) { return "Max files must be a number between 1 and 1000"; } return true; }, }, { type: "confirm", name: "addMetadata", message: "Add metadata to all imported files?", default: false, }, ]); let metadata; if (answers.addMetadata) { const metadataAnswers = await collectMetadata(); if (Object.keys(metadataAnswers).length > 0) { metadata = metadataAnswers; } } const options = { memoryBlock: memoryBlockId, recursive: answers.recursive, include: answers.includePattern || undefined, exclude: answers.excludePattern || undefined, maxFiles: answers.maxFiles, metadata: metadata ? JSON.stringify(metadata) : undefined, verbose: false, dryRun: false, }; await importDirectory(apiClient, answers.dirPath, options); } async function findFilesToImport(dirPath, options) { const { recursive = false, includePattern, excludePattern, maxFiles = 100, } = options; let pattern = recursive ? "**/*" : "*"; if (includePattern) { pattern = recursive ? `**/${includePattern}` : includePattern; } const globOptions = { cwd: dirPath, absolute: true, nodir: true, }; let files = await (0, glob_1.glob)(pattern, globOptions); if (excludePattern) { const excludeFiles = await (0, glob_1.glob)(recursive ? `**/${excludePattern}` : excludePattern, globOptions); const excludeSet = new Set(excludeFiles); files = files.filter((file) => !excludeSet.has(file)); } files = files.filter((file) => isSupportedFileType(file)); if (files.length > maxFiles) { files = files.slice(0, maxFiles); } return files; } function getFileTypeBreakdown(files) { const breakdown = {}; files.forEach((file) => { const ext = path.extname(file).toLowerCase() || "No extension"; breakdown[ext] = (breakdown[ext] || 0) + 1; }); return breakdown; } async function collectMetadata() { const metadata = {}; console.log(chalk_1.default.yellow("\nšŸ·ļø Adding Metadata (press Enter with empty key to finish):\n")); while (true) { const { key } = await inquirer_1.default.prompt([ { type: "input", name: "key", message: "Metadata key:", default: "", }, ]); if (!key.trim()) { break; } const { value, valueType } = await inquirer_1.default.prompt([ { type: "list", name: "valueType", message: "Value type:", choices: [ { name: "String", value: "string" }, { name: "Number", value: "number" }, { name: "Boolean", value: "boolean" }, { name: "JSON", value: "json" }, ], default: "string", }, { type: "input", name: "value", message: `Value for "${key}":`, validate: (input, answers) => { if (!input.trim() && answers.valueType !== "boolean") { return "Value is required"; } if (answers.valueType === "number" && isNaN(Number(input))) { return "Value must be a valid number"; } if (answers.valueType === "json") { try { JSON.parse(input); } catch { return "Value must be valid JSON"; } } return true; }, }, ]); switch (valueType) { case "number": metadata[key] = Number(value); break; case "boolean": metadata[key] = ["true", "yes", "1", "on"].includes(value.toLowerCase()); break; case "json": metadata[key] = JSON.parse(value); break; case "string": default: metadata[key] = value; break; } console.log(chalk_1.default.green(`āœ“ Added: ${key} = ${JSON.stringify(metadata[key])}`)); } return metadata; } function isSupportedFileType(filePath) { const supportedExtensions = [ ".txt", ".md", ".pdf", ".doc", ".docx", ".html", ".htm", ".json", ".csv", ".rtf", ".odt", ".pages", ".xml", ".js", ".ts", ".py", ".java", ".c", ".cpp", ".h", ".hpp", ".cs", ".php", ".rb", ".go", ".rs", ".swift", ".kt", ]; const ext = path.extname(filePath).toLowerCase(); return supportedExtensions.includes(ext); } //# sourceMappingURL=import-dir.js.map