Skip to main content
Glama
by microsoft
fs.ts9.2 kB
import { lstat, mkdir, writeFile, readFile, appendFile } from "fs/promises" import { HTTPS_REGEX } from "./constants" import { host } from "./host" import { dirname } from "path" import { JSON5TryParse } from "./json5" import { homedir } from "os" import { genaiscriptDebug } from "./debug" const dbg = genaiscriptDebug("fs") /** * Changes the file extension of a given file name. * * @param filename - The name of the file whose extension needs to be changed. * @param newext - The new extension to apply. If it does not start with a dot, one will be added automatically. * @returns The file name with the updated extension. */ export function changeext(filename: string, newext: string) { dbg(`checking if newext starts with a dot`) if (newext && !newext.startsWith(".")) { newext = "." + newext } return filename.replace(/\.[^.]+$/, newext) } /** * Reads the content of a specified file as text. * * @param fn - Path of the file to be read. * @returns The textual content of the file. */ export async function readText(fn: string) { dbg(`reading file ${fn}`) return readFile(fn, { encoding: "utf8" }) } /** * Attempts to read text content from a file. If the file cannot be read, returns undefined. * * @param fn - The path of the file to read. * @returns The content of the file as a string if successfully read, or undefined if an error occurs. */ export async function tryReadText(fn: string) { try { dbg(`trying to read text from file ${fn}`) return await readText(fn) } catch { return undefined } } /** * Ensures that the specified directory exists. * Creates the directory and any necessary parent directories if they do not exist. * * @param dir - The path of the directory to ensure exists. */ export async function ensureDir(dir: string) { dbg(`ensuring directory exists ${dir}`) await mkdir(dir, { recursive: true }) } /** * Expands homedir */ export function expandHomeDir(dir: string) { if (dir?.startsWith("~/")) { const home = homedir() dir = host.path.join(home, dir.slice(2)) } return dir } /** * Writes text content to a specified file, creating directories if necessary. * * @param fn - The path of the file to write to. Directories in the path will be created if they do not exist. * @param content - The textual content to write into the file. */ export async function writeText(fn: string, content: string) { if (!fn) throw new Error("filename is required") if (typeof content !== "string") throw new Error("content must be a string") await ensureDir(dirname(fn)) dbg(`writing text to file ${fn}`) await writeFile(fn, content, { encoding: "utf8" }) } /** * Appends text content to the end of the specified file, creating directories as needed. * * @param fn - Path to the file where content will be appended. Must be provided. * @param content - Text content to append to the file. * @throws Throws an error if the filename is not provided. */ export async function appendText(fn: string, content: string) { if (!fn) throw new Error("filename is required") await ensureDir(dirname(fn)) dbg(`append text to file ${fn}`) await appendFile(fn, content, { encoding: "utf8" }) } /** * Checks if a file exists at the given path. * * @param fn - The path to the file to check. * @returns A promise that resolves to `true` if the file exists and is a file, or `false` otherwise. */ export async function fileExists(fn: string) { dbg(`checking if file exists ${fn}`) const stat = await tryStat(fn) return !!stat?.isFile() } /** * Attempts to retrieve the file status for a given file path. * If an error occurs (e.g., the file does not exist), it returns undefined. * * @param fn - The path of the file to retrieve the status for. If not provided, returns undefined. * @returns The file status object if the file exists, or undefined if it does not. */ export async function tryStat(fn: string) { try { dbg(`getting file stats for ${fn}`) if (!fn) return undefined return await lstat(fn) } catch { return undefined } } /** * Reads and parses a JSON file from the specified path. * * @param fn - The path to the JSON file to be read. * @returns The parsed JSON object from the file. * @throws Throws an error if the file cannot be read or parsed as JSON. */ export async function readJSON(fn: string) { if (!fn) throw new Error("filename is required") dbg(`reading JSON from file ${fn}`) return JSON.parse(await readText(fn)) } /** * Tries to read and parse a JSON object from a file. * * @param fn - Path to the file to be read. * @returns The parsed JSON object if the operation succeeds, or `undefined` if an error occurs. */ export async function tryReadJSON(fn: string) { try { if (!fn) return undefined return JSON.parse(await readText(fn)) } catch { return undefined } } export async function tryReadJSON5(fn: string) { try { return JSON5TryParse(await readText(fn)) } catch { return undefined } } /** * Writes a JSON object to a file. * * @param fn - The path to the file where the JSON object will be written. * @param obj - The JSON object to be written to the file. */ export async function writeJSON(fn: string, obj: any) { if (!fn) throw new Error("filename is required") dbg(`writing JSON to file ${fn}`) await writeText(fn, JSON.stringify(obj)) } /** * Expands given file paths into a list of file paths and URLs, applying optional filtering and processing. * * @param files - An array of file paths or URLs to process. * @param options - Optional parameters for filtering and processing. * @param excludedFiles - A list of file paths or URLs to exclude from the result. * @param accept - A comma-separated list of file extensions to include (e.g., ".js,.ts"). * @param applyGitIgnore - Whether to apply `.gitignore` rules during file discovery. * @returns An array of expanded file paths and URLs, filtered based on the given options. */ export async function expandFiles( files: string[], options?: { excludedFiles?: string[] accept?: string applyGitIgnore?: boolean } ) { const { excludedFiles = [], accept, applyGitIgnore } = options || {} dbg(`no files to expand or accept is none`) if (!files.length || accept === "none") { return [] } dbg(`filtering URLs from files`) const urls = files .filter((f) => HTTPS_REGEX.test(f)) .filter((f) => !excludedFiles.includes(f)) dbg(`finding other files`) const others = await host.findFiles( files.filter((f) => !HTTPS_REGEX.test(f)), { ignore: excludedFiles.filter((f) => !HTTPS_REGEX.test(f)), applyGitIgnore, } ) const res = new Set([...urls, ...others]) dbg(`applying accept filter`) if (accept) { const exts = accept .split(",") .map((s) => s.trim().replace(/^\*\./, ".")) .filter((s) => !!s) for (const rf of res) { dbg(`removing file ${rf} as it does not match accepted extensions`) if (!exts.some((ext) => rf.endsWith(ext))) { res.delete(rf) } } } return Array.from(res) } /** * Expands a list of files or workspace files into a unified list of workspace files. * * @param files - Array of file paths or workspace file objects to process. * - Strings in the array represent file paths. * - Objects in the array represent workspace files. * @returns A Promise resolving to an array of workspace file objects. * * The function separates file paths and workspace file objects from the input, processes the file paths * through `expandFiles` to resolve all matching paths, and combines the results with the workspace file objects. */ export async function expandFileOrWorkspaceFiles( files: (string | WorkspaceFile)[] ): Promise<WorkspaceFile[]> { dbg(`expanding file or workspace files`) const filesPaths = await expandFiles( files.filter((f) => typeof f === "string"), { applyGitIgnore: false, } ) dbg(`filtering workspace files`) const workspaceFiles = files.filter( (f) => typeof f === "object" ) as WorkspaceFile[] return [ ...filesPaths.map( (filename) => ({ filename, }) satisfies WorkspaceFile ), ...workspaceFiles, ] } /** * Converts a file path or URL into a workspace-friendly file path. * * @param f - The file path or URL to convert. If the input is a valid HTTPS URL or an absolute file path as resolved by the host, it is returned as-is. Otherwise, the path is prefixed with `./` to create a relative path. * @returns The workspace-compatible file path or URL. */ export function filePathOrUrlToWorkspaceFile(f: string) { dbg(`converting file path or URL to workspace file ${f}`) return HTTPS_REGEX.test(f) || host.path.resolve(f) === f ? f : `./${f}` }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/microsoft/genaiscript'

If you have feedback or need assistance with the MCP directory API, please join our Discord server