@@ -41,10 +41,11 @@ type Payload struct {
4141 NodeProjects []model.NodeScanResult `json:"node_projects"`
4242 BrewPkgManager * model.PkgManager `json:"brew_package_manager,omitempty"`
4343 BrewScans []model.BrewScanResult `json:"brew_scans"`
44- PythonPkgManagers []model.PkgManager `json:"python_package_managers"`
45- PythonGlobalPackages []model.PythonScanResult `json:"python_global_packages"`
46- PythonProjects []model.ProjectInfo `json:"python_projects"`
47- AIAgents []model.AITool `json:"ai_agents"`
44+ PythonPkgManagers []model.PkgManager `json:"python_package_managers"`
45+ PythonGlobalPackages []model.PythonScanResult `json:"python_global_packages"`
46+ PythonProjects []model.ProjectInfo `json:"python_projects"`
47+ SystemPackageScans []model.SystemPackageScanResult `json:"system_package_scans"`
48+ AIAgents []model.AITool `json:"ai_agents"`
4849 MCPConfigs []model.MCPConfigEnterprise `json:"mcp_configs"`
4950
5051 ExecutionLogs * ExecutionLogs `json:"execution_logs,omitempty"`
@@ -68,6 +69,7 @@ type PerformanceMetrics struct {
6869 BrewCasksCount int `json:"brew_casks_count"`
6970 PythonGlobalPkgsCount int `json:"python_global_packages_count"`
7071 PythonProjectsCount int `json:"python_projects_count"`
72+ SystemPackagesCount int `json:"system_packages_count"`
7173}
7274
7375// Run executes enterprise telemetry: scan, build payload, upload to S3.
@@ -287,6 +289,67 @@ func Run(exec executor.Executor, log *progress.Logger, cfg *cli.Config) error {
287289 fmt .Fprintln (os .Stderr )
288290 }
289291
292+ // System package scanning (Linux only — rpm, dpkg, pacman, apk, snap, flatpak)
293+ var systemPackageScans []model.SystemPackageScanResult
294+
295+ if exec .GOOS () == model .PlatformLinux {
296+ log .Progress ("Detecting system packages..." )
297+ sysPkgDetector := detector .NewSystemPkgDetector (userExec )
298+
299+ // Primary system PM (rpm, dpkg, pacman, or apk)
300+ if pm := sysPkgDetector .Detect (ctx ); pm != nil {
301+ log .Progress (" Found: %s v%s at %s" , pm .Name , pm .Version , pm .Path )
302+ start := time .Now ()
303+ packages := sysPkgDetector .ListPackages (ctx )
304+ duration := time .Since (start ).Milliseconds ()
305+ if packages == nil {
306+ packages = []model.SystemPackage {}
307+ }
308+ systemPackageScans = append (systemPackageScans , model.SystemPackageScanResult {
309+ ScanType : pm .Name ,
310+ PackageManager : pm ,
311+ Packages : packages ,
312+ PackagesCount : len (packages ),
313+ ScanDurationMs : duration ,
314+ })
315+ log .Progress (" %s: %d packages in %dms" , pm .Name , len (packages ), duration )
316+ }
317+
318+ // Additional PMs (snap, flatpak) — coexist with system PM
319+ for _ , mgr := range sysPkgDetector .DetectAdditionalManagers (ctx ) {
320+ mgr := mgr
321+ log .Progress (" Found: %s v%s at %s" , mgr .Name , mgr .Version , mgr .Path )
322+ start := time .Now ()
323+ var packages []model.SystemPackage
324+ switch mgr .Name {
325+ case "snap" :
326+ packages = sysPkgDetector .ListSnapPackages (ctx )
327+ case "flatpak" :
328+ packages = sysPkgDetector .ListFlatpakPackages (ctx )
329+ }
330+ duration := time .Since (start ).Milliseconds ()
331+ if packages == nil {
332+ packages = []model.SystemPackage {}
333+ }
334+ systemPackageScans = append (systemPackageScans , model.SystemPackageScanResult {
335+ ScanType : mgr .Name ,
336+ PackageManager : & mgr ,
337+ Packages : packages ,
338+ PackagesCount : len (packages ),
339+ ScanDurationMs : duration ,
340+ })
341+ log .Progress (" %s: %d packages in %dms" , mgr .Name , len (packages ), duration )
342+ }
343+
344+ if len (systemPackageScans ) == 0 {
345+ log .Progress (" No system package managers found" )
346+ }
347+ fmt .Fprintln (os .Stderr )
348+ } else {
349+ log .Progress ("System package scanning: skipped (non-Linux)" )
350+ fmt .Fprintln (os .Stderr )
351+ }
352+
290353 // Node.js scanning
291354 npmEnabled := true
292355 if cfg .EnableNPMScan != nil {
@@ -345,6 +408,9 @@ func Run(exec executor.Executor, log *progress.Logger, cfg *cli.Config) error {
345408 if pythonProjects == nil {
346409 pythonProjects = []model.ProjectInfo {}
347410 }
411+ if systemPackageScans == nil {
412+ systemPackageScans = []model.SystemPackageScanResult {}
413+ }
348414
349415 // Finalize execution logs before building payload
350416 execLogsBase64 := capture .Finalize ()
@@ -373,6 +439,7 @@ func Run(exec executor.Executor, log *progress.Logger, cfg *cli.Config) error {
373439 PythonPkgManagers : pythonPkgManagers ,
374440 PythonGlobalPackages : pythonGlobalPkgs ,
375441 PythonProjects : pythonProjects ,
442+ SystemPackageScans : systemPackageScans ,
376443 AIAgents : allAI ,
377444 MCPConfigs : mcpConfigs ,
378445
@@ -393,6 +460,7 @@ func Run(exec executor.Executor, log *progress.Logger, cfg *cli.Config) error {
393460 BrewCasksCount : brewCasksCount (brewScans ),
394461 PythonGlobalPkgsCount : len (pythonGlobalPkgs ),
395462 PythonProjectsCount : len (pythonProjects ),
463+ SystemPackagesCount : totalSystemPackagesCount (systemPackageScans ),
396464 },
397465 }
398466
@@ -425,6 +493,14 @@ func brewCasksCount(scans []model.BrewScanResult) int {
425493 return 0
426494}
427495
496+ func totalSystemPackagesCount (scans []model.SystemPackageScanResult ) int {
497+ total := 0
498+ for _ , s := range scans {
499+ total += s .PackagesCount
500+ }
501+ return total
502+ }
503+
428504func uploadToS3 (ctx context.Context , log * progress.Logger , payload * Payload ) error {
429505 payloadJSON , err := json .Marshal (payload )
430506 if err != nil {
0 commit comments