provenance.go
1 package skills 2 3 import ( 4 "encoding/json" 5 "os" 6 "path/filepath" 7 ) 8 9 const ( 10 InstallSourceMarketplace = "marketplace" 11 InstallSourceLocal = "local" 12 InstallSourceBundled = "bundled" 13 14 marketplaceProvenanceFile = ".marketplace.json" 15 ) 16 17 type marketplaceProvenance struct { 18 MarketplaceSlug string `json:"marketplace_slug"` 19 } 20 21 func installProvenanceForSkill(source, skillDir string) (string, string) { 22 switch source { 23 case SourceBundled: 24 return InstallSourceBundled, "" 25 case SourceGlobal: 26 if slug, ok := readMarketplaceProvenance(skillDir); ok { 27 return InstallSourceMarketplace, slug 28 } 29 return InstallSourceLocal, "" 30 default: 31 return "", "" 32 } 33 } 34 35 func readMarketplaceProvenance(skillDir string) (string, bool) { 36 data, err := os.ReadFile(filepath.Join(skillDir, marketplaceProvenanceFile)) 37 if err != nil { 38 return "", false 39 } 40 41 var provenance marketplaceProvenance 42 if err := json.Unmarshal(data, &provenance); err != nil { 43 return "", false 44 } 45 if err := ValidateSkillName(provenance.MarketplaceSlug); err != nil { 46 return "", false 47 } 48 if provenance.MarketplaceSlug != filepath.Base(skillDir) { 49 return "", false 50 } 51 return provenance.MarketplaceSlug, true 52 } 53 54 func writeMarketplaceProvenance(skillDir, slug string) error { 55 data, err := json.Marshal(marketplaceProvenance{MarketplaceSlug: slug}) 56 if err != nil { 57 return err 58 } 59 return atomicWrite(filepath.Join(skillDir, marketplaceProvenanceFile), data) 60 } 61 62 func clearMarketplaceProvenance(skillDir string) error { 63 err := os.Remove(filepath.Join(skillDir, marketplaceProvenanceFile)) 64 if err == nil || os.IsNotExist(err) { 65 return nil 66 } 67 return err 68 }