diff --git a/src/commands/models.ts b/src/commands/models.ts index 6be456a..fabd950 100644 --- a/src/commands/models.ts +++ b/src/commands/models.ts @@ -1,10 +1,12 @@ import { readFileSync, writeFileSync, existsSync } from "fs"; +import { execSync } from "child_process"; import { homedir } from "os"; import { join } from "path"; import { parse, stringify } from "yaml"; const CONFIG_PATH = join(homedir(), ".hermes", "config.yaml"); const ENV_PATH = join(homedir(), ".hermes", ".env"); +const CFG_PROVIDERS_KEY = "HERMES_CUSTOM_PROVIDERS"; // ── Types ────────────────────────────────────────────────────────────── @@ -72,10 +74,43 @@ function saveConfig(config: Config) { writeFileSync(CONFIG_PATH, stringify(config, { lineWidth: 120 }), "utf-8"); } +/** + * Read all available providers from cfg (central registry). + * Falls back to config.yaml custom_providers if cfg unavailable. + */ +function getCfgProviders(): CustomProvider[] { + try { + const raw = execSync(`cfg get ${CFG_PROVIDERS_KEY}`, { + encoding: "utf-8", + timeout: 5000, + }).trim(); + if (!raw) return []; + return JSON.parse(raw) as CustomProvider[]; + } catch { + return []; + } +} + +/** + * Get providers from cfg (authoritative source). + * The config.yaml custom_providers is now only a "currently active" slot. + */ +function getAllProviders(): CustomProvider[] { + const cfgProviders = getCfgProviders(); + if (cfgProviders.length > 0) return cfgProviders; + // Fallback to config.yaml if cfg has nothing + const config = loadConfig(); + return config.custom_providers || []; +} + function getCustomProviders(config: Config): CustomProvider[] { return config.custom_providers || []; } +function findProviderFromRegistry(name: string): CustomProvider | undefined { + return getAllProviders().find((p) => p.name === name); +} + function findProvider( config: Config, name: string @@ -123,42 +158,41 @@ function getAuthHeaders(provider: CustomProvider): Record { function providers() { const config = loadConfig(); - const customProviders = getCustomProviders(config); + const allProviders = getAllProviders(); const mainProvider = config.model?.provider || "auto"; const mainModel = config.model?.default || "(not set)"; console.log(`Main: provider=${mainProvider} model=${mainModel}\n`); - if (customProviders.length === 0) { + if (allProviders.length === 0) { console.log("No custom providers configured."); return; } - console.log("Custom providers:\n"); - for (const p of customProviders) { + console.log("Custom providers (from cfg):\n"); + for (const p of allProviders) { const active = p.name === mainProvider || `custom:${p.name}` === mainProvider ? " ◀ active" : ""; console.log(` ${p.name}${active}`); console.log(` url: ${p.base_url}`); if (p.api_mode) console.log(` mode: ${p.api_mode}`); } - console.log(`\nTotal: ${customProviders.length}`); + console.log(`\nTotal: ${allProviders.length}`); } // ── list ──────────────────────────────────────────────────────────────── async function list(providerName?: string) { - const config = loadConfig(); - const customProviders = getCustomProviders(config); + const allProviders = getAllProviders(); const targets: CustomProvider[] = providerName - ? customProviders.filter((p) => p.name === providerName) - : customProviders; + ? allProviders.filter((p) => p.name === providerName) + : allProviders; if (targets.length === 0) { if (providerName) { console.error(`Provider not found: ${providerName}`); console.error( - `Available: ${customProviders.map((p) => p.name).join(", ")}` + `Available: ${allProviders.map((p) => p.name).join(", ")}` ); } else { console.log("No custom providers configured."); @@ -220,12 +254,11 @@ async function list(providerName?: string) { // ── test ──────────────────────────────────────────────────────────────── async function test(providerName?: string) { - const config = loadConfig(); - const customProviders = getCustomProviders(config); + const allProviders = getAllProviders(); const targets: CustomProvider[] = providerName - ? customProviders.filter((p) => p.name === providerName) - : customProviders; + ? allProviders.filter((p) => p.name === providerName) + : allProviders; if (targets.length === 0) { if (providerName) { @@ -322,21 +355,45 @@ function switchConfig(opts: SwitchOptions) { console.log(` auxiliary.${task} → provider=${opts.provider}${opts.model ? ` model=${opts.model}` : ""}`); } } else { - // Switch main provider - const providerValue = findProvider(config, opts.provider) - ? `custom:${opts.provider}` - : opts.provider; - - if (!config.model) config.model = {}; - config.model.provider = providerValue; - if (opts.model) { - config.model.default = opts.model; + // Switch main provider — look up from cfg registry + const registryProvider = findProviderFromRegistry(opts.provider); + + if (registryProvider) { + // Write this provider into config.yaml custom_providers (replace all) + config.custom_providers = [registryProvider]; + const providerValue = `custom:${registryProvider.name}`; + if (!config.model) config.model = {}; + config.model.provider = providerValue; + if (opts.model) { + config.model.default = opts.model; + } + console.log(` main → provider=${providerValue}${opts.model ? ` model=${opts.model}` : ""}`); + console.log(` config.yaml custom_providers updated with ${registryProvider.name}`); + } else { + // Standard provider (openrouter, anthropic, etc.) + if (!config.model) config.model = {}; + config.model.provider = opts.provider; + if (opts.model) { + config.model.default = opts.model; + } + // Clear custom_providers since switching to standard + delete config.custom_providers; + console.log(` main → provider=${opts.provider}${opts.model ? ` model=${opts.model}` : ""}`); } - console.log(` main → provider=${providerValue}${opts.model ? ` model=${opts.model}` : ""}`); } saveConfig(config); - console.log("\nConfig saved. Run 'hermes gateway restart' or start a new session to apply."); + + // Restart gateway + try { + execSync("systemctl --user restart hermes-gateway 2>/dev/null || true", { + encoding: "utf-8", + timeout: 10000, + }); + console.log("\nConfig saved & gateway restarted."); + } catch { + console.log("\nConfig saved. Restart gateway manually to apply."); + } } async function switchTelegram(opts: SwitchOptions) {