cli_lib.ts•3.8 kB
// src/lib/cli_lib.ts
import { URL } from "url";
import * as path from "node:path";
import { CliConfig } from "./cli.js";
import { extractDomain } from "./utils.js";
import { logger } from "./logger.js";
export interface ParsedNameUrl {
name: string;
urlValue: string;
}
/**
* Parse a string into a name-value pair using the specified delimiter
*/
export function parseNameValuePair(
input: string,
delimiter = ":"
): ParsedNameUrl | null {
const parts = input.split(delimiter);
if (parts.length < 2) {
logger.error(
`Invalid format: '${input}'. Expected 'name${delimiter}value'. Skipping.`
);
return null;
}
const name = parts[0].trim();
const urlValue = parts.slice(1).join(delimiter).trim();
if (!name || !urlValue) {
logger.error(
`Invalid format: '${input}'. Name or value part is empty. Skipping.`
);
return null;
}
return { name, urlValue };
}
/**
* Validate and add a URL or file path to the target record
*/
export function validateAndAddSource(
target: Record<string, string>,
name: string,
urlValue: string,
sourceOrigin: string
): void {
try {
new URL(urlValue);
target[name] = urlValue;
} catch (e) {
// Handle local file paths
if (urlValue.startsWith("/") || urlValue.startsWith(".")) {
const absolutePath = path.resolve(urlValue);
target[name] = absolutePath;
} else {
logger.error(
`(${sourceOrigin}) Invalid URL or Path format for '${name}': '${urlValue}'. Skipping. Error: ${
e instanceof Error ? e.message : e
}`
);
}
}
}
/**
* Process a list of space-separated items using the provided processor function
*/
export function processSpaceSeparatedItems<T>(
input: string | undefined,
processor: (item: string) => T | null,
errorPrefix: string
): T[] {
const results: T[] = [];
if (!input) return results;
const items = input.split(/\\s+/);
for (const item of items) {
if (!item.trim()) continue;
const result = processor(item.trim());
if (result) {
results.push(result);
}
}
return results;
}
/**
* Process multiple items using the provided processor function
*/
export function processMultipleItems<T>(
items: string[] | undefined,
processor: (item: string) => T | null,
errorPrefix: string
): T[] {
const results: T[] = [];
if (!items || items.length === 0) return results;
for (const item of items) {
const result = processor(item);
if (result) {
results.push(result);
}
}
return results;
}
/**
* Add a parsed source to the target record
*/
export function addParsedSourceToTarget(
source: ParsedNameUrl,
target: Record<string, string>,
origin: string
): void {
validateAndAddSource(target, source.name, source.urlValue, origin);
}
/**
* Normalize and add a domain to the target set
*/
export function normalizeAndAddDomain(
domain: string,
target: Set<string>,
origin: string
): void {
const extractedDomain = extractDomain(domain.trim());
if (extractedDomain) {
target.add(extractedDomain);
logger.info(`Added domain ${extractedDomain} from ${origin}`);
}
}
/**
* Log the configuration summary if in appropriate mode
*/
export function logConfigSummary(config: CliConfig): void {
logger.info("\n--- SushiMCP Configuration Summary ---");
logger.info(
`Documentation Sources: ${JSON.stringify(config.docSources, null, 2)}`
);
logger.info(
`OpenAPI Specs: ${JSON.stringify(config.openApiSpecs, null, 2)}`
);
logger.info(
`Allowed Fetch Domains: ${
[...config.allowedDomains].join(", ") || "(None - local only?)"
}`
);
logger.info("------------------------------------\n");
}
// Copyright (C) 2025 Christopher White
// SPDX-License-Identifier: AGPL-3.0-or-later