UNPKG

data-source-cli

Version:

A CLI tool to manage and generate environment configuration for Claude/Goose or custom AI agents

165 lines (158 loc) 5.25 kB
#!/usr/bin/env node // bin/data-source-cli.ts import yargs from "yargs"; import { hideBin } from "yargs/helpers"; // lib/prompt.ts import readline from "readline"; async function ask(question) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); }); }); } // lib/config.ts import fs from "fs/promises"; import path from "path"; import os from "os"; import YAML from "yaml"; var CONFIG_DIR = path.join(os.homedir(), ".config", "data-source-cli"); var CONFIG_PATH = path.join(CONFIG_DIR, "config.yaml"); async function loadConfig() { try { const file = await fs.readFile(CONFIG_PATH, "utf-8"); return YAML.parse(file) || {}; } catch { return { sources: {} }; } } async function saveConfig(config) { await fs.mkdir(CONFIG_DIR, { recursive: true }); await fs.writeFile(CONFIG_PATH, YAML.stringify(config)); } // lib/utils.ts async function addSource() { const name = await ask("\u{1F539} Source name (e.g. my_api): "); const type = await ask("\u{1F538} Type (api | db | file): "); const base_url = await ask("\u{1F310} Base URL: "); const api_key = await ask("\u{1F511} API Key: "); const sandboxInput = await ask("\u{1F3DD}\uFE0F Use sandbox mode? (y/N): "); const sandbox = sandboxInput.toLowerCase() === "y"; const config = await loadConfig(); config.sources = config.sources || {}; config.sources[name] = { type, base_url, api_key, sandbox }; await saveConfig(config); console.log(`\u2705 Saved source '${name}' to config.`); } async function listSources() { const config = await loadConfig(); const sources = config.sources ?? {}; const keys = Object.keys(sources); if (!keys.length) { console.log("\u{1F4ED} No data sources found."); return; } console.log("\n\u{1F4CB} Registered Data Sources:\n"); keys.forEach((name, i) => { const s = sources[name]; console.log(`${i + 1}. ${name}`); console.log(` Type: ${s.type}`); console.log(` Base URL: ${s.base_url}`); console.log(` Sandbox: ${s.sandbox}`); console.log(""); }); } async function removeSource() { const config = await loadConfig(); const sources = config.sources ?? {}; const keys = Object.keys(sources); if (!keys.length) { console.log("\u{1F4ED} No sources to remove."); return; } console.log("\n\u{1F5D1}\uFE0F Select a source to remove:\n"); keys.forEach((key, i) => { console.log(`${i + 1}. ${key}`); }); const input = await ask("\u2753 Enter the number of the source to remove: "); const index = parseInt(input, 10); if (isNaN(index) || index < 1 || index > keys.length) { console.log("\u274C Invalid selection."); return; } const name = keys[index - 1]; delete config.sources[name]; await saveConfig(config); console.log(`\u2705 Removed source '${name}'`); } async function generateEnv(argv) { let sourceName = argv._[1]; const config = await loadConfig(); const sources = config.sources ?? {}; if (!Object.keys(sources).length) { console.log("\u{1F4ED} No data sources available."); return; } if (!sourceName) { console.log("\n\u{1F4CB} Available sources:"); Object.keys(sources).forEach((name, i) => { console.log(`${i + 1}. ${name}`); }); sourceName = await ask("Enter source name: "); } const source = sources[sourceName]; if (!source) { console.log(`\u274C Source '${sourceName}' not found.`); return; } console.log(` \u{1F510} .env values for '${sourceName}': `); Object.entries(source).forEach(([key, value]) => { const envKey = `${sourceName.toUpperCase()}_${key.toUpperCase()}`; console.log(`${envKey}=${value}`); }); } async function getUrl(argv) { let sourceName = argv._[1]; const config = await loadConfig(); const sources = config.sources ?? {}; if (!Object.keys(sources).length) { console.log("\u{1F4ED} No data sources configured."); return; } if (!sourceName) { console.log("\n\u{1F4CB} Available sources:"); Object.keys(sources).forEach((name, i) => console.log(`${i + 1}. ${name}`)); sourceName = await ask("Enter source name: "); } const source = sources[sourceName]; if (!source) { console.log(`\u274C Source '${sourceName}' not found.`); return; } const base = "goose://extension"; const query = new URLSearchParams({ cmd: "npx", arg: "data-source-cli", arg2: "start", id: sourceName, name: sourceName, ...Object.entries(source).reduce((acc, [key, val]) => { acc[`env[${key.toUpperCase()}]`] = String(val); return acc; }, {}) }); const url = `${base}?${query.toString()}`; console.log(` \u{1F517} Goose/Claude URL: ${url} `); } // bin/data-source-cli.ts yargs(hideBin(process.argv)).command("add", "Add a new data source", {}, addSource).command("list", "List all data sources", {}, listSources).command("remove", "Remove a data source", {}, removeSource).command("generate-env [name]", "Output .env format for a data source", {}, generateEnv).command("get-url [name]", "Generate Goose/Claude MCP-compatible URL", {}, getUrl).demandCommand().help().argv;