feat: read providers from cfg registry instead of config.yaml
- providers/list/test now read from cfg (HERMES_CUSTOM_PROVIDERS) - switch writes target provider into config.yaml custom_providers and restarts gateway - standard providers (openrouter, anthropic) clear custom_providers on switch - fallback to config.yaml if cfg unavailable Signed-off-by: Xiaonuo <xiaonuo@shazhou.work>
This commit is contained in:
parent
f1c65b2c1b
commit
d736d43f72
@ -1,10 +1,12 @@
|
|||||||
import { readFileSync, writeFileSync, existsSync } from "fs";
|
import { readFileSync, writeFileSync, existsSync } from "fs";
|
||||||
|
import { execSync } from "child_process";
|
||||||
import { homedir } from "os";
|
import { homedir } from "os";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { parse, stringify } from "yaml";
|
import { parse, stringify } from "yaml";
|
||||||
|
|
||||||
const CONFIG_PATH = join(homedir(), ".hermes", "config.yaml");
|
const CONFIG_PATH = join(homedir(), ".hermes", "config.yaml");
|
||||||
const ENV_PATH = join(homedir(), ".hermes", ".env");
|
const ENV_PATH = join(homedir(), ".hermes", ".env");
|
||||||
|
const CFG_PROVIDERS_KEY = "HERMES_CUSTOM_PROVIDERS";
|
||||||
|
|
||||||
// ── Types ──────────────────────────────────────────────────────────────
|
// ── Types ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@ -72,10 +74,43 @@ function saveConfig(config: Config) {
|
|||||||
writeFileSync(CONFIG_PATH, stringify(config, { lineWidth: 120 }), "utf-8");
|
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[] {
|
function getCustomProviders(config: Config): CustomProvider[] {
|
||||||
return config.custom_providers || [];
|
return config.custom_providers || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findProviderFromRegistry(name: string): CustomProvider | undefined {
|
||||||
|
return getAllProviders().find((p) => p.name === name);
|
||||||
|
}
|
||||||
|
|
||||||
function findProvider(
|
function findProvider(
|
||||||
config: Config,
|
config: Config,
|
||||||
name: string
|
name: string
|
||||||
@ -123,42 +158,41 @@ function getAuthHeaders(provider: CustomProvider): Record<string, string> {
|
|||||||
|
|
||||||
function providers() {
|
function providers() {
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
const customProviders = getCustomProviders(config);
|
const allProviders = getAllProviders();
|
||||||
const mainProvider = config.model?.provider || "auto";
|
const mainProvider = config.model?.provider || "auto";
|
||||||
const mainModel = config.model?.default || "(not set)";
|
const mainModel = config.model?.default || "(not set)";
|
||||||
|
|
||||||
console.log(`Main: provider=${mainProvider} model=${mainModel}\n`);
|
console.log(`Main: provider=${mainProvider} model=${mainModel}\n`);
|
||||||
|
|
||||||
if (customProviders.length === 0) {
|
if (allProviders.length === 0) {
|
||||||
console.log("No custom providers configured.");
|
console.log("No custom providers configured.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Custom providers:\n");
|
console.log("Custom providers (from cfg):\n");
|
||||||
for (const p of customProviders) {
|
for (const p of allProviders) {
|
||||||
const active = p.name === mainProvider || `custom:${p.name}` === mainProvider ? " ◀ active" : "";
|
const active = p.name === mainProvider || `custom:${p.name}` === mainProvider ? " ◀ active" : "";
|
||||||
console.log(` ${p.name}${active}`);
|
console.log(` ${p.name}${active}`);
|
||||||
console.log(` url: ${p.base_url}`);
|
console.log(` url: ${p.base_url}`);
|
||||||
if (p.api_mode) console.log(` mode: ${p.api_mode}`);
|
if (p.api_mode) console.log(` mode: ${p.api_mode}`);
|
||||||
}
|
}
|
||||||
console.log(`\nTotal: ${customProviders.length}`);
|
console.log(`\nTotal: ${allProviders.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── list ────────────────────────────────────────────────────────────────
|
// ── list ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async function list(providerName?: string) {
|
async function list(providerName?: string) {
|
||||||
const config = loadConfig();
|
const allProviders = getAllProviders();
|
||||||
const customProviders = getCustomProviders(config);
|
|
||||||
|
|
||||||
const targets: CustomProvider[] = providerName
|
const targets: CustomProvider[] = providerName
|
||||||
? customProviders.filter((p) => p.name === providerName)
|
? allProviders.filter((p) => p.name === providerName)
|
||||||
: customProviders;
|
: allProviders;
|
||||||
|
|
||||||
if (targets.length === 0) {
|
if (targets.length === 0) {
|
||||||
if (providerName) {
|
if (providerName) {
|
||||||
console.error(`Provider not found: ${providerName}`);
|
console.error(`Provider not found: ${providerName}`);
|
||||||
console.error(
|
console.error(
|
||||||
`Available: ${customProviders.map((p) => p.name).join(", ")}`
|
`Available: ${allProviders.map((p) => p.name).join(", ")}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log("No custom providers configured.");
|
console.log("No custom providers configured.");
|
||||||
@ -220,12 +254,11 @@ async function list(providerName?: string) {
|
|||||||
// ── test ────────────────────────────────────────────────────────────────
|
// ── test ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async function test(providerName?: string) {
|
async function test(providerName?: string) {
|
||||||
const config = loadConfig();
|
const allProviders = getAllProviders();
|
||||||
const customProviders = getCustomProviders(config);
|
|
||||||
|
|
||||||
const targets: CustomProvider[] = providerName
|
const targets: CustomProvider[] = providerName
|
||||||
? customProviders.filter((p) => p.name === providerName)
|
? allProviders.filter((p) => p.name === providerName)
|
||||||
: customProviders;
|
: allProviders;
|
||||||
|
|
||||||
if (targets.length === 0) {
|
if (targets.length === 0) {
|
||||||
if (providerName) {
|
if (providerName) {
|
||||||
@ -322,21 +355,45 @@ function switchConfig(opts: SwitchOptions) {
|
|||||||
console.log(` auxiliary.${task} → provider=${opts.provider}${opts.model ? ` model=${opts.model}` : ""}`);
|
console.log(` auxiliary.${task} → provider=${opts.provider}${opts.model ? ` model=${opts.model}` : ""}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Switch main provider
|
// Switch main provider — look up from cfg registry
|
||||||
const providerValue = findProvider(config, opts.provider)
|
const registryProvider = findProviderFromRegistry(opts.provider);
|
||||||
? `custom:${opts.provider}`
|
|
||||||
: 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 = {};
|
if (!config.model) config.model = {};
|
||||||
config.model.provider = providerValue;
|
config.model.provider = providerValue;
|
||||||
if (opts.model) {
|
if (opts.model) {
|
||||||
config.model.default = opts.model;
|
config.model.default = opts.model;
|
||||||
}
|
}
|
||||||
console.log(` main → provider=${providerValue}${opts.model ? ` model=${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}` : ""}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveConfig(config);
|
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) {
|
async function switchTelegram(opts: SwitchOptions) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user