Skip to main content
Glama
upgrader.ts3.91 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { fetchLatestVersionString, isValidMedplumSemver, Logger, normalizeErrorString } from '@medplum/core'; import { execSync, spawnSync } from 'node:child_process'; import { existsSync } from 'node:fs'; import { platform } from 'node:os'; import process from 'node:process'; import * as semver from 'semver'; import { downloadRelease, getReleaseBinPath } from './upgrader-utils'; export async function upgraderMain(argv: string[]): Promise<void> { // TODO: Add support for Linux if (platform() !== 'win32') { throw new Error(`Unsupported platform: ${platform()}. Agent upgrader currently only supports Windows`); } // NOTE: Windows past this point, for now const globalLogger = new Logger((msg) => console.log(msg)); if (!process.send) { globalLogger.error('Upgrader not started as a child process with Node IPC enabled. Aborting...'); process.exit(1); } let rejectOnTimeout!: () => void; const disconnectedPromise = new Promise<void>((resolve, reject) => { rejectOnTimeout = () => reject(new Error('Timed out while waiting for IPC to disconnect')); process.once('disconnect', resolve); }); // Make sure if version is given, it matches semver if (argv[3] && !isValidMedplumSemver(argv[3])) { throw new Error('Invalid version specified'); } const version = argv[3] ?? (await fetchLatestVersionString('agent-upgrader')); const binPath = getReleaseBinPath(version); // If release in not locally downloaded, download it first if (!existsSync(binPath)) { // Download release globalLogger.info(`Could not find binary at "${binPath}". Downloading release from GitHub...`); await downloadRelease(version, binPath); globalLogger.info('Release successfully downloaded'); } process.send({ type: 'STARTED' }); globalLogger.info('Waiting for parent to disconnect from IPC...'); const disconnectTimeout = setTimeout(rejectOnTimeout, 5000); await disconnectedPromise; clearTimeout(disconnectTimeout); try { // If downgrading to a pre-zero-downtime agent (pre-4.2.4), stop and uninstall the current agent service before continuing if (semver.lt(version, '4.2.4')) { // Call the current binary with the --remove-old-services and the --all flags to remove all existing agent services before installing the "new" (old, pre-4.2.4) agent globalLogger.info('Uninstalling the current agent service before installing the pre-zero-downtime agent...'); spawnSync(__filename, ['--remove-old-services', '--all']); globalLogger.info('Successfully uninstalled all existing agent services'); // We use this command to create a mock 'MedplumAgent' service, which allows us to preserve the agent.properties file by opting into the 'Upgrade' installer path globalLogger.info('Creating mock MedplumAgent service to opt into "Upgrade" path in installer...'); execSync('sc.exe create MedplumAgent binPath=cmd.exe'); globalLogger.info('Successfully created mock service'); } // Run installer globalLogger.info('Running installer silently', { binPath }); spawnSync(`"${binPath}" /S`, { windowsHide: true, shell: true }); globalLogger.info(`Agent version ${version} successfully installed`); } catch (err: unknown) { // Try to restart Agent service if anything goes wrong globalLogger.error(`Error while attempting to run installer: ${normalizeErrorString(err)}`); globalLogger.error('Failed to run installer, attempting to restart agent service...'); try { execSync('net start "Medplum Agent"'); globalLogger.info('Successfully restarted agent service'); } catch (_err: unknown) { globalLogger.info('Medplum agent already started, skipping restart'); } return; } globalLogger.info('Finished upgrade'); }

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