Skip to main content
Glama

mcp-google-sheets

piece-utils.ts6.66 kB
import { readdir, stat } from 'node:fs/promises' import * as path from 'path' import { cwd } from 'node:process' import { readPackageJson, readProjectJson } from './files' import { exec } from './exec' import axios from 'axios' import chalk from 'chalk' import FormData from 'form-data'; import fs from 'fs'; export const piecesPath = () => path.join(cwd(), 'packages', 'pieces') export const customPiecePath = () => path.join(piecesPath(), 'custom') /** * Finds and returns the paths of specific pieces or all available pieces in a given directory. * * @param inputPath - The root directory to search for pieces. If not provided, a default path to custom pieces is used. * @param pieces - An optional array of piece names to search for. If not provided, all pieces in the directory are returned. * @returns A promise resolving to an array of strings representing the paths of the found pieces. */ export async function findPieces(inputPath?: string, pieces?: string[]): Promise<string[]> { const piecesPath = inputPath ?? customPiecePath() const piecesFolders = await traverseFolder(piecesPath) if (pieces) { return pieces.flatMap((piece) => { const folder = piecesFolders.find((p) => { const normalizedPath = path.normalize(p); return normalizedPath.endsWith(path.sep + piece); }); if (!folder) { return []; } return [folder]; }); } else { return piecesFolders } } /** * Finds and returns the path of a single piece. Exits the process if the piece is not found. * * @param pieceName - The name of the piece to search for. * @returns A promise resolving to a string representing the path of the found piece. If not found, the process exits. */ export async function findPiece(pieceName: string): Promise<string | null> { return (await findPieces(piecesPath(), [pieceName]))[0] ?? null; } export async function buildPiece(pieceFolder: string): Promise<{ outputFolder: string, outputFile: string }> { const projectJson = await readProjectJson(pieceFolder); await buildPackage(projectJson.name); const compiledPath = `dist/packages/${removeStartingSlashes(pieceFolder).split(path.sep + 'packages')[1]}`; const { stdout } = await exec('npm pack --json', { cwd: compiledPath }); const tarFileName = JSON.parse(stdout)[0].filename; return { outputFolder: compiledPath, outputFile: path.join(compiledPath, tarFileName) }; } export async function buildPackage(projectName:string) { await exec(`npx nx build ${projectName} --skip-cache`); return { outputFolder: `dist/packages/${projectName}`, } } export async function publishPieceFromFolder( {pieceFolder, apiUrl, apiKey, failOnError}: {pieceFolder: string, apiUrl: string, apiKey: string, failOnError: boolean,} ) { const projectJson = await readProjectJson(pieceFolder); const packageJson = await readPackageJson(pieceFolder); await buildPackage(projectJson.name); const { outputFile } = await buildPiece(pieceFolder); const formData = new FormData(); console.log(chalk.blue(`Uploading ${outputFile}`)); formData.append('pieceArchive', fs.createReadStream(outputFile)); formData.append('pieceName', packageJson.name); formData.append('pieceVersion', packageJson.version); formData.append('packageType', 'ARCHIVE'); formData.append('scope', 'PLATFORM'); try { await axios.post(`${apiUrl}/v1/pieces`, formData, { headers: { 'Authorization': `Bearer ${apiKey}`, ...formData.getHeaders() } }); console.info(chalk.green(`Piece '${packageJson.name}' published.`)); } catch (error) { if (axios.isAxiosError(error)) { if (error.response.status === 409) { console.info(chalk.yellow(`Piece '${packageJson.name}' and '${packageJson.version}' already published.`)); } else if (Math.floor(error.response.status / 100) !== 2) { console.info(chalk.red(`Error publishing piece '${packageJson.name}', ${error}` )); if (failOnError) { console.info(chalk.yellow(`Terminating process due to publish failure for piece '${packageJson.name}' (fail-on-error is enabled)`)); process.exit(1); } } else { console.error(chalk.red(`Unexpected error: ${error.message}`)); if (failOnError) { console.info(chalk.yellow(`Terminating process due to unexpected error for piece '${packageJson.name}' (fail-on-error is enabled)`)); process.exit(1); } } } else { console.error(chalk.red(`Unexpected error: ${error.message}`)); if (failOnError) { console.info(chalk.yellow(`Terminating process due to unexpected error for piece '${packageJson.name}' (fail-on-error is enabled)`)); process.exit(1); } } } } async function traverseFolder(folderPath: string): Promise<string[]> { const paths: string[] = [] const directoryExists = await stat(folderPath).catch(() => null) if (directoryExists && directoryExists.isDirectory()) { const files = await readdir(folderPath) for (const file of files) { const filePath = path.join(folderPath, file) const fileStats = await stat(filePath) if (fileStats.isDirectory() && file !== 'node_modules' && file !== 'dist') { paths.push(...await traverseFolder(filePath)) } else if (file === 'package.json') { paths.push(folderPath) } } } return paths } export function displayNameToKebabCase(displayName: string): string { return displayName.toLowerCase().replace(/\s+/g, '-'); } export function displayNameToCamelCase(input: string): string { const words = input.split(' '); const camelCaseWords = words.map((word, index) => { if (index === 0) { return word.toLowerCase(); } else { return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); } }); return camelCaseWords.join(''); } export const assertPieceExists = async (pieceName: string | null) => { if (!pieceName) { console.error(chalk.red(`🚨 Piece ${pieceName} not found`)); process.exit(1); } }; export const removeStartingSlashes = (str: string) => { return str.startsWith('/') ? str.slice(1) : str; }

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/activepieces/activepieces'

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