${configured?'Credentials configured; values are never displayed.':status==='invalid'?'Configuration was rejected. Review the fields and try again.':status==='not-configured'||status==='disabled'?'No credentials are configured for this service.':'No safe connection result was returned.'}
`;}).join('')}
Secret values, key fingerprints, and response bodies are never shown.
Your role can view safe provider status, but only an administrator can edit or test credentials.
';form?.setAttribute('hidden','');if(test)test.disabled=true;}else{form?.removeAttribute('hidden');if(test)test.disabled=false;} }
- function populateAiProviderFields(payload) { const source=payload?.nous_portal||payload?.provider_config||payload||{}; [['nousModel',source.model||source.nous_model],['nousBaseUrl',source.base_url||source.nous_base_url],['firecrawlBaseUrl',source.firecrawl_base_url||payload?.firecrawl?.base_url]].forEach(([id,value])=>{if(value!==undefined&&$(id))$(id).value=value;}); ['nousApiKey','firecrawlApiKey'].forEach(id=>{if($(id))$(id).value='';}); }
+ function populateAiProviderFields(payload) { const source=payload?.nous_portal||payload?.provider_config||payload||{}; [['nousModel',source.model||source.nous_model],['nousBaseUrl',source.base_url||source.nous_base_url],['searxngBaseUrl',source.searxng_base_url||source.firecrawl_base_url||payload?.searxng?.base_url||payload?.firecrawl?.base_url]].forEach(([id,value])=>{if(value!==undefined&&$(id))$(id).value=value;}); ['nousApiKey'].forEach(id=>{if($(id))$(id).value='';}); }
async function loadAiProviderSettings(){const state=$('aiProviderStatus');if(!state)return;setAiProviderAdminState();state.innerHTML='
Loading AI provider status…
';try{const payload=await jsonRequest('/api/v1/ai/provider-config');populateAiProviderFields(payload);renderAiProviderStatus(payload);}catch(error){if(error.message!=='unauthorized')renderAiProviderStatus({status:error.message.toLowerCase().includes('invalid')?'invalid':'error',message:error.message});}}
- async function saveAiProviderSettings(event){event.preventDefault();if(!canEditAiProviders()){message('aiProviderMessage','Administrator access is required to update provider settings.',true);return;}const form=event.currentTarget,data=Object.fromEntries(new FormData(form).entries()),body={provider:data.provider,model:data.nous_model.trim(),nous_base_url:data.nous_base_url.trim(),firecrawl_base_url:data.firecrawl_base_url.trim(),enabled:true,credentials:{}};if(data.nous_api_key)body.credentials.nous_api_key=data.nous_api_key;if(data.firecrawl_api_key)body.credentials.firecrawl_api_key=data.firecrawl_api_key;const button=$('saveAiProviderBtn');button.disabled=true;message('aiProviderMessage','Saving provider settings…');try{await jsonRequest('/api/v1/ai/provider-config',{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});form.querySelectorAll('input[type="password"]').forEach(input=>input.value='');message('aiProviderMessage','Provider settings saved. Discovery and AI work were not started.');await loadAiProviderSettings();}catch(error){if(error.message!=='unauthorized')message('aiProviderMessage',error.message||'Unable to save provider settings.',true);}finally{button.disabled=false;}}
+ async function saveAiProviderSettings(event){event.preventDefault();if(!canEditAiProviders()){message('aiProviderMessage','Administrator access is required to update provider settings.',true);return;}const form=event.currentTarget,data=Object.fromEntries(new FormData(form).entries()),body={provider:data.provider,model:data.nous_model.trim(),nous_base_url:data.nous_base_url.trim(),searxng_base_url:data.searxng_base_url.trim(),enabled:true,credentials:{}};if(data.nous_api_key)body.credentials.nous_api_key=data.nous_api_key;const button=$('saveAiProviderBtn');button.disabled=true;message('aiProviderMessage','Saving provider settings…');try{await jsonRequest('/api/v1/ai/provider-config',{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});form.querySelectorAll('input[type="password"]').forEach(input=>input.value='');message('aiProviderMessage','Provider settings saved. Discovery and AI work were not started.');await loadAiProviderSettings();}catch(error){if(error.message!=='unauthorized')message('aiProviderMessage',error.message||'Unable to save provider settings.',true);}finally{button.disabled=false;}}
async function testAiProviderConnection(){if(!canEditAiProviders())return;const button=$('testAiProviderBtn');button.disabled=true;const state=$('aiProviderStatus');state.innerHTML='
Testing provider connection…
';try{const payload=await jsonRequest('/api/v1/admin/ai-provider-config/test',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({})});renderAiProviderStatus(payload);message('aiProviderMessage',payload?.status==='invalid'?'Connection test returned an invalid configuration.':'Connection test completed; no secrets were returned.');}catch(error){if(error.message!=='unauthorized')renderAiProviderStatus({status:error.message.toLowerCase().includes('invalid')?'invalid':'error',message:error.message});}finally{button.disabled=false;}}
async function loadProviderPolicy(){const panel=$('providerPolicyPanel');if(!panel)return;panel.innerHTML='
Loading provider policy…
';try{const payload=await jsonRequest('/api/v1/outreach/provider-config');renderProviderPolicy(payload);}catch(error){if(error.message!=='unauthorized')renderProviderPolicy({status:error.message.toLowerCase().includes('auth')?'auth-required':'error',message:error.message,providers:[]});}}
function renderDetail(p){const s=scoreFor(p),st=statusOf(p),f=freshness(p),factors=p.score_factors||p.factors||[],blocked=st==='review'||st==='suppressed',stage=p.pipeline_stage||p.pipeline?.stage||(Array.isArray(p.pipeline)?p.pipeline.at(-1)?.stage:'')||'new',contacts=p.contacts||[],domains=p.domains||[],websites=p.websites||[],evidence=p.evidence||p.evidence_timeline||[],notes=p.notes||[],review=p.review_status||p.review|| (st==='review'?'Needs review':st[0].toUpperCase()+st.slice(1));$('detailPanel').innerHTML=`
View approved provider status without exposing credentials or secrets.
Sending is disabled by default. This panel is status-only. Provider configuration never creates a send trigger, and no credentials are displayed.
Sign in to load provider policy.
-
SYSTEM CONFIGURATION
AI Provider Settings
Configure the approved Nous Portal service and the internal SearXNG service used by workspace AI research.
Admin-only and write-only credentials. Keys are sent only over the authenticated API, are never displayed or stored in this browser, and updating settings does not start discovery or AI work.
PROVIDER CONFIGURATION
Service connection
Approved providers only
+
SYSTEM CONFIGURATION
AI Provider Settings
Configure the approved Nous Portal service and the internal SearXNG service used by workspace AI research.
Admin-only and write-only credentials. Keys are sent only over the authenticated API, are never displayed or stored in this browser, and updating settings does not start discovery or AI work.