/**
* TOML configuration loader
* Loads and validates OssHub configuration from TOML files
*/
import { readFileSync, existsSync } from "fs";
import { parse as parseToml } from "@iarna/toml";
import type { TomlConfig, SourceConfig, ToolConfig } from "../types/config.js";
/**
* Load configuration from a TOML file
*/
export function loadTomlConfig(configPath: string): TomlConfig {
if (!existsSync(configPath)) {
throw new Error(`Configuration file not found: ${configPath}`);
}
const content = readFileSync(configPath, "utf-8");
const parsed = parseToml(content) as any;
// Validate and transform sources
const sources: SourceConfig[] = [];
if (parsed.sources && Array.isArray(parsed.sources)) {
for (const source of parsed.sources) {
sources.push(validateSource(source));
}
}
// Validate and transform tools
const tools: ToolConfig[] = [];
if (parsed.tools && Array.isArray(parsed.tools)) {
for (const tool of parsed.tools) {
tools.push(validateTool(tool, sources));
}
}
return { sources, tools };
}
/**
* Validate a source configuration
*/
function validateSource(source: any): SourceConfig {
if (!source.id || typeof source.id !== "string") {
throw new Error("Source configuration requires an 'id' field (string)");
}
if (!source.type || typeof source.type !== "string") {
throw new Error(`Source '${source.id}': 'type' field is required (obs, oss, s3, cos, minio)`);
}
if (!source.endpoint || typeof source.endpoint !== "string") {
throw new Error(`Source '${source.id}': 'endpoint' field is required`);
}
if (!source.access_key || typeof source.access_key !== "string") {
throw new Error(`Source '${source.id}': 'access_key' field is required`);
}
if (!source.secret_key || typeof source.secret_key !== "string") {
throw new Error(`Source '${source.id}': 'secret_key' field is required`);
}
return {
id: source.id,
type: source.type,
endpoint: source.endpoint,
access_key: source.access_key,
secret_key: source.secret_key,
region: source.region,
default_bucket: source.default_bucket,
connection_timeout: source.connection_timeout,
security_token: source.security_token,
ssl: source.ssl,
path_style: source.path_style,
};
}
/**
* Validate a tool configuration
*/
function validateTool(tool: any, sources: SourceConfig[]): ToolConfig {
if (!tool.name || typeof tool.name !== "string") {
throw new Error("Tool configuration requires a 'name' field");
}
if (!tool.source || typeof tool.source !== "string") {
throw new Error(`Tool '${tool.name}': 'source' field is required`);
}
// Validate source exists
const sourceExists = sources.some((s) => s.id === tool.source);
if (!sourceExists) {
throw new Error(
`Tool '${tool.name}': source '${tool.source}' not found. Available sources: ${sources.map((s) => s.id).join(", ")}`
);
}
return tool as ToolConfig;
}
/**
* Find configuration file in common locations
*/
export function findConfigFile(explicitPath?: string): string | null {
// Check explicit path first
if (explicitPath) {
if (existsSync(explicitPath)) {
return explicitPath;
}
throw new Error(`Configuration file not found: ${explicitPath}`);
}
// Check common locations
const locations = [
"./osshub.toml",
"./config/osshub.toml",
"~/.osshub/config.toml",
];
for (const location of locations) {
const expandedPath = location.replace("~", process.env.HOME || process.env.USERPROFILE || "");
if (existsSync(expandedPath)) {
return expandedPath;
}
}
return null;
}