@tehreet/conduit
Version:
LLM API gateway with intelligent routing, robust process management, and health monitoring
199 lines (196 loc) • 7.43 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrapperCommand = wrapperCommand;
exports.installWrapperCommand = installWrapperCommand;
const claude_wrapper_1 = require("../core/claude-wrapper");
const metadata_handler_1 = require("../core/metadata-handler");
const synapse_features_1 = require("../features/synapse-features");
const utils_1 = require("../utils");
const log_1 = require("../utils/log");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
async function wrapperCommand(args) {
try {
// Find Claude binary
const claudeBinary = await findClaudeBinary();
if (!claudeBinary) {
console.error('Error: Claude CLI not found. Please install Claude Code first.');
process.exit(1);
}
// Load Conduit configuration
const config = await (0, utils_1.initConfig)();
// Initialize Synapse features if enabled
let synapseFeatures;
if (process.env.SYNAPSE_PROJECT_ID || process.env.SYNAPSE_AGENT_ID) {
synapseFeatures = new synapse_features_1.SynapseFeatures({
telemetryEndpoint: process.env.SYNAPSE_TELEMETRY_ENDPOINT,
});
}
// Create wrapper instance
const wrapper = new claude_wrapper_1.ClaudeWrapper({
claudeBinaryPath: claudeBinary,
config,
loggingEnabled: config.LOG || process.env.LOG === 'true',
metricsEnabled: true,
});
// Handle routing decision events
wrapper.on('routing-decision', decision => {
if (config.LOG) {
(0, log_1.log)('Routing decision:', decision);
}
});
// Handle usage tracking
if (synapseFeatures) {
wrapper.on('usage', async (usage) => {
await synapseFeatures.trackUsage({
projectId: usage.projectId || 'unknown',
agentId: usage.agentId || 'unknown',
model: usage.model,
inputTokens: usage.tokenCount || 0,
outputTokens: 0, // Will be updated when response completes
});
});
}
// Wrap the Claude command
const claudeProcess = await wrapper.wrapCommand(args, process.env);
// Set up stream handling with metadata injection
if (claudeProcess.stdout) {
const processor = metadata_handler_1.MetadataHandler.createStreamProcessor(metadata => {
// Metadata is already injected, just log if needed
if (config.LOG) {
(0, log_1.log)('Metadata injected:', metadata);
}
});
claudeProcess.stdout.pipe(processor).pipe(process.stdout);
}
// Pass through stderr
if (claudeProcess.stderr) {
claudeProcess.stderr.pipe(process.stderr);
}
// Pass through stdin
if (claudeProcess.stdin) {
process.stdin.pipe(claudeProcess.stdin);
}
// Handle process exit
claudeProcess.on('exit', code => {
process.exit(code || 0);
});
// Handle errors
claudeProcess.on('error', error => {
console.error('Claude process error:', error);
process.exit(1);
});
}
catch (error) {
console.error('Wrapper error:', error);
process.exit(1);
}
}
async function findClaudeBinary() {
// Common locations for Claude CLI
const possiblePaths = [
// Global npm install
'/usr/local/bin/claude',
'/usr/bin/claude',
// Local npm install
path.join(process.env.HOME || '', '.npm-global/bin/claude'),
path.join(process.env.HOME || '', 'node_modules/.bin/claude'),
// Homebrew
'/opt/homebrew/bin/claude',
'/usr/local/opt/claude/bin/claude',
// Direct from PATH
'claude',
];
// Check if CLAUDE_BINARY is set
if (process.env.CLAUDE_BINARY) {
possiblePaths.unshift(process.env.CLAUDE_BINARY);
}
// Try to find Claude in PATH first
try {
const { execSync } = require('child_process');
const which = execSync('which claude', { encoding: 'utf-8' }).trim();
if (which) {
return which;
}
}
catch (error) {
// which command failed, try other paths
}
// Check each possible path
for (const claudePath of possiblePaths) {
try {
if (fs.existsSync(claudePath)) {
// Verify it's executable
fs.accessSync(claudePath, fs.constants.X_OK);
return claudePath;
}
}
catch (error) {
// Continue to next path
}
}
return null;
}
async function installWrapperCommand() {
console.log('🔧 Setting up Conduit wrapper mode...\n');
// Create wrapper script
const wrapperScript = `#!/usr/bin/env node
// Conduit wrapper for Claude CLI
const { spawn } = require('child_process');
const path = require('path');
// Path to Conduit CLI
const conduitPath = '${process.argv[1]}';
// Run Conduit in wrapper mode
const conduit = spawn('node', [conduitPath, 'wrap', ...process.argv.slice(2)], {
stdio: 'inherit',
env: process.env
});
conduit.on('exit', (code) => process.exit(code || 0));
`;
// Save wrapper script
const wrapperPath = path.join(process.env.HOME || '', '.conduit', 'bin', 'conduit-claude');
const binDir = path.dirname(wrapperPath);
// Create directory
fs.mkdirSync(binDir, { recursive: true });
// Write wrapper script
fs.writeFileSync(wrapperPath, wrapperScript);
fs.chmodSync(wrapperPath, '755');
console.log('✅ Wrapper script created at:', wrapperPath);
console.log('\nTo use Conduit as a Claude wrapper, set:');
console.log(`export CLAUDE_BINARY="${wrapperPath}"`);
console.log('\nOr add to your shell configuration file.');
}
//# sourceMappingURL=wrapper-command.js.map