@@ -51,6 +51,22 @@ export function allProviders(): ProviderDefinition[] {
5151 return Array . from ( registry . values ( ) ) ;
5252}
5353
54+ /**
55+ * Validate a raw provider config against its schema, mapping zod issues to a
56+ * readable UserError. Providers call `.parse()`-style validation through here so
57+ * config mistakes surface as `provider 'x' config invalid: field: message` lines
58+ * instead of a raw ZodError JSON dump leaking to the host's stderr.
59+ */
60+ function parseProviderConfig ( def : ProviderDefinition , rawConfig : unknown ) : unknown {
61+ const result = def . configSchema . safeParse ( rawConfig ) ;
62+ if ( result . success ) return result . data ;
63+ const details = result . error . issues . map ( ( issue ) => {
64+ const path = issue . path . join ( "." ) ;
65+ return path ? `${ path } : ${ issue . message } ` : issue . message ;
66+ } ) ;
67+ throw new UserError ( `Provider '${ def . name } ' config invalid:\n${ details . join ( "\n" ) } ` ) ;
68+ }
69+
5470export function buildProviders (
5571 providersConfig : Record < string , unknown > ,
5672 projectName ?: string ,
@@ -62,7 +78,7 @@ export function buildProviders(
6278 if ( ! def ) {
6379 throw new UserError ( `Unknown provider '${ name } '. Registered: ${ Array . from ( registry . keys ( ) ) . join ( ", " ) } ` ) ;
6480 }
65- const parsed = def . configSchema . parse ( rawConfig ) ;
81+ const parsed = parseProviderConfig ( def , rawConfig ) ;
6682 const adapter = def . createAdapter ( parsed , projectName ) ;
6783 validateProviderFacets ( def , adapter ) ;
6884 adapters . set ( name , adapter ) ;
@@ -75,11 +91,14 @@ export function buildProviders(
7591 * Environment variable mappings for each provider.
7692 * Used to construct a provider adapter without a agents.yaml config file.
7793 */
78- const PROVIDER_ENV_VARS : Record < string , Record < string , { env : string [ ] ; required : boolean } > > = {
94+ const PROVIDER_ENV_VARS : Record < string , Record < string , { env : string [ ] ; required : boolean ; placeholder ?: boolean } > > = {
7995 bailian : {
8096 api_key : { env : [ "DASHSCOPE_API_KEY" , "BAILIAN_API_KEY" ] , required : true } ,
81- workspace_id : { env : [ "BAILIAN_WORKSPACE_ID" ] , required : true } ,
82- base_url : { env : [ "BAILIAN_BASE_URL" ] , required : false } ,
97+ // Neither workspace_id nor base_url is required on its own; the config schema
98+ // enforces "at least one" so a host can supply just base_url (BAILIAN_BASE_URL).
99+ // base_url is the preferred placeholder emitted by `agents sync`.
100+ workspace_id : { env : [ "BAILIAN_WORKSPACE_ID" ] , required : false } ,
101+ base_url : { env : [ "BAILIAN_BASE_URL" ] , required : false , placeholder : true } ,
83102 } ,
84103 qoder : {
85104 api_key : { env : [ "QODER_PAT" , "QODER_API_KEY" ] , required : true } ,
@@ -97,15 +116,17 @@ const PROVIDER_ENV_VARS: Record<string, Record<string, { env: string[]; required
97116
98117/**
99118 * Build a `providers` config block for a single provider using `${ENV}`
100- * placeholders for its required fields. Used by `agents sync` to emit a providers
101- * block without leaking resolved secrets when the original file is unavailable.
119+ * placeholders. Emits fields that are either required for env resolution or
120+ * explicitly marked as the provider's preferred placeholder (e.g. bailian's
121+ * base_url). Used by `agents sync` to emit a providers block without leaking
122+ * resolved secrets when the original file is unavailable.
102123 */
103124export function placeholderProviderConfig ( providerName : string ) : Record < string , string > {
104125 const envMap = PROVIDER_ENV_VARS [ providerName ] ;
105126 if ( ! envMap ) return { } ;
106127 const out : Record < string , string > = { } ;
107- for ( const [ field , { env, required } ] of Object . entries ( envMap ) ) {
108- if ( required && env [ 0 ] ) out [ field ] = `\${${ env [ 0 ] } }` ;
128+ for ( const [ field , { env, required, placeholder } ] of Object . entries ( envMap ) ) {
129+ if ( ( required || placeholder ) && env [ 0 ] ) out [ field ] = `\${${ env [ 0 ] } }` ;
109130 }
110131 return out ;
111132}
@@ -160,7 +181,7 @@ export function buildProviderFromEnv(providerName: string, projectName?: string)
160181 }
161182
162183 const config = resolveProviderConfigFromEnv ( providerName ) ;
163- const parsed = def . configSchema . parse ( config ) ;
184+ const parsed = parseProviderConfig ( def , config ) ;
164185 const adapter = def . createAdapter ( parsed , projectName ) ;
165186 validateProviderFacets ( def , adapter ) ;
166187 return adapter ;
0 commit comments