feat: gh-actions (#2)

This commit is contained in:
gzuuus
2025-02-13 22:09:55 +00:00
committed by GitHub
parent b3e11fcaa9
commit ee26c01b3b
3 changed files with 132 additions and 87 deletions

26
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Bun Runtime
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Run tests
env:
NODE_ENV: test
run: bun test
- name: Check formatting
run: bun run format --check

View File

@@ -1,41 +1,96 @@
import { parse } from 'yaml';
import { join } from 'path';
import { existsSync, readFileSync } from 'fs';
import type { MCPServerConfig } from './types';
interface NostrConfig {
privateKey: string;
relayUrls: string[];
}
interface MCPConfig {
name: string;
about: string;
clientName: string;
clientVersion: string;
servers: MCPServerConfig[];
}
interface WhitelistConfig {
allowedPubkeys: Set<string> | undefined;
}
interface AppConfig {
nostr: NostrConfig;
mcp: MCPConfig;
whitelist: WhitelistConfig;
}
import type { AppConfig, MCPServerConfig } from './types';
const CONFIG_PATH = join(process.cwd(), 'config.yml');
const HEX_KEYS_REGEX = /^(?:[0-9a-fA-F]{64})$/;
if (!existsSync(CONFIG_PATH)) {
const TEST_CONFIG: AppConfig = {
nostr: {
privateKey:
'd4d4d7aae7857054596c4c0976b22a73acac3a10d30bf56db35ee038bbf0dd44',
relayUrls: ['ws://localhost:3334'],
},
mcp: {
name: 'Test DVM MCP Bridge',
about: 'Test MCP-enabled DVM',
clientName: 'Test Client',
clientVersion: '1.0.0',
servers: [],
},
whitelist: {
allowedPubkeys: new Set(),
},
};
function validateRequiredField(value: any, fieldName: string): string {
if (!value) {
throw new Error(`Missing required config field: ${fieldName}`);
}
return value;
}
function getConfigValue(
value: string | undefined,
defaultValue: string
): string {
return value || defaultValue;
}
function validateRelayUrls(urls: any): string[] {
if (!Array.isArray(urls) || urls.length === 0) {
throw new Error(
'No config.yml file found. Please create one based on config.example.yml'
'At least one relay URL must be provided in nostr.relayUrls'
);
}
return urls.map((url: string) => {
try {
const trimmedUrl = url.trim();
new URL(trimmedUrl);
if (!trimmedUrl.startsWith('ws://') && !trimmedUrl.startsWith('wss://')) {
throw new Error(
`Relay URL must start with ws:// or wss://: ${trimmedUrl}`
);
}
return trimmedUrl;
} catch (error) {
throw new Error(`Invalid relay URL: ${url}`);
}
});
}
function validateMCPServers(servers: any): MCPServerConfig[] {
if (!Array.isArray(servers) || servers.length === 0) {
throw new Error(
'At least one MCP server must be configured in mcp.servers'
);
}
return servers.map((server: any, index: number) => {
if (!server.name || !server.command || !Array.isArray(server.args)) {
throw new Error(
`Invalid MCP server configuration at index ${index}. Required fields: name, command, args[]`
);
}
return {
name: server.name,
command: server.command,
args: server.args,
};
});
}
function loadConfig(): AppConfig {
if (process.env.NODE_ENV === 'test') {
return TEST_CONFIG;
}
if (!existsSync(CONFIG_PATH)) {
throw new Error(
'No config.yml file found. Please create one based on config.example.yml'
);
}
try {
const configFile = readFileSync(CONFIG_PATH, 'utf8');
const rawConfig = parse(configFile);
@@ -83,63 +138,4 @@ function loadConfig(): AppConfig {
}
}
function validateRequiredField(value: any, fieldName: string): string {
if (!value) {
throw new Error(`Missing required config field: ${fieldName}`);
}
return value;
}
function getConfigValue(
value: string | undefined,
defaultValue: string
): string {
return value || defaultValue;
}
function validateRelayUrls(urls: any): string[] {
if (!Array.isArray(urls) || urls.length === 0) {
throw new Error(
'At least one relay URL must be provided in nostr.relayUrls'
);
}
return urls.map((url: string) => {
try {
const trimmedUrl = url.trim();
new URL(trimmedUrl);
if (!trimmedUrl.startsWith('ws://') && !trimmedUrl.startsWith('wss://')) {
throw new Error(
`Relay URL must start with ws:// or wss://: ${trimmedUrl}`
);
}
return trimmedUrl;
} catch (error) {
throw new Error(`Invalid relay URL: ${url}`);
}
});
}
function validateMCPServers(servers: any): MCPServerConfig[] {
if (!Array.isArray(servers) || servers.length === 0) {
throw new Error(
'At least one MCP server must be configured in mcp.servers'
);
}
return servers.map((server: any, index: number) => {
if (!server.name || !server.command || !Array.isArray(server.args)) {
throw new Error(
`Invalid MCP server configuration at index ${index}. Required fields: name, command, args[]`
);
}
return {
name: server.name,
command: server.command,
args: server.args,
};
});
}
export const CONFIG = loadConfig();

View File

@@ -1,3 +1,26 @@
export interface NostrConfig {
privateKey: string;
relayUrls: string[];
}
export interface MCPConfig {
name: string;
about: string;
clientName: string;
clientVersion: string;
servers: MCPServerConfig[];
}
export interface WhitelistConfig {
allowedPubkeys: Set<string> | undefined;
}
export interface AppConfig {
nostr: NostrConfig;
mcp: MCPConfig;
whitelist: WhitelistConfig;
}
export interface MCPServerConfig {
name: string;
command: string;