UNPKG

uapi-sdk

Version:

CLI client for Library Navigator API

225 lines 7.6 kB
#!/usr/bin/env node import { Command } from 'commander'; import chalk from 'chalk'; import axios from 'axios'; import ora from 'ora'; import dotenv from 'dotenv'; import * as fs from 'fs'; // Load environment variables dotenv.config(); // Define API paths and configuration const BASE_URL = process.env.API_BASE_URL || 'https://lymanlabs--library-navigator-api-run-api.modal.run'; const API_PATHS = { status: '/api/status', list_domains: '/api/domains', navigate: '/api/navigate', view: '/api/view', search: '/api/search' }; // API client class class LibraryNavigatorClient { baseUrl; constructor(baseUrl = BASE_URL) { this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash if present } async getStatus() { return this.makeRequest('GET', 'status'); } async listDomains() { return this.makeRequest('GET', 'list_domains'); } async navigateDirectory(path, query) { const payload = { path }; if (query) { payload.query = query; } return this.makeRequest('POST', 'navigate', payload); } async viewFile(filePath) { const payload = { file_path: filePath }; return this.makeRequest('POST', 'view', payload); } async searchFiles(query, library, topK = 5) { const payload = { query, top_k: topK }; if (library) { payload.library = library; } return this.makeRequest('POST', 'search', payload); } async makeRequest(method, pathKey, payload) { const url = `${this.baseUrl}${API_PATHS[pathKey]}`; try { let response; if (method.toUpperCase() === 'GET') { response = await axios.get(url, { timeout: 30000 }); } else if (method.toUpperCase() === 'POST') { response = await axios.post(url, payload, { timeout: 30000 }); } else { throw new Error(`Unsupported HTTP method: ${method}`); } return response.data; } catch (error) { if (error && typeof error === 'object') { const axiosError = error; if (axiosError.response) { return { error: `Request failed with status ${axiosError.response.status}`, data: axiosError.response.data }; } else if (axiosError.request) { return { error: 'No response received from server' }; } else if (axiosError.message) { return { error: `Request error: ${axiosError.message}` }; } } return { error: `Unexpected error: ${error}` }; } } } // Create CLI program const program = new Command(); const client = new LibraryNavigatorClient(); program .name('uapi') .description('CLI for Library Navigator API') .version('0.1.0'); // Status command program .command('status') .description('Check API server status') .action(async () => { const spinner = ora('Checking API status...').start(); try { const result = await client.getStatus(); spinner.succeed('Status check completed'); console.log(JSON.stringify(result, null, 2)); } catch (error) { spinner.fail(`Error: ${error}`); } }); // List domains command program .command('list') .description('List all available domains') .action(async () => { const spinner = ora('Fetching domains...').start(); try { const result = await client.listDomains(); spinner.succeed('Domains fetched successfully'); console.log(JSON.stringify(result, null, 2)); } catch (error) { spinner.fail(`Error: ${error}`); } }); // Navigate command program .command('nav') .description('Navigate a directory and optionally get suggestions') .argument('<path>', 'Directory path to navigate') .option('-q, --query <query>', 'Query for directory navigation suggestions') .action(async (path, options) => { const spinner = ora(`Navigating ${path}...`).start(); try { const result = await client.navigateDirectory(path, options.query); spinner.succeed('Navigation completed'); console.log(JSON.stringify(result, null, 2)); } catch (error) { spinner.fail(`Error: ${error}`); } }); // View file command program .command('view') .description('View the contents of a file') .argument('<file_path>', 'Path to the file to view') .action(async (filePath) => { const spinner = ora(`Loading file ${filePath}...`).start(); try { const result = await client.viewFile(filePath); spinner.succeed('File loaded successfully'); if (result && 'content' in result && !('error' in result)) { console.log(chalk.cyan(`\n--- Content of ${result.virtual_path || filePath} ---`)); console.log(result.content); console.log(chalk.cyan('--- End of Content ---\n')); } else { console.log(JSON.stringify(result, null, 2)); } } catch (error) { spinner.fail(`Error: ${error}`); } }); // Search command program .command('search') .description('Search for files matching a query') .argument('<query>', 'Search query') .option('-l, --lib <library>', 'Specific library to search in') .option('-k, --topk <number>', 'Number of top results to return', '5') .action(async (query, options) => { const spinner = ora(`Searching for "${query}"...`).start(); try { // Validate and parse topk parameter let topK = 5; if (options.topk) { const parsedTopK = parseInt(options.topk); if (isNaN(parsedTopK) || parsedTopK <= 0) { spinner.fail(`Invalid value for --topk: ${options.topk}. Using default of 5.`); topK = 5; } else if (parsedTopK > 100) { spinner.warn('The requested number of results is too large. Limiting to 100.'); topK = 100; } else { topK = parsedTopK; } } const result = await client.searchFiles(query, options.lib, topK); spinner.succeed('Search completed'); console.log(JSON.stringify(result, null, 2)); } catch (error) { spinner.fail(`Error: ${error}`); } }); // Set base URL command program .command('config') .description('Set or view API base URL') .option('-u, --url <url>', 'Set the API base URL') .action((options) => { if (options.url) { // Validate URL format try { const url = new URL(options.url); if (!url.protocol.startsWith('http')) { console.log(chalk.red('Error: URL must use HTTP or HTTPS protocol')); return; } // Create .env file with the new URL const envContent = `API_BASE_URL=${options.url}`; fs.writeFileSync('.env', envContent); console.log(chalk.green(`API base URL set to: ${options.url}`)); } catch (error) { console.log(chalk.red(`Error: Invalid URL format - ${options.url}`)); } } else { console.log(chalk.blue(`Current API base URL: ${BASE_URL}`)); } }); // Parse command line arguments program.parse(); // Display help if no arguments provided if (process.argv.length <= 2) { program.help(); } //# sourceMappingURL=index.js.map