mirror of
https://github.com/aljazceru/dvmcp.git
synced 2025-12-17 05:14:24 +01:00
225 lines
5.9 KiB
TypeScript
Executable File
225 lines
5.9 KiB
TypeScript
Executable File
#!/usr/bin/env bun
|
|
import {
|
|
ConfigGenerator,
|
|
generateHexKey,
|
|
type FieldConfig,
|
|
CONFIG_EMOJIS,
|
|
validateHexKey,
|
|
validateRelayUrl,
|
|
} from '@dvmcp/commons/config-generator';
|
|
import { join, resolve } from 'path';
|
|
import type { Config } from './src/config.js';
|
|
import { argv } from 'process';
|
|
import { existsSync } from 'fs';
|
|
import {
|
|
setConfigPath,
|
|
setInMemoryConfig,
|
|
createDefaultConfig,
|
|
} from './src/config.js';
|
|
import { decodeNaddr, decodeNprofile } from './src/nip19-utils.js';
|
|
import {
|
|
fetchProviderAnnouncement,
|
|
fetchServerAnnouncement,
|
|
parseAnnouncement,
|
|
} from './src/direct-discovery.js';
|
|
import type { DirectServerInfo } from './index.js';
|
|
import logger from './src/logger';
|
|
|
|
const defaultConfigPath = join(process.cwd(), 'config.dvmcp.yml');
|
|
let configPath = defaultConfigPath;
|
|
|
|
// Check for provider flag
|
|
const providerArgIndex = argv.indexOf('--provider');
|
|
const hasProviderFlag = providerArgIndex !== -1 && argv[providerArgIndex + 1];
|
|
const providerValue = hasProviderFlag ? argv[providerArgIndex + 1] : null;
|
|
|
|
// Check for server flag
|
|
const serverArgIndex = argv.indexOf('--server');
|
|
const hasServerFlag = serverArgIndex !== -1 && argv[serverArgIndex + 1];
|
|
const serverValue = hasServerFlag ? argv[serverArgIndex + 1] : null;
|
|
|
|
// Check for config path flag (only used if provider and server flags are not present)
|
|
const configPathArgIndex = argv.indexOf('--config-path');
|
|
if (
|
|
!hasProviderFlag &&
|
|
!hasServerFlag &&
|
|
configPathArgIndex !== -1 &&
|
|
argv[configPathArgIndex + 1]
|
|
) {
|
|
configPath = resolve(argv[configPathArgIndex + 1]);
|
|
logger(`Using config path: ${configPath}`);
|
|
setConfigPath(configPath);
|
|
}
|
|
|
|
const configFields: Record<string, FieldConfig> = {
|
|
nostr: {
|
|
type: 'nested',
|
|
description: 'Nostr Configuration',
|
|
emoji: CONFIG_EMOJIS.NOSTR,
|
|
fields: {
|
|
privateKey: {
|
|
type: 'hex',
|
|
description: 'Private key',
|
|
generator: generateHexKey,
|
|
validation: validateHexKey,
|
|
required: true,
|
|
},
|
|
relayUrls: {
|
|
type: 'array',
|
|
description: 'Relay URLs',
|
|
validation: validateRelayUrl,
|
|
required: true,
|
|
},
|
|
},
|
|
},
|
|
mcp: {
|
|
type: 'nested',
|
|
description: 'Service Configuration',
|
|
emoji: CONFIG_EMOJIS.SERVICE,
|
|
fields: {
|
|
name: {
|
|
type: 'string',
|
|
description: 'Service name',
|
|
default: 'DVMCP Discovery',
|
|
},
|
|
version: {
|
|
type: 'string',
|
|
description: 'Service version',
|
|
default: '1.0.0',
|
|
required: true,
|
|
},
|
|
about: {
|
|
type: 'string',
|
|
description: 'Service description',
|
|
default: 'DVMCP Discovery Server for aggregating MCP tools from DVMs',
|
|
},
|
|
},
|
|
},
|
|
whitelist: {
|
|
type: 'nested',
|
|
description: 'DVM Whitelist Configuration',
|
|
emoji: CONFIG_EMOJIS.WHITELIST,
|
|
fields: {
|
|
allowedDVMs: {
|
|
type: 'set',
|
|
description: 'Allowed DVM public keys',
|
|
validation: validateHexKey,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const configure = async () => {
|
|
logger(
|
|
`${CONFIG_EMOJIS.SETUP} DVMCP Discovery Configuration Setup ${CONFIG_EMOJIS.SETUP}`
|
|
);
|
|
const generator = new ConfigGenerator<Config>(configPath, configFields);
|
|
await generator.generate();
|
|
};
|
|
|
|
const runApp = async (directServerInfo?: DirectServerInfo) => {
|
|
const main = await import('./index.js');
|
|
logger(`${CONFIG_EMOJIS.INFO} Running main application...`);
|
|
await main.default(directServerInfo);
|
|
};
|
|
|
|
const setupInMemoryConfig = (relays: string[], pubkey: string) => {
|
|
const config = createDefaultConfig(relays);
|
|
|
|
config.whitelist = {
|
|
allowedDVMs: new Set([pubkey]),
|
|
};
|
|
|
|
setInMemoryConfig(config);
|
|
};
|
|
|
|
const setupFromProvider = async (nprofileEntity: string) => {
|
|
logger(`${CONFIG_EMOJIS.INFO} Setting up from provider: ${nprofileEntity}`);
|
|
|
|
const providerData = decodeNprofile(nprofileEntity);
|
|
if (!providerData) {
|
|
console.error('Invalid nprofile entity');
|
|
process.exit(1);
|
|
}
|
|
|
|
try {
|
|
const announcement = await fetchProviderAnnouncement(providerData);
|
|
if (!announcement) {
|
|
console.error('Failed to fetch provider announcement');
|
|
process.exit(1);
|
|
}
|
|
|
|
setupInMemoryConfig(providerData.relays, providerData.pubkey);
|
|
logger(`${CONFIG_EMOJIS.SUCCESS} Successfully set up from provider`);
|
|
} catch (error) {
|
|
console.error(`Error: ${error}`);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
const setupFromServer = async (naddrEntity: string) => {
|
|
logger(`${CONFIG_EMOJIS.INFO} Setting up from server: ${naddrEntity}`);
|
|
|
|
const addrData = decodeNaddr(naddrEntity);
|
|
if (!addrData) {
|
|
console.error('Invalid naddr entity');
|
|
process.exit(1);
|
|
}
|
|
|
|
try {
|
|
const announcement = await fetchServerAnnouncement(addrData);
|
|
if (!announcement) {
|
|
console.error('Failed to fetch server announcement');
|
|
process.exit(1);
|
|
}
|
|
|
|
const parsedAnnouncement = parseAnnouncement(announcement);
|
|
if (!parsedAnnouncement) {
|
|
console.error('Failed to parse server announcement');
|
|
process.exit(1);
|
|
}
|
|
|
|
setupInMemoryConfig(addrData.relays, addrData.pubkey);
|
|
logger(`${CONFIG_EMOJIS.SUCCESS} Successfully set up from server`);
|
|
|
|
return {
|
|
pubkey: addrData.pubkey,
|
|
announcement: parsedAnnouncement,
|
|
};
|
|
} catch (error) {
|
|
console.error(`Error: ${error}`);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
const cliMain = async () => {
|
|
// Handle --configure flag
|
|
if (argv.includes('--configure')) {
|
|
await configure();
|
|
return;
|
|
}
|
|
|
|
// Handle --provider flag
|
|
if (hasProviderFlag && providerValue) {
|
|
await setupFromProvider(providerValue);
|
|
await runApp();
|
|
}
|
|
// Handle --server flag
|
|
else if (hasServerFlag && serverValue) {
|
|
const serverInfo = await setupFromServer(serverValue);
|
|
await runApp(serverInfo);
|
|
}
|
|
// Handle normal config file mode
|
|
else if (!existsSync(configPath)) {
|
|
logger(
|
|
`${CONFIG_EMOJIS.INFO} No configuration file found. Starting setup...`
|
|
);
|
|
await configure();
|
|
await runApp();
|
|
} else {
|
|
await runApp();
|
|
}
|
|
};
|
|
|
|
cliMain().catch(console.error);
|