@@ -9,6 +9,8 @@ defmodule Codex.Plugins.Manifest do
99 alias Codex.Plugins.Paths
1010 alias Codex.Schema
1111
12+ @ max_default_prompt_count 3
13+ @ max_default_prompt_length 128
1214 @ key_mapping % {
1315 "mcp_servers" => "mcpServers" ,
1416 "display_name" => "displayName" ,
@@ -135,7 +137,7 @@ defmodule Codex.Plugins.Manifest do
135137
136138 def parse ( data ) do
137139 data
138- |> normalize_input ( )
140+ |> Schema . normalize_input ( @ key_mapping )
139141 |> then ( & Schema . parse ( schema ( ) , & 1 , :invalid_plugin_manifest ) )
140142 |> project ( )
141143 end
@@ -163,19 +165,19 @@ defmodule Codex.Plugins.Manifest do
163165 @ spec to_map ( t ( ) ) :: map ( )
164166 def to_map ( % __MODULE__ { } = value ) do
165167 % { }
166- |> maybe_put ( "name" , value . name )
167- |> maybe_put ( "version" , value . version )
168- |> maybe_put ( "description" , value . description )
169- |> maybe_put ( "author" , encode_author ( value . author ) )
170- |> maybe_put ( "homepage" , value . homepage )
171- |> maybe_put ( "repository" , value . repository )
172- |> maybe_put ( "license" , value . license )
173- |> maybe_put ( "keywords" , value . keywords )
174- |> maybe_put ( "skills" , value . skills )
175- |> maybe_put ( "hooks" , value . hooks )
176- |> maybe_put ( "mcpServers" , value . mcp_servers )
177- |> maybe_put ( "apps" , value . apps )
178- |> maybe_put ( "interface" , encode_interface ( value . interface ) )
168+ |> Schema . put_present ( "name" , value . name )
169+ |> Schema . put_present ( "version" , value . version )
170+ |> Schema . put_present ( "description" , value . description )
171+ |> Schema . put_present ( "author" , encode_author ( value . author ) )
172+ |> Schema . put_present ( "homepage" , value . homepage )
173+ |> Schema . put_present ( "repository" , value . repository )
174+ |> Schema . put_present ( "license" , value . license )
175+ |> Schema . put_present ( "keywords" , value . keywords )
176+ |> Schema . put_present ( "skills" , value . skills )
177+ |> Schema . put_present ( "hooks" , value . hooks )
178+ |> Schema . put_present ( "mcpServers" , value . mcp_servers )
179+ |> Schema . put_present ( "apps" , value . apps )
180+ |> Schema . put_present ( "interface" , encode_interface ( value . interface ) )
179181 |> Schema . merge_extra ( value . extra )
180182 end
181183
@@ -209,30 +211,27 @@ defmodule Codex.Plugins.Manifest do
209211 @ spec normalize_default_prompt ( term ( ) , keyword ( ) ) ::
210212 { :ok , [ String . t ( ) ] } | { :error , String . t ( ) }
211213 def normalize_default_prompt ( value , _opts ) when is_binary ( value ) do
212- prompt = String . trim ( value )
213-
214- if prompt == "" do
215- { :error , "expected a non-empty prompt string" }
216- else
214+ with { :ok , prompt } <- normalize_prompt_string ( value ) do
217215 { :ok , [ prompt ] }
218216 end
219217 end
220218
221219 def normalize_default_prompt ( values , _opts ) when is_list ( values ) do
222- values
223- |> Enum . reduce_while ( { :ok , [ ] } , fn
224- value , { :ok , acc } when is_binary ( value ) ->
225- prompt = String . trim ( value )
226-
227- if prompt == "" do
228- { :halt , { :error , "expected non-empty prompt strings" } }
229- else
230- { :cont , { :ok , acc ++ [ prompt ] } }
231- end
232-
233- _value , _acc ->
234- { :halt , { :error , "expected a string or a list of strings" } }
235- end )
220+ if length ( values ) > @ max_default_prompt_count do
221+ { :error , "expected at most #{ @ max_default_prompt_count } prompts" }
222+ else
223+ values
224+ |> Enum . reduce_while ( { :ok , [ ] } , fn
225+ value , { :ok , acc } when is_binary ( value ) ->
226+ case normalize_prompt_string ( value ) do
227+ { :ok , prompt } -> { :cont , { :ok , acc ++ [ prompt ] } }
228+ { :error , reason } -> { :halt , { :error , reason } }
229+ end
230+
231+ _value , _acc ->
232+ { :halt , { :error , "expected a string or a list of strings" } }
233+ end )
234+ end
236235 end
237236
238237 def normalize_default_prompt ( _value , _opts ) ,
@@ -307,30 +306,30 @@ defmodule Codex.Plugins.Manifest do
307306
308307 defp encode_author ( author ) do
309308 % { }
310- |> maybe_put ( "name" , author [ :name ] )
311- |> maybe_put ( "email" , author [ :email ] )
312- |> maybe_put ( "url" , author [ :url ] )
309+ |> Schema . put_present ( "name" , author [ :name ] )
310+ |> Schema . put_present ( "email" , author [ :email ] )
311+ |> Schema . put_present ( "url" , author [ :url ] )
313312 |> Schema . merge_extra ( author [ :extra ] || % { } )
314313 end
315314
316315 defp encode_interface ( nil ) , do: nil
317316
318317 defp encode_interface ( interface ) do
319318 % { }
320- |> maybe_put ( "displayName" , interface [ :display_name ] )
321- |> maybe_put ( "shortDescription" , interface [ :short_description ] )
322- |> maybe_put ( "longDescription" , interface [ :long_description ] )
323- |> maybe_put ( "developerName" , interface [ :developer_name ] )
324- |> maybe_put ( "category" , interface [ :category ] )
325- |> maybe_put ( "capabilities" , interface [ :capabilities ] )
326- |> maybe_put ( "websiteURL" , interface [ :website_url ] )
327- |> maybe_put ( "privacyPolicyURL" , interface [ :privacy_policy_url ] )
328- |> maybe_put ( "termsOfServiceURL" , interface [ :terms_of_service_url ] )
329- |> maybe_put ( "defaultPrompt" , interface [ :default_prompt ] )
330- |> maybe_put ( "brandColor" , interface [ :brand_color ] )
331- |> maybe_put ( "composerIcon" , interface [ :composer_icon ] )
332- |> maybe_put ( "logo" , interface [ :logo ] )
333- |> maybe_put ( "screenshots" , interface [ :screenshots ] )
319+ |> Schema . put_present ( "displayName" , interface [ :display_name ] )
320+ |> Schema . put_present ( "shortDescription" , interface [ :short_description ] )
321+ |> Schema . put_present ( "longDescription" , interface [ :long_description ] )
322+ |> Schema . put_present ( "developerName" , interface [ :developer_name ] )
323+ |> Schema . put_present ( "category" , interface [ :category ] )
324+ |> Schema . put_present ( "capabilities" , interface [ :capabilities ] )
325+ |> Schema . put_present ( "websiteURL" , interface [ :website_url ] )
326+ |> Schema . put_present ( "privacyPolicyURL" , interface [ :privacy_policy_url ] )
327+ |> Schema . put_present ( "termsOfServiceURL" , interface [ :terms_of_service_url ] )
328+ |> Schema . put_present ( "defaultPrompt" , interface [ :default_prompt ] )
329+ |> Schema . put_present ( "brandColor" , interface [ :brand_color ] )
330+ |> Schema . put_present ( "composerIcon" , interface [ :composer_icon ] )
331+ |> Schema . put_present ( "logo" , interface [ :logo ] )
332+ |> Schema . put_present ( "screenshots" , interface [ :screenshots ] )
334333 |> Schema . merge_extra ( interface [ :extra ] || % { } )
335334 end
336335
@@ -410,48 +409,24 @@ defmodule Codex.Plugins.Manifest do
410409 )
411410 end
412411
413- defp normalize_input ( nil ) , do: % { }
414-
415- defp normalize_input ( list ) when is_list ( list ) do
416- if list != [ ] and Keyword . keyword? ( list ) do
417- list
418- |> Enum . into ( % { } )
419- |> normalize_input ( )
420- else
421- list
422- end
423- end
424-
425- defp normalize_input ( % { } = value ) do
426- value
427- |> Enum . map ( fn { key , nested_value } ->
428- { normalize_key ( key ) , normalize_value ( nested_value ) }
429- end )
430- |> Map . new ( )
431- end
432-
433- defp normalize_input ( value ) , do: value
412+ defp normalize_prompt_string ( value ) when is_binary ( value ) do
413+ prompt =
414+ value
415+ |> String . split ( )
416+ |> Enum . join ( " " )
434417
435- defp normalize_key ( key ) when is_atom ( key ) , do: key |> Atom . to_string ( ) |> normalize_key ( )
436- defp normalize_key ( key ) when is_binary ( key ) , do: Map . get ( @ key_mapping , key , key )
418+ cond do
419+ prompt == "" ->
420+ { :error , "expected a non-empty prompt string" }
437421
438- defp normalize_value ( % { } = value ) , do: normalize_input ( value )
422+ String . length ( prompt ) > @ max_default_prompt_length ->
423+ { :error , "expected prompts at most #{ @ max_default_prompt_length } characters" }
439424
440- defp normalize_value ( values ) when is_list ( values ) do
441- if values != [ ] and Keyword . keyword? ( values ) do
442- values
443- |> Enum . into ( % { } )
444- |> normalize_input ( )
445- else
446- Enum . map ( values , & normalize_value / 1 )
425+ true ->
426+ { :ok , prompt }
447427 end
448428 end
449429
450- defp normalize_value ( value ) , do: value
451-
452- defp maybe_put ( map , _key , nil ) , do: map
453- defp maybe_put ( map , key , value ) , do: Map . put ( map , key , value )
454-
455430 defp author_empty? ( author ) do
456431 Enum . all? ( [ author [ :name ] , author [ :email ] , author [ :url ] ] , & is_nil / 1 ) and author [ :extra ] == % { }
457432 end
0 commit comments