Skip to main content
Glama
agent-main.ts3.85 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { MedplumClient, normalizeErrorString, parseLogLevel, sleep } from '@medplum/core'; import { existsSync, readFileSync } from 'node:fs'; import { App } from './app'; import { RETRY_WAIT_DURATION_MS } from './constants'; import { LoggerType, parseLoggerConfigFromArgs, WinstonWrapperLogger } from './logger'; import type { AgentArgs } from './types'; export async function agentMain(argv: string[]): Promise<App> { let args: AgentArgs; if (argv.length >= 6) { args = readCommandLineArgs(argv); } else if (argv.length === 3 && (argv[2] === '-h' || argv[2] === '--help')) { console.log('Expected arguments:'); console.log(' baseUrl: The Medplum server base URL.'); console.log(' clientId: The OAuth client ID.'); console.log(' clientSecret: The OAuth client secret.'); console.log(' agentId: The Medplum agent ID.'); process.exit(0); } else if (existsSync('agent.properties')) { args = readPropertiesFile('agent.properties'); } else { console.log('Missing arguments'); console.log('Arguments can be passed on the command line or in a properties file.'); console.log('Example with command line arguments:'); console.log(' node medplum-agent.js <baseUrl> <clientId> <clientSecret> <agentId>'); console.log('Example with properties file:'); console.log(' node medplum-agent.js'); process.exit(1); } if (!args.baseUrl || !args.clientId || !args.clientSecret || !args.agentId) { console.log('Missing arguments'); console.log('Expected arguments:'); console.log(' baseUrl: The Medplum server base URL.'); console.log(' clientId: The OAuth client ID.'); console.log(' clientSecret: The OAuth client secret.'); console.log(' agentId: The Medplum agent ID.'); process.exit(1); } const { baseUrl, clientId, clientSecret, agentId } = args; const medplum = new MedplumClient({ baseUrl, clientId }); let loggedIn = false; while (!loggedIn) { try { await medplum.startClientLogin(clientId, clientSecret); loggedIn = true; } catch (err) { console.error('Failed to login', { err: normalizeErrorString(err) }); console.log('Retrying login in 10 seconds...'); await sleep(RETRY_WAIT_DURATION_MS); } } // Replace logger logLevels if top-level logLevel is specified if (args.logLevel) { args['logger.main.logLevel'] = args.logLevel; args['logger.channel.logLevel'] = args.logLevel; } // Parse logger config before handing it to the app const [fullConfig, warnings] = parseLoggerConfigFromArgs(args); // Create loggers based on parsed config const mainLogger = new WinstonWrapperLogger(fullConfig[LoggerType.MAIN], LoggerType.MAIN); const channelLogger = new WinstonWrapperLogger(fullConfig[LoggerType.CHANNEL], LoggerType.CHANNEL); // Log out config warnings before proceeding to initializing app for (const warning of warnings) { mainLogger.warn(warning); } const app = new App(medplum, agentId, args.logLevel ? parseLogLevel(args.logLevel) : undefined, { mainLogger, channelLogger, }); await app.start(); process.on('SIGINT', async () => { console.log('Gracefully shutting down from SIGINT (Ctrl-C)'); await app.stop(); process.exit(); }); return app; } function readCommandLineArgs(argv: string[]): AgentArgs { const [_node, _script, baseUrl, clientId, clientSecret, agentId, logLevel] = argv; return { baseUrl, clientId, clientSecret, agentId, logLevel }; } function readPropertiesFile(fileName: string): AgentArgs { return Object.fromEntries( readFileSync(fileName) .toString() .split('\n') .map((line) => line.split('=').map((s) => s.trim())) ); }

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

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