@civic/nexus-bridge
Version:
Stdio <-> HTTP/SSE MCP bridge with Civic auth handling
118 lines • 4.47 kB
JavaScript
/**
* index.ts
*
* Main entry point for the Nexus Bridge. Initializes the bridge,
* handles process signals, and manages graceful shutdown.
* Now uses the new Bridge implementation for improved reliability.
*/
import { Bridge } from './bridge.js';
import * as configVars from './config.js';
import { version } from './version.js';
import { runInstaller, AVAILABLE_TARGETS } from './installer/index.js';
// Redirect console.log to stderr to avoid interfering with MCP JSON messages
// Note: Our custom logger (utils/logger.ts) already writes directly to stderr, but
// these redirects ensure that any direct console.log calls or third-party libraries
// that use console methods also have their output redirected to stderr. This provides
// a comprehensive solution where ALL output is consistently sent to stderr regardless
// of its source, maintaining clean stdout for the MCP protocol.
console.log = (...args) => {
process.stderr.write(`LOG: ${args.join(' ')}\n`);
};
console.error = (...args) => {
process.stderr.write(`ERROR: ${args.join(' ')}\n`);
};
console.warn = (...args) => {
process.stderr.write(`WARN: ${args.join(' ')}\n`);
};
console.info = (...args) => {
process.stderr.write(`INFO: ${args.join(' ')}\n`);
};
// Log configuration values
const logConfig = () => {
console.log(`Civic Nexus Bridge v${version} (Node ${process.version})`);
console.log('Configuration:');
console.log('MCP_REMOTE_URL:', configVars.REMOTE_MCP_URL);
console.log('CIVIC_AUTH_URL:', configVars.CIVIC_AUTH_URL);
console.log('CLIENT_ID:', configVars.CLIENT_ID);
console.log('REQUEST_TIMEOUT_MS:', configVars.REQUEST_TIMEOUT_MS);
console.log('NO_LOGIN:', configVars.NO_LOGIN);
console.log('NO_AUTH_CAPTURE:', configVars.NO_AUTH_CAPTURE);
};
let bridge = null;
let isShuttingDown = false; // Prevent double shutdown calls
/**
* Handle installer mode when 'install' argument is passed
*/
async function handleInstaller() {
const args = process.argv.slice(2);
if (args.length > 0 && args[0] === 'install') {
if (args.length < 2) {
console.error('Error: Missing installation target. Use: nexus-bridge install <target>');
console.error(`Available targets: ${AVAILABLE_TARGETS.join(', ')}`);
throw new Error('Missing installation target');
}
const target = args[1];
try {
await runInstaller(target);
// Successfully completed installation
return true;
}
catch (error) {
console.error(`Error during installation: ${error}`);
throw error;
}
return true;
}
return false;
}
async function main() {
// Check if we should run in installer mode
const isInstallerMode = await handleInstaller();
if (isInstallerMode)
return;
logConfig();
// Normal bridge operation
bridge = new Bridge();
try {
await bridge.start();
}
catch (error) {
console.error("Fatal error during bridge startup:", error);
throw new Error(`Bridge startup failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function shutdown(signal) {
if (isShuttingDown)
return;
isShuttingDown = true;
console.log(`\nReceived ${signal}. Shutting down gracefully...`);
if (bridge) {
await bridge.shutdown();
}
console.log("Shutdown complete.");
// Node will exit naturally after all event handlers complete
}
// Handle termination signals
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('SIGTERM', () => shutdown('SIGTERM'));
// Basic error handling
process.on('uncaughtException', (error, origin) => {
console.error(`\nFATAL UNCAUGHT EXCEPTION (${origin}):`, error);
// Attempt graceful shutdown, but it might fail if state is corrupted
shutdown('uncaughtException').catch(() => {
console.error('Failed to shutdown gracefully after uncaught exception');
throw error;
});
});
process.on('unhandledRejection', (reason) => {
console.error('\nFATAL UNHANDLED REJECTION:');
console.error('Reason:', reason);
// Attempt graceful shutdown
shutdown('unhandledRejection').catch(() => {
console.error('Failed to shutdown gracefully after unhandled rejection');
throw reason instanceof Error ? reason : new Error(String(reason));
});
});
// Start the application
main();
//# sourceMappingURL=index.js.map