@@ -2,32 +2,177 @@ package cmd
22
33import (
44 "fmt"
5+ "strings"
56
7+ "github.com/idlab-discover/AIBoMGen-cli/internal/completeness"
8+ "github.com/idlab-discover/AIBoMGen-cli/internal/enricher"
9+ "github.com/idlab-discover/AIBoMGen-cli/internal/fetcher"
10+ bomio "github.com/idlab-discover/AIBoMGen-cli/internal/io"
11+ "github.com/idlab-discover/AIBoMGen-cli/internal/metadata"
612 "github.com/spf13/cobra"
13+ "github.com/spf13/viper"
714)
815
916// enrichCmd represents the enrich command
1017var enrichCmd = & cobra.Command {
1118 Use : "enrich" ,
1219 Short : "Enrich an existing AIBOM with additional metadata" ,
13- Long : `Enrich an existing AIBOM with additional metadata by prompting the user for inputs on empty or missing fields.` ,
20+ Long : `Enrich an existing AIBOM with additional metadata through interactive prompts
21+ or by loading values from a configuration file. Optionally refetch model metadata
22+ from Hugging Face API and README before enrichment.` ,
1423 RunE : func (cmd * cobra.Command , args []string ) error {
15- fmt .Println ("Not implemented" )
24+ // Validate strategy
25+ strategy := strings .ToLower (strings .TrimSpace (enrichStrategy ))
26+ if strategy == "" {
27+ strategy = "interactive"
28+ }
29+ switch strategy {
30+ case "interactive" , "file" :
31+ // ok
32+ default :
33+ return fmt .Errorf ("invalid --strategy %q (expected interactive|file)" , enrichStrategy )
34+ }
35+
36+ // Validate log level
37+ level := strings .ToLower (strings .TrimSpace (enrichLogLevel ))
38+ if level == "" {
39+ level = "standard"
40+ }
41+ switch level {
42+ case "quiet" , "standard" , "debug" :
43+ // ok
44+ default :
45+ return fmt .Errorf ("invalid --log-level %q (expected quiet|standard|debug)" , enrichLogLevel )
46+ }
47+
48+ // Wire internal package logging
49+ if level != "quiet" {
50+ lw := cmd .ErrOrStderr ()
51+ enricher .SetLogger (lw )
52+ completeness .SetLogger (lw )
53+ fetcher .SetLogger (lw )
54+ if level == "debug" {
55+ metadata .SetLogger (lw )
56+ }
57+ }
58+
59+ // Read existing BOM
60+ bom , err := bomio .ReadBOM (enrichInput , enrichInputFormat )
61+ if err != nil {
62+ return fmt .Errorf ("failed to read input BOM: %w" , err )
63+ }
64+
65+ // Determine output path
66+ outPath := enrichOutput
67+ if outPath == "" {
68+ outPath = enrichInput // overwrite by default
69+ }
70+
71+ // Determine spec version (default to same version as input)
72+ // Note: if not specified, WriteBOM will use the BOM's existing spec version
73+ specVersion := strings .TrimSpace (enrichSpecVersion )
74+
75+ // Build enricher configuration
76+ cfg := enricher.Config {
77+ Strategy : strategy ,
78+ ConfigFile : enrichConfigFile ,
79+ RequiredOnly : enrichRequiredOnly ,
80+ MinWeight : enrichMinWeight ,
81+ Refetch : enrichRefetch ,
82+ NoPreview : enrichNoPreview ,
83+ SpecVersion : specVersion ,
84+ HFToken : enrichHFToken ,
85+ HFBaseURL : enrichHFBaseURL ,
86+ HFTimeout : enrichHFTimeout ,
87+ }
88+
89+ // Load config file values if using file strategy
90+ var fileValues map [string ]interface {}
91+ if strategy == "file" {
92+ if enrichConfigFile == "" {
93+ return fmt .Errorf ("--file is required when using --strategy file" )
94+ }
95+ fileValues , err = loadEnrichmentConfig (enrichConfigFile )
96+ if err != nil {
97+ return fmt .Errorf ("failed to load config file: %w" , err )
98+ }
99+ }
100+
101+ // Create enricher
102+ e := enricher .New (enricher.Options {
103+ Reader : cmd .InOrStdin (),
104+ Writer : cmd .OutOrStdout (),
105+ Config : cfg ,
106+ })
107+
108+ // Run enrichment
109+ enriched , err := e .Enrich (bom , fileValues )
110+ if err != nil {
111+ return fmt .Errorf ("enrichment failed: %w" , err )
112+ }
113+
114+ // Write output
115+ if err := bomio .WriteBOM (enriched , outPath , enrichOutputFormat , specVersion ); err != nil {
116+ return fmt .Errorf ("failed to write output: %w" , err )
117+ }
118+
119+ if level != "quiet" {
120+ fmt .Fprintf (cmd .OutOrStdout (), "\n ✓ Enriched BOM saved to %s\n " , outPath )
121+ }
122+
16123 return nil
17124 },
18125}
19126
20127var (
21128 enrichInput string
129+ enrichInputFormat string
22130 enrichOutput string
23131 enrichOutputFormat string
24132 enrichSpecVersion string
25- enrichQuiet bool
133+ enrichStrategy string
134+ enrichConfigFile string
135+ enrichRequiredOnly bool
136+ enrichMinWeight float64
137+ enrichRefetch bool
138+ enrichNoPreview bool
139+ enrichLogLevel string
140+ enrichHFToken string
141+ enrichHFBaseURL string
142+ enrichHFTimeout int
26143)
27144
28145func init () {
29- enrichCmd .Flags ().StringVarP (& enrichInput , "input" , "i" , "" , "Path to existing AIBOM JSON (required)" )
146+ enrichCmd .Flags ().StringVarP (& enrichInput , "input" , "i" , "" , "Path to existing AIBOM (required)" )
30147 enrichCmd .Flags ().StringVarP (& enrichOutput , "output" , "o" , "" , "Output file path (default: overwrite input)" )
31- enrichCmd .Flags ().StringVarP (& enrichOutputFormat , "format" , "f" , "auto" , "Output BOM format: json|xml|auto (default: auto)" )
32- enrichCmd .Flags ().StringVar (& enrichSpecVersion , "spec" , "" , "CycloneDX spec version for output (e.g., 1.3, 1.4, 1.5, 1.6)" )
148+ enrichCmd .Flags ().StringVarP (& enrichInputFormat , "format" , "f" , "auto" , "Input BOM format: json|xml|auto" )
149+ enrichCmd .Flags ().StringVar (& enrichOutputFormat , "output-format" , "auto" , "Output BOM format: json|xml|auto" )
150+ enrichCmd .Flags ().StringVar (& enrichSpecVersion , "spec" , "" , "CycloneDX spec version for output (default: same as input)" )
151+
152+ enrichCmd .Flags ().StringVar (& enrichStrategy , "strategy" , "interactive" , "Enrichment strategy: interactive|file" )
153+ enrichCmd .Flags ().StringVar (& enrichConfigFile , "file" , "" , "Path to enrichment config file (YAML)" )
154+ enrichCmd .Flags ().BoolVar (& enrichRequiredOnly , "required-only" , false , "Only prompt for required fields" )
155+ enrichCmd .Flags ().Float64Var (& enrichMinWeight , "min-weight" , 0.0 , "Only prompt for fields with weight >= this value" )
156+ enrichCmd .Flags ().BoolVar (& enrichRefetch , "refetch" , false , "Refetch model metadata from Hugging Face before enrichment" )
157+ enrichCmd .Flags ().BoolVar (& enrichNoPreview , "no-preview" , false , "Skip preview before saving" )
158+
159+ enrichCmd .Flags ().StringVar (& enrichLogLevel , "log-level" , "standard" , "Log level: quiet|standard|debug" )
160+ enrichCmd .Flags ().StringVar (& enrichHFToken , "hf-token" , "" , "Hugging Face API token (for refetch)" )
161+ enrichCmd .Flags ().StringVar (& enrichHFBaseURL , "hf-base-url" , "" , "Hugging Face base URL (for refetch)" )
162+ enrichCmd .Flags ().IntVar (& enrichHFTimeout , "hf-timeout" , 30 , "Hugging Face API timeout in seconds (for refetch)" )
163+
164+ _ = enrichCmd .MarkFlagRequired ("input" )
165+ }
166+
167+ // loadEnrichmentConfig loads enrichment values from a YAML config file
168+ func loadEnrichmentConfig (path string ) (map [string ]interface {}, error ) {
169+ v := viper .New ()
170+ v .SetConfigFile (path )
171+ v .SetConfigType ("yaml" )
172+
173+ if err := v .ReadInConfig (); err != nil {
174+ return nil , err
175+ }
176+
177+ return v .AllSettings (), nil
33178}
0 commit comments