From 4a66af87dafd4e3ca558ee232457e1927a57cfd3 Mon Sep 17 00:00:00 2001 From: redshift <213178690+1ftredsh@users.noreply.github.com> Date: Sun, 7 Jun 2026 11:13:39 +0800 Subject: [PATCH] feat(providers): add --refresh flag to providers list routstrd providers list --refresh now: - Re-fetches Nostr kind 38421 provider discovery events - Re-fetches Nostr kind 38423 routstr21 model list - Re-fetches Nostr kind 38425 review events (applies LGTM-based disable) - Fetches models from all discovered providers - Syncs fresh provider list and disabled status into the store Usage: routstrd providers list --refresh --- src/cli.ts | 10 ++++++-- src/daemon/http/index.ts | 10 ++++++++ src/daemon/index.ts | 3 ++- src/daemon/models.ts | 55 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 2da87d3..99f1514 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -743,10 +743,16 @@ const providersCmd = program providersCmd .command("list") .description("List all providers with their enabled/disabled status") - .action(async () => { + .option("--refresh", "Force re-fetch all Nostr events and refresh models from all enabled providers", false) + .action(async (options: { refresh: boolean }) => { await ensureDaemonRunning(); - const result = await callDaemon("/providers"); + const query = options.refresh ? "/providers?refresh=true" : "/providers"; + if (options.refresh) { + console.log("Force-refreshing providers from Nostr and fetching models...\n"); + } + + const result = await callDaemon(query); if (result.error) { console.log(result.error); process.exit(1); diff --git a/src/daemon/http/index.ts b/src/daemon/http/index.ts index 1ed8a3e..5f5fbc9 100644 --- a/src/daemon/http/index.ts +++ b/src/daemon/http/index.ts @@ -41,6 +41,7 @@ type DaemonDeps = { ensureProvidersBootstrapped: () => Promise; getRoutstr21Models: (forceRefresh?: boolean) => Promise; getModelProviders: (modelId: string) => Promise; + refreshProvidersAndModels: () => Promise; mode?: ClientMode; /** Nostr hex pubkey for routstr review/model events (kind 38425/38423). */ routstrPubkey?: string; @@ -294,6 +295,7 @@ export function createDaemonRequestHandler(deps: { ensureProvidersBootstrapped: () => Promise; getRoutstr21Models: (forceRefresh?: boolean) => Promise; getModelProviders: (modelId: string) => Promise; + refreshProvidersAndModels: () => Promise; mode?: "xcashu" | "apikeys"; /** Nostr hex pubkey for routstr review/model events (kind 38425/38423). */ routstrPubkey?: string; @@ -1079,6 +1081,14 @@ export function createDaemonRequestHandler(deps: { if (req.method === "GET" && url.pathname === "/providers") { try { + const forceRefresh = + url.searchParams.get("refresh")?.toLowerCase() === "true"; + + if (forceRefresh) { + logger.log("Force-refreshing providers from Nostr and fetching models..."); + await deps.refreshProvidersAndModels(); + } + const state = deps.store.getState(); const baseUrlsList: string[] = state.baseUrlsList || []; const disabledProviders: string[] = state.disabledProviders || []; diff --git a/src/daemon/index.ts b/src/daemon/index.ts index 07d26ba..c940a00 100644 --- a/src/daemon/index.ts +++ b/src/daemon/index.ts @@ -70,7 +70,7 @@ async function main(): Promise { }); // Create shared ProviderManager for consistent failure tracking across all requests const providerManager = new ProviderManager(providerRegistry, store, daemonSdkLogger); - const { ensureProvidersBootstrapped, getRoutstr21Models, getModelProviders } = + const { ensureProvidersBootstrapped, getRoutstr21Models, getModelProviders, refreshProvidersAndModels } = createModelService(modelManager, store); const walletClient = createCocodClient({ cocodPath: config.cocodPath }); @@ -123,6 +123,7 @@ async function main(): Promise { ensureProvidersBootstrapped, getRoutstr21Models, getModelProviders, + refreshProvidersAndModels, mode: config.mode || "apikeys", routstrPubkey: config.routstrPubkey, usageTrackingDriver, diff --git a/src/daemon/models.ts b/src/daemon/models.ts index abb0999..1f8a1ea 100644 --- a/src/daemon/models.ts +++ b/src/daemon/models.ts @@ -120,9 +120,64 @@ export function createModelService(modelManager: ModelManager, store: SdkStore) }; }; + /** + * Force-refresh everything: re-fetch Nostr provider discovery events, + * routstr21 model list, Nostr review events, and models from all enabled + * providers. Syncs the discovered provider list into the store. + */ + const refreshProvidersAndModels = async (): Promise => { + // Reset the bootstrap promise so we don't reuse cached results + providerBootstrapPromise = null; + + logger.log("Force-refreshing providers from Nostr..."); + + // Force-refresh provider discovery from Nostr (kind 38421) + const providers = await modelManager.bootstrapProviders(false, true); + logger.log(`Discovered ${providers.length} providers from Nostr`); + + // Force-refresh routstr21 models from Nostr (kind 38423) + const routstr21ModelIds = await modelManager.fetchRoutstr21Models(true); + logger.log(`Fetched ${routstr21ModelIds.length} routstr21 model IDs from Nostr`); + + // Force-refresh models from all providers + const models = await modelManager.fetchModels(providers, true); + logger.log(`Fetched ${models.length} models from ${providers.length} providers`); + + // Sync review events from Nostr (kind 38425) and apply disabled status + const reviewedDisabled = await modelManager.syncReviewedProvidersFromNostr( + providers, + undefined, + true, + ); + if (reviewedDisabled.length > 0) { + logger.log( + `Review sync disabled ${reviewedDisabled.length} provider(s): ${reviewedDisabled.join(", ")}`, + ); + } + + // Sync discovered providers into the store + const { baseUrlsList, setBaseUrlsList, disabledProviders, setDisabledProviders } = + store.getState() as any; + + // Replace baseUrlsList with the fresh provider list + setBaseUrlsList(providers); + + // Merge review-disabled providers into the store's disabled list + const existingDisabled = new Set(disabledProviders || []); + for (const url of reviewedDisabled) { + existingDisabled.add(url); + } + setDisabledProviders([...existingDisabled]); + + logger.log( + `Provider refresh complete: ${providers.length} total, ${existingDisabled.size} disabled`, + ); + }; + return { ensureProvidersBootstrapped, getRoutstr21Models, getModelProviders, + refreshProvidersAndModels, }; }