@@ -28,9 +28,17 @@ type EngineConfig struct {
2828 Agent string // Agent identifier for copilot --agent flag (copilot engine only)
2929
3030 // Inline definition fields (populated when engine.runtime is specified in frontmatter)
31- IsInlineDefinition bool // true when the engine is defined inline via engine.runtime + optional engine.provider
32- InlineProviderID string // engine.provider.id (e.g. "openai", "anthropic")
33- InlineProviderSecret string // engine.provider.auth.secret (name of the GitHub Actions secret for the provider API key)
31+ IsInlineDefinition bool // true when the engine is defined inline via engine.runtime + optional engine.provider
32+ InlineProviderID string // engine.provider.id (e.g. "openai", "anthropic")
33+ // Deprecated: Use InlineProviderAuth instead. Kept for backwards compatibility when only
34+ // engine.provider.auth.secret is specified without a strategy.
35+ InlineProviderSecret string // engine.provider.auth.secret (backwards compat: simple API key secret name)
36+
37+ // Extended inline auth fields (engine.provider.auth.* beyond the simple secret)
38+ InlineProviderAuth * AuthDefinition // full auth definition parsed from engine.provider.auth
39+
40+ // Extended inline request shaping fields (engine.provider.request.*)
41+ InlineProviderRequest * RequestShape // request shaping parsed from engine.provider.request
3442}
3543
3644// NetworkPermissions represents network access permissions for workflow execution
@@ -118,11 +126,25 @@ func (c *Compiler) ExtractEngineConfig(frontmatter map[string]any) (string, *Eng
118126 }
119127 if auth , hasAuth := providerObj ["auth" ]; hasAuth {
120128 if authObj , ok := auth .(map [string ]any ); ok {
121- if secret , ok := authObj ["secret" ].(string ); ok {
122- config .InlineProviderSecret = secret
129+ authDef := parseAuthDefinition (authObj )
130+ // Only store an AuthDefinition when the user actually provided
131+ // at least one recognised field. An empty map (e.g. `auth: {}`)
132+ // must not be treated as an explicit auth override.
133+ if authDef .Strategy != "" || authDef .Secret != "" ||
134+ authDef .TokenURL != "" || authDef .ClientIDRef != "" ||
135+ authDef .ClientSecretRef != "" || authDef .HeaderName != "" ||
136+ authDef .TokenField != "" {
137+ config .InlineProviderAuth = authDef
138+ // Backwards compat: expose the simple secret field directly.
139+ config .InlineProviderSecret = authDef .Secret
123140 }
124141 }
125142 }
143+ if request , hasRequest := providerObj ["request" ]; hasRequest {
144+ if requestObj , ok := request .(map [string ]any ); ok {
145+ config .InlineProviderRequest = parseRequestShape (requestObj )
146+ }
147+ }
126148 }
127149 }
128150
@@ -351,3 +373,58 @@ func (c *Compiler) extractEngineConfigFromJSON(engineJSON string) (*EngineConfig
351373 _ , config := c .ExtractEngineConfig (tempFrontmatter )
352374 return config , nil
353375}
376+
377+ // parseAuthDefinition converts a raw auth config map (from engine.provider.auth) into
378+ // an AuthDefinition. It is backward-compatible: a map with only a "secret" key produces
379+ // an AuthDefinition with Strategy="" and Secret set (callers normalise Strategy to api-key).
380+ func parseAuthDefinition (authObj map [string ]any ) * AuthDefinition {
381+ def := & AuthDefinition {}
382+ if s , ok := authObj ["strategy" ].(string ); ok {
383+ def .Strategy = AuthStrategy (s )
384+ }
385+ if s , ok := authObj ["secret" ].(string ); ok {
386+ def .Secret = s
387+ }
388+ if s , ok := authObj ["token-url" ].(string ); ok {
389+ def .TokenURL = s
390+ }
391+ if s , ok := authObj ["client-id" ].(string ); ok {
392+ def .ClientIDRef = s
393+ }
394+ if s , ok := authObj ["client-secret" ].(string ); ok {
395+ def .ClientSecretRef = s
396+ }
397+ if s , ok := authObj ["token-field" ].(string ); ok {
398+ def .TokenField = s
399+ }
400+ if s , ok := authObj ["header-name" ].(string ); ok {
401+ def .HeaderName = s
402+ }
403+ return def
404+ }
405+
406+ // parseRequestShape converts a raw request config map (from engine.provider.request) into
407+ // a RequestShape.
408+ func parseRequestShape (requestObj map [string ]any ) * RequestShape {
409+ shape := & RequestShape {}
410+ if s , ok := requestObj ["path-template" ].(string ); ok {
411+ shape .PathTemplate = s
412+ }
413+ if q , ok := requestObj ["query" ].(map [string ]any ); ok {
414+ shape .Query = make (map [string ]string , len (q ))
415+ for k , v := range q {
416+ if vs , ok := v .(string ); ok {
417+ shape .Query [k ] = vs
418+ }
419+ }
420+ }
421+ if b , ok := requestObj ["body-inject" ].(map [string ]any ); ok {
422+ shape .BodyInject = make (map [string ]string , len (b ))
423+ for k , v := range b {
424+ if vs , ok := v .(string ); ok {
425+ shape .BodyInject [k ] = vs
426+ }
427+ }
428+ }
429+ return shape
430+ }
0 commit comments