Skip to main content
Glama
deleonio
by deleonio
cli.ts5.03 kB
#!/usr/bin/env node import process from 'node:process'; import { Command, CommanderError } from 'commander'; import packageJson from '../package.json' assert { type: 'json' }; import { createHydrateServer } from './server.js'; import type { HydrateServerOptions } from './types.js'; const DEFAULT_REST_PORT = 3000; const DEFAULT_GRPC_PORT = 50051; const DEFAULT_ROUTE = '/render'; const DEFAULT_HOST = '0.0.0.0'; type CliOptions = { restPort?: number; restHost?: string; grpcPort?: number; grpcHost?: string; host?: string; route?: string; logLevel?: string; quiet?: boolean; }; const parsePort = ( value: string, flag: string, errorFactory: (message: string) => Error = (message) => new CommanderError(1, 'InvalidOptionArgument', message), ): number => { const port = Number.parseInt(value, 10); if (!Number.isInteger(port) || port < 0 || port > 65535) { throw errorFactory(`Invalid value for ${flag}: ${value}`); } return port; }; const readEnvPort = (key: string): number | undefined => { const value = process.env[key]; if (!value) { return undefined; } return parsePort(value, key, (message) => new Error(message)); }; const program = new Command(); program .name('kolibri-hydrate-server') .description('High-performance REST and gRPC server for hydrating KoliBri components') .version(packageJson.version as string, '-v, --version', 'Display version number') .helpOption('-h, --help', 'Show this help text') .showHelpAfterError() .allowExcessArguments(false) .option('--host <host>', 'Hostname for both REST and gRPC servers') .option('--rest-host <host>', `Hostname for the REST server (default: ${DEFAULT_HOST})`) .option('--rest-port <port>', `Port for the REST server (default: ${DEFAULT_REST_PORT})`, (value) => parsePort(value, '--rest-port')) .option('--grpc-host <host>', `Hostname for the gRPC server (default: ${DEFAULT_HOST})`) .option('--grpc-port <port>', `Port for the gRPC server (default: ${DEFAULT_GRPC_PORT})`, (value) => parsePort(value, '--grpc-port')) .option('--route <path>', `REST endpoint path (default: ${DEFAULT_ROUTE})`) .option('--log-level <lvl>', 'Set Fastify logger level') .option('--quiet', 'Disable the Fastify logger'); program.exitOverride(); const main = async () => { let parsedArgs: CliOptions; try { program.parse(process.argv); parsedArgs = program.opts<CliOptions>(); } catch (error) { if (error instanceof CommanderError) { if (error.code === 'commander.helpDisplayed' || error.code === 'commander.version') { return; } process.exitCode = error.exitCode; return; } throw error; } if (parsedArgs.host) { parsedArgs.restHost = parsedArgs.restHost ?? parsedArgs.host; parsedArgs.grpcHost = parsedArgs.grpcHost ?? parsedArgs.host; } let envRestPort: number | undefined; let envGrpcPort: number | undefined; try { envRestPort = readEnvPort('KOLIBRI_HYDRATE_REST_PORT'); envGrpcPort = readEnvPort('KOLIBRI_HYDRATE_GRPC_PORT'); } catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; return; } const envRestHost = process.env.KOLIBRI_HYDRATE_REST_HOST; const envGrpcHost = process.env.KOLIBRI_HYDRATE_GRPC_HOST; const envRoute = process.env.KOLIBRI_HYDRATE_ROUTE; const normalizedEnvRoute = envRoute ? (envRoute.startsWith('/') ? envRoute : `/${envRoute}`) : undefined; const options: HydrateServerOptions = { restHost: parsedArgs.restHost ?? envRestHost ?? DEFAULT_HOST, restPort: parsedArgs.restPort ?? envRestPort ?? DEFAULT_REST_PORT, restRoute: (parsedArgs.route?.startsWith('/') ? parsedArgs.route : parsedArgs.route ? `/${parsedArgs.route}` : undefined) ?? normalizedEnvRoute ?? DEFAULT_ROUTE, grpcHost: parsedArgs.grpcHost ?? envGrpcHost ?? DEFAULT_HOST, grpcPort: parsedArgs.grpcPort ?? envGrpcPort ?? DEFAULT_GRPC_PORT, }; if (parsedArgs.quiet ?? process.env.KOLIBRI_HYDRATE_QUIET === 'true') { options.logger = false; } else if (parsedArgs.logLevel ?? process.env.KOLIBRI_HYDRATE_LOG_LEVEL) { options.logger = { level: parsedArgs.logLevel ?? process.env.KOLIBRI_HYDRATE_LOG_LEVEL }; } const server = await createHydrateServer(options); try { await server.start(); } catch (error) { console.error('Failed to start the hydrate server:', error instanceof Error ? error.message : String(error)); process.exitCode = 1; return; } const restUrl = server.getRestUrl(); const grpcEndpoint = server.getGrpcEndpoint(); console.log(`REST endpoint ready on ${restUrl ?? `${options.restHost}:${options.restPort}${options.restRoute}`}`); console.log(`gRPC server ready on ${grpcEndpoint ?? `${options.grpcHost}:${options.grpcPort}`}`); let shuttingDown = false; const shutdown = async () => { if (shuttingDown) { return; } shuttingDown = true; console.log('\nShutting down hydrate server...'); await server.stop(); process.exit(0); }; process.on('SIGINT', () => { void shutdown(); }); process.on('SIGTERM', () => { void shutdown(); }); }; void main();

Latest Blog Posts

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/deleonio/public-ui-kolibri'

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