arch-ops-server 3.1.0__py3-none-any.whl → 3.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arch_ops_server/__init__.py +3 -1
- arch_ops_server/server.py +114 -41
- arch_ops_server/system_health_check.py +189 -0
- arch_ops_server/tool_metadata.py +19 -0
- {arch_ops_server-3.1.0.dist-info → arch_ops_server-3.2.0.dist-info}/METADATA +27 -7
- {arch_ops_server-3.1.0.dist-info → arch_ops_server-3.2.0.dist-info}/RECORD +8 -7
- {arch_ops_server-3.1.0.dist-info → arch_ops_server-3.2.0.dist-info}/WHEEL +2 -2
- {arch_ops_server-3.1.0.dist-info → arch_ops_server-3.2.0.dist-info}/entry_points.txt +0 -0
arch_ops_server/__init__.py
CHANGED
|
@@ -6,7 +6,7 @@ A Model Context Protocol server that bridges AI assistants with the Arch Linux
|
|
|
6
6
|
ecosystem, providing access to the Arch Wiki, AUR, and official repositories.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "3.
|
|
9
|
+
__version__ = "3.2.0"
|
|
10
10
|
|
|
11
11
|
from .wiki import search_wiki, get_wiki_page, get_wiki_page_as_text
|
|
12
12
|
from .aur import (
|
|
@@ -43,6 +43,7 @@ from .system import (
|
|
|
43
43
|
check_failed_services,
|
|
44
44
|
get_boot_logs
|
|
45
45
|
)
|
|
46
|
+
from .system_health_check import run_system_health_check
|
|
46
47
|
from .news import (
|
|
47
48
|
get_latest_news,
|
|
48
49
|
check_critical_news,
|
|
@@ -147,6 +148,7 @@ __all__ = [
|
|
|
147
148
|
"get_pacman_cache_stats",
|
|
148
149
|
"check_failed_services",
|
|
149
150
|
"get_boot_logs",
|
|
151
|
+
"run_system_health_check",
|
|
150
152
|
# News
|
|
151
153
|
"get_latest_news",
|
|
152
154
|
"check_critical_news",
|
arch_ops_server/server.py
CHANGED
|
@@ -15,6 +15,7 @@ from mcp.server import Server
|
|
|
15
15
|
from mcp.types import (
|
|
16
16
|
Resource,
|
|
17
17
|
Tool,
|
|
18
|
+
ToolAnnotations,
|
|
18
19
|
TextContent,
|
|
19
20
|
ImageContent,
|
|
20
21
|
EmbeddedResource,
|
|
@@ -76,6 +77,8 @@ from . import (
|
|
|
76
77
|
analyze_makepkg_conf,
|
|
77
78
|
check_ignored_packages,
|
|
78
79
|
get_parallel_downloads_setting,
|
|
80
|
+
# System health check
|
|
81
|
+
run_system_health_check,
|
|
79
82
|
# Utils
|
|
80
83
|
IS_ARCH,
|
|
81
84
|
run_command,
|
|
@@ -302,6 +305,13 @@ async def list_resources() -> list[Resource]:
|
|
|
302
305
|
mimeType="application/json",
|
|
303
306
|
description="Check when package databases were last synchronized"
|
|
304
307
|
),
|
|
308
|
+
# System health resources
|
|
309
|
+
Resource(
|
|
310
|
+
uri="system://health",
|
|
311
|
+
name="System - Health Check",
|
|
312
|
+
mimeType="application/json",
|
|
313
|
+
description="Comprehensive system health check report"
|
|
314
|
+
),
|
|
305
315
|
]
|
|
306
316
|
|
|
307
317
|
|
|
@@ -482,6 +492,11 @@ async def read_resource(uri: str) -> str:
|
|
|
482
492
|
else:
|
|
483
493
|
raise ValueError(result.get("error", "Failed to get boot logs"))
|
|
484
494
|
|
|
495
|
+
elif resource_path == "health":
|
|
496
|
+
# Get system health check
|
|
497
|
+
result = await run_system_health_check()
|
|
498
|
+
return json.dumps(result, indent=2)
|
|
499
|
+
|
|
485
500
|
else:
|
|
486
501
|
raise ValueError(f"Unsupported system resource: {resource_path}")
|
|
487
502
|
|
|
@@ -579,7 +594,8 @@ async def list_tools() -> list[Tool]:
|
|
|
579
594
|
}
|
|
580
595
|
},
|
|
581
596
|
"required": ["query"]
|
|
582
|
-
}
|
|
597
|
+
},
|
|
598
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
583
599
|
),
|
|
584
600
|
|
|
585
601
|
# AUR tools
|
|
@@ -606,7 +622,8 @@ async def list_tools() -> list[Tool]:
|
|
|
606
622
|
}
|
|
607
623
|
},
|
|
608
624
|
"required": ["query"]
|
|
609
|
-
}
|
|
625
|
+
},
|
|
626
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
610
627
|
),
|
|
611
628
|
|
|
612
629
|
Tool(
|
|
@@ -621,7 +638,8 @@ async def list_tools() -> list[Tool]:
|
|
|
621
638
|
}
|
|
622
639
|
},
|
|
623
640
|
"required": ["package_name"]
|
|
624
|
-
}
|
|
641
|
+
},
|
|
642
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
625
643
|
),
|
|
626
644
|
|
|
627
645
|
Tool(
|
|
@@ -630,7 +648,8 @@ async def list_tools() -> list[Tool]:
|
|
|
630
648
|
inputSchema={
|
|
631
649
|
"type": "object",
|
|
632
650
|
"properties": {}
|
|
633
|
-
}
|
|
651
|
+
},
|
|
652
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
634
653
|
),
|
|
635
654
|
|
|
636
655
|
Tool(
|
|
@@ -645,7 +664,8 @@ async def list_tools() -> list[Tool]:
|
|
|
645
664
|
}
|
|
646
665
|
},
|
|
647
666
|
"required": ["package_name"]
|
|
648
|
-
}
|
|
667
|
+
},
|
|
668
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
649
669
|
),
|
|
650
670
|
|
|
651
671
|
Tool(
|
|
@@ -660,7 +680,8 @@ async def list_tools() -> list[Tool]:
|
|
|
660
680
|
}
|
|
661
681
|
},
|
|
662
682
|
"required": ["pkgbuild_content"]
|
|
663
|
-
}
|
|
683
|
+
},
|
|
684
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
664
685
|
),
|
|
665
686
|
|
|
666
687
|
Tool(
|
|
@@ -675,7 +696,8 @@ async def list_tools() -> list[Tool]:
|
|
|
675
696
|
}
|
|
676
697
|
},
|
|
677
698
|
"required": ["package_info"]
|
|
678
|
-
}
|
|
699
|
+
},
|
|
700
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
679
701
|
),
|
|
680
702
|
|
|
681
703
|
# Package Removal Tools
|
|
@@ -701,7 +723,8 @@ async def list_tools() -> list[Tool]:
|
|
|
701
723
|
}
|
|
702
724
|
},
|
|
703
725
|
"required": ["package_name"]
|
|
704
|
-
}
|
|
726
|
+
},
|
|
727
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
705
728
|
),
|
|
706
729
|
|
|
707
730
|
Tool(
|
|
@@ -722,7 +745,8 @@ async def list_tools() -> list[Tool]:
|
|
|
722
745
|
}
|
|
723
746
|
},
|
|
724
747
|
"required": ["package_names"]
|
|
725
|
-
}
|
|
748
|
+
},
|
|
749
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
726
750
|
),
|
|
727
751
|
|
|
728
752
|
# Orphan Package Management
|
|
@@ -732,7 +756,8 @@ async def list_tools() -> list[Tool]:
|
|
|
732
756
|
inputSchema={
|
|
733
757
|
"type": "object",
|
|
734
758
|
"properties": {}
|
|
735
|
-
}
|
|
759
|
+
},
|
|
760
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
736
761
|
),
|
|
737
762
|
|
|
738
763
|
Tool(
|
|
@@ -753,7 +778,8 @@ async def list_tools() -> list[Tool]:
|
|
|
753
778
|
}
|
|
754
779
|
},
|
|
755
780
|
"required": []
|
|
756
|
-
}
|
|
781
|
+
},
|
|
782
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
757
783
|
),
|
|
758
784
|
|
|
759
785
|
# Package Ownership Tools
|
|
@@ -769,7 +795,8 @@ async def list_tools() -> list[Tool]:
|
|
|
769
795
|
}
|
|
770
796
|
},
|
|
771
797
|
"required": ["file_path"]
|
|
772
|
-
}
|
|
798
|
+
},
|
|
799
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
773
800
|
),
|
|
774
801
|
|
|
775
802
|
Tool(
|
|
@@ -788,7 +815,8 @@ async def list_tools() -> list[Tool]:
|
|
|
788
815
|
}
|
|
789
816
|
},
|
|
790
817
|
"required": ["package_name"]
|
|
791
|
-
}
|
|
818
|
+
},
|
|
819
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
792
820
|
),
|
|
793
821
|
|
|
794
822
|
Tool(
|
|
@@ -803,7 +831,8 @@ async def list_tools() -> list[Tool]:
|
|
|
803
831
|
}
|
|
804
832
|
},
|
|
805
833
|
"required": ["filename_pattern"]
|
|
806
|
-
}
|
|
834
|
+
},
|
|
835
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
807
836
|
),
|
|
808
837
|
|
|
809
838
|
# Package Verification
|
|
@@ -824,7 +853,8 @@ async def list_tools() -> list[Tool]:
|
|
|
824
853
|
}
|
|
825
854
|
},
|
|
826
855
|
"required": ["package_name"]
|
|
827
|
-
}
|
|
856
|
+
},
|
|
857
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
828
858
|
),
|
|
829
859
|
|
|
830
860
|
# Package Groups
|
|
@@ -834,7 +864,8 @@ async def list_tools() -> list[Tool]:
|
|
|
834
864
|
inputSchema={
|
|
835
865
|
"type": "object",
|
|
836
866
|
"properties": {}
|
|
837
|
-
}
|
|
867
|
+
},
|
|
868
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
838
869
|
),
|
|
839
870
|
|
|
840
871
|
Tool(
|
|
@@ -849,7 +880,8 @@ async def list_tools() -> list[Tool]:
|
|
|
849
880
|
}
|
|
850
881
|
},
|
|
851
882
|
"required": ["group_name"]
|
|
852
|
-
}
|
|
883
|
+
},
|
|
884
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
853
885
|
),
|
|
854
886
|
|
|
855
887
|
# Install Reason Management
|
|
@@ -859,7 +891,8 @@ async def list_tools() -> list[Tool]:
|
|
|
859
891
|
inputSchema={
|
|
860
892
|
"type": "object",
|
|
861
893
|
"properties": {}
|
|
862
|
-
}
|
|
894
|
+
},
|
|
895
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
863
896
|
),
|
|
864
897
|
|
|
865
898
|
Tool(
|
|
@@ -874,7 +907,8 @@ async def list_tools() -> list[Tool]:
|
|
|
874
907
|
}
|
|
875
908
|
},
|
|
876
909
|
"required": ["package_name"]
|
|
877
|
-
}
|
|
910
|
+
},
|
|
911
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
878
912
|
),
|
|
879
913
|
|
|
880
914
|
Tool(
|
|
@@ -889,7 +923,8 @@ async def list_tools() -> list[Tool]:
|
|
|
889
923
|
}
|
|
890
924
|
},
|
|
891
925
|
"required": ["package_name"]
|
|
892
|
-
}
|
|
926
|
+
},
|
|
927
|
+
annotations=ToolAnnotations(destructiveHint=True)
|
|
893
928
|
),
|
|
894
929
|
|
|
895
930
|
# System Diagnostic Tools
|
|
@@ -899,7 +934,8 @@ async def list_tools() -> list[Tool]:
|
|
|
899
934
|
inputSchema={
|
|
900
935
|
"type": "object",
|
|
901
936
|
"properties": {}
|
|
902
|
-
}
|
|
937
|
+
},
|
|
938
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
903
939
|
),
|
|
904
940
|
|
|
905
941
|
Tool(
|
|
@@ -908,7 +944,8 @@ async def list_tools() -> list[Tool]:
|
|
|
908
944
|
inputSchema={
|
|
909
945
|
"type": "object",
|
|
910
946
|
"properties": {}
|
|
911
|
-
}
|
|
947
|
+
},
|
|
948
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
912
949
|
),
|
|
913
950
|
|
|
914
951
|
Tool(
|
|
@@ -917,7 +954,8 @@ async def list_tools() -> list[Tool]:
|
|
|
917
954
|
inputSchema={
|
|
918
955
|
"type": "object",
|
|
919
956
|
"properties": {}
|
|
920
|
-
}
|
|
957
|
+
},
|
|
958
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
921
959
|
),
|
|
922
960
|
|
|
923
961
|
Tool(
|
|
@@ -926,7 +964,8 @@ async def list_tools() -> list[Tool]:
|
|
|
926
964
|
inputSchema={
|
|
927
965
|
"type": "object",
|
|
928
966
|
"properties": {}
|
|
929
|
-
}
|
|
967
|
+
},
|
|
968
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
930
969
|
),
|
|
931
970
|
|
|
932
971
|
Tool(
|
|
@@ -942,7 +981,8 @@ async def list_tools() -> list[Tool]:
|
|
|
942
981
|
}
|
|
943
982
|
},
|
|
944
983
|
"required": []
|
|
945
|
-
}
|
|
984
|
+
},
|
|
985
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
946
986
|
),
|
|
947
987
|
|
|
948
988
|
# News Tools
|
|
@@ -963,7 +1003,8 @@ async def list_tools() -> list[Tool]:
|
|
|
963
1003
|
}
|
|
964
1004
|
},
|
|
965
1005
|
"required": []
|
|
966
|
-
}
|
|
1006
|
+
},
|
|
1007
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
967
1008
|
),
|
|
968
1009
|
|
|
969
1010
|
Tool(
|
|
@@ -979,7 +1020,8 @@ async def list_tools() -> list[Tool]:
|
|
|
979
1020
|
}
|
|
980
1021
|
},
|
|
981
1022
|
"required": []
|
|
982
|
-
}
|
|
1023
|
+
},
|
|
1024
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
983
1025
|
),
|
|
984
1026
|
|
|
985
1027
|
Tool(
|
|
@@ -988,7 +1030,8 @@ async def list_tools() -> list[Tool]:
|
|
|
988
1030
|
inputSchema={
|
|
989
1031
|
"type": "object",
|
|
990
1032
|
"properties": {}
|
|
991
|
-
}
|
|
1033
|
+
},
|
|
1034
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
992
1035
|
),
|
|
993
1036
|
|
|
994
1037
|
# Transaction Log Tools
|
|
@@ -1011,7 +1054,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1011
1054
|
}
|
|
1012
1055
|
},
|
|
1013
1056
|
"required": []
|
|
1014
|
-
}
|
|
1057
|
+
},
|
|
1058
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1015
1059
|
),
|
|
1016
1060
|
|
|
1017
1061
|
Tool(
|
|
@@ -1026,7 +1070,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1026
1070
|
}
|
|
1027
1071
|
},
|
|
1028
1072
|
"required": ["package_name"]
|
|
1029
|
-
}
|
|
1073
|
+
},
|
|
1074
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1030
1075
|
),
|
|
1031
1076
|
|
|
1032
1077
|
Tool(
|
|
@@ -1035,7 +1080,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1035
1080
|
inputSchema={
|
|
1036
1081
|
"type": "object",
|
|
1037
1082
|
"properties": {}
|
|
1038
|
-
}
|
|
1083
|
+
},
|
|
1084
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1039
1085
|
),
|
|
1040
1086
|
|
|
1041
1087
|
Tool(
|
|
@@ -1051,7 +1097,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1051
1097
|
}
|
|
1052
1098
|
},
|
|
1053
1099
|
"required": []
|
|
1054
|
-
}
|
|
1100
|
+
},
|
|
1101
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1055
1102
|
),
|
|
1056
1103
|
|
|
1057
1104
|
# Mirror Management Tools
|
|
@@ -1061,7 +1108,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1061
1108
|
inputSchema={
|
|
1062
1109
|
"type": "object",
|
|
1063
1110
|
"properties": {}
|
|
1064
|
-
}
|
|
1111
|
+
},
|
|
1112
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1065
1113
|
),
|
|
1066
1114
|
|
|
1067
1115
|
Tool(
|
|
@@ -1076,7 +1124,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1076
1124
|
}
|
|
1077
1125
|
},
|
|
1078
1126
|
"required": []
|
|
1079
|
-
}
|
|
1127
|
+
},
|
|
1128
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1080
1129
|
),
|
|
1081
1130
|
|
|
1082
1131
|
Tool(
|
|
@@ -1096,7 +1145,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1096
1145
|
}
|
|
1097
1146
|
},
|
|
1098
1147
|
"required": []
|
|
1099
|
-
}
|
|
1148
|
+
},
|
|
1149
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1100
1150
|
),
|
|
1101
1151
|
|
|
1102
1152
|
Tool(
|
|
@@ -1105,7 +1155,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1105
1155
|
inputSchema={
|
|
1106
1156
|
"type": "object",
|
|
1107
1157
|
"properties": {}
|
|
1108
|
-
}
|
|
1158
|
+
},
|
|
1159
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1109
1160
|
),
|
|
1110
1161
|
|
|
1111
1162
|
# Configuration Tools
|
|
@@ -1115,7 +1166,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1115
1166
|
inputSchema={
|
|
1116
1167
|
"type": "object",
|
|
1117
1168
|
"properties": {}
|
|
1118
|
-
}
|
|
1169
|
+
},
|
|
1170
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1119
1171
|
),
|
|
1120
1172
|
|
|
1121
1173
|
Tool(
|
|
@@ -1124,7 +1176,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1124
1176
|
inputSchema={
|
|
1125
1177
|
"type": "object",
|
|
1126
1178
|
"properties": {}
|
|
1127
|
-
}
|
|
1179
|
+
},
|
|
1180
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1128
1181
|
),
|
|
1129
1182
|
|
|
1130
1183
|
Tool(
|
|
@@ -1133,7 +1186,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1133
1186
|
inputSchema={
|
|
1134
1187
|
"type": "object",
|
|
1135
1188
|
"properties": {}
|
|
1136
|
-
}
|
|
1189
|
+
},
|
|
1190
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1137
1191
|
),
|
|
1138
1192
|
|
|
1139
1193
|
Tool(
|
|
@@ -1142,7 +1196,8 @@ async def list_tools() -> list[Tool]:
|
|
|
1142
1196
|
inputSchema={
|
|
1143
1197
|
"type": "object",
|
|
1144
1198
|
"properties": {}
|
|
1145
|
-
}
|
|
1199
|
+
},
|
|
1200
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1146
1201
|
),
|
|
1147
1202
|
|
|
1148
1203
|
Tool(
|
|
@@ -1151,7 +1206,18 @@ async def list_tools() -> list[Tool]:
|
|
|
1151
1206
|
inputSchema={
|
|
1152
1207
|
"type": "object",
|
|
1153
1208
|
"properties": {}
|
|
1154
|
-
}
|
|
1209
|
+
},
|
|
1210
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1211
|
+
),
|
|
1212
|
+
|
|
1213
|
+
Tool(
|
|
1214
|
+
name="run_system_health_check",
|
|
1215
|
+
description="[MONITORING] Run a comprehensive system health check. Integrates multiple diagnostics to provide a complete overview of system status, including disk space, failed services, updates, orphan packages, and more. Only works on Arch Linux.",
|
|
1216
|
+
inputSchema={
|
|
1217
|
+
"type": "object",
|
|
1218
|
+
"properties": {}
|
|
1219
|
+
},
|
|
1220
|
+
annotations=ToolAnnotations(readOnlyHint=True)
|
|
1155
1221
|
),
|
|
1156
1222
|
]
|
|
1157
1223
|
|
|
@@ -1466,6 +1532,13 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent |
|
|
|
1466
1532
|
|
|
1467
1533
|
result = await get_parallel_downloads_setting()
|
|
1468
1534
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1535
|
+
|
|
1536
|
+
elif name == "run_system_health_check":
|
|
1537
|
+
if not IS_ARCH:
|
|
1538
|
+
return [TextContent(type="text", text="Error: run_system_health_check only available on Arch Linux systems")]
|
|
1539
|
+
|
|
1540
|
+
result = await run_system_health_check()
|
|
1541
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
1469
1542
|
|
|
1470
1543
|
elif name == "check_database_freshness":
|
|
1471
1544
|
if not IS_ARCH:
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-only OR MIT
|
|
2
|
+
"""
|
|
3
|
+
System health check module.
|
|
4
|
+
Provides a comprehensive system health check by integrating multiple system diagnostics.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
|
|
10
|
+
from .utils import IS_ARCH
|
|
11
|
+
from . import (
|
|
12
|
+
get_system_info,
|
|
13
|
+
check_disk_space,
|
|
14
|
+
check_failed_services,
|
|
15
|
+
get_pacman_cache_stats,
|
|
16
|
+
check_updates_dry_run,
|
|
17
|
+
check_critical_news,
|
|
18
|
+
list_orphan_packages,
|
|
19
|
+
check_database_freshness,
|
|
20
|
+
check_mirrorlist_health
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def run_system_health_check() -> Dict[str, Any]:
|
|
27
|
+
"""
|
|
28
|
+
Run a comprehensive system health check.
|
|
29
|
+
|
|
30
|
+
This function integrates multiple system diagnostics to provide a complete
|
|
31
|
+
overview of the system's health status in a single call.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dict with comprehensive health check results
|
|
35
|
+
"""
|
|
36
|
+
logger.info("Starting comprehensive system health check")
|
|
37
|
+
|
|
38
|
+
health_report = {
|
|
39
|
+
"status": "success",
|
|
40
|
+
"system_info": {},
|
|
41
|
+
"disk_space": {},
|
|
42
|
+
"services": {},
|
|
43
|
+
"pacman_cache": {},
|
|
44
|
+
"updates": {},
|
|
45
|
+
"news": {},
|
|
46
|
+
"orphans": {},
|
|
47
|
+
"database": {},
|
|
48
|
+
"mirrors": {},
|
|
49
|
+
"issues": [],
|
|
50
|
+
"suggestions": []
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
# System information
|
|
55
|
+
logger.info("Checking system information")
|
|
56
|
+
system_info = await get_system_info()
|
|
57
|
+
health_report["system_info"] = system_info
|
|
58
|
+
|
|
59
|
+
# Disk space check
|
|
60
|
+
logger.info("Checking disk space")
|
|
61
|
+
disk_space = await check_disk_space()
|
|
62
|
+
health_report["disk_space"] = disk_space
|
|
63
|
+
|
|
64
|
+
# Check for low disk space
|
|
65
|
+
if disk_space.get("status") == "success":
|
|
66
|
+
for partition in disk_space.get("data", []):
|
|
67
|
+
if partition.get("used_percent", 0) > 90:
|
|
68
|
+
health_report["issues"].append({
|
|
69
|
+
"type": "critical",
|
|
70
|
+
"message": f"Low disk space on {partition['mount_point']}: {partition['used_percent']}% used",
|
|
71
|
+
"suggestion": "Clean up unnecessary files or resize the partition"
|
|
72
|
+
})
|
|
73
|
+
elif partition.get("used_percent", 0) > 80:
|
|
74
|
+
health_report["issues"].append({
|
|
75
|
+
"type": "warning",
|
|
76
|
+
"message": f"Disk space getting low on {partition['mount_point']}: {partition['used_percent']}% used",
|
|
77
|
+
"suggestion": "Consider cleaning up files to free up space"
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
# Failed services check
|
|
81
|
+
logger.info("Checking for failed services")
|
|
82
|
+
failed_services = await check_failed_services()
|
|
83
|
+
health_report["services"] = failed_services
|
|
84
|
+
|
|
85
|
+
if failed_services.get("status") == "success" and failed_services.get("data"):
|
|
86
|
+
health_report["issues"].append({
|
|
87
|
+
"type": "warning",
|
|
88
|
+
"message": f"{len(failed_services['data'])} failed systemd services detected",
|
|
89
|
+
"suggestion": "Check systemd journal logs for details about failed services"
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
# Pacman cache statistics
|
|
93
|
+
logger.info("Checking pacman cache")
|
|
94
|
+
cache_stats = await get_pacman_cache_stats()
|
|
95
|
+
health_report["pacman_cache"] = cache_stats
|
|
96
|
+
|
|
97
|
+
if cache_stats.get("status") == "success":
|
|
98
|
+
cache_size = cache_stats.get("data", {}).get("total_size_mb", 0)
|
|
99
|
+
if cache_size > 5000: # 5GB
|
|
100
|
+
health_report["suggestions"].append({
|
|
101
|
+
"message": f"Pacman cache is large ({cache_size:.1f}MB)",
|
|
102
|
+
"action": "Run 'paccache -r' to clean old packages"
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
# Updates check
|
|
106
|
+
logger.info("Checking for available updates")
|
|
107
|
+
updates = await check_updates_dry_run()
|
|
108
|
+
health_report["updates"] = updates
|
|
109
|
+
|
|
110
|
+
if updates.get("status") == "success":
|
|
111
|
+
if updates.get("updates_available"):
|
|
112
|
+
count = updates.get("count", 0)
|
|
113
|
+
health_report["suggestions"].append({
|
|
114
|
+
"message": f"{count} updates available",
|
|
115
|
+
"action": "Run 'sudo pacman -Syu' to update the system"
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
# Critical news check
|
|
119
|
+
logger.info("Checking for critical news")
|
|
120
|
+
critical_news = await check_critical_news()
|
|
121
|
+
health_report["news"] = critical_news
|
|
122
|
+
|
|
123
|
+
if critical_news.get("status") == "success" and critical_news.get("data"):
|
|
124
|
+
health_report["issues"].append({
|
|
125
|
+
"type": "critical",
|
|
126
|
+
"message": f"{len(critical_news['data'])} critical news items require attention",
|
|
127
|
+
"suggestion": "Review the news items before updating"
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
# Orphan packages check
|
|
131
|
+
logger.info("Checking for orphan packages")
|
|
132
|
+
orphans = await list_orphan_packages()
|
|
133
|
+
health_report["orphans"] = orphans
|
|
134
|
+
|
|
135
|
+
if orphans.get("status") == "success":
|
|
136
|
+
orphan_count = len(orphans.get("data", []))
|
|
137
|
+
if orphan_count > 0:
|
|
138
|
+
health_report["suggestions"].append({
|
|
139
|
+
"message": f"{orphan_count} orphan packages detected",
|
|
140
|
+
"action": "Run 'sudo pacman -Rns $(pacman -Qtdq)' to remove orphans"
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
# Database freshness
|
|
144
|
+
logger.info("Checking database freshness")
|
|
145
|
+
db_freshness = await check_database_freshness()
|
|
146
|
+
health_report["database"] = db_freshness
|
|
147
|
+
|
|
148
|
+
# Mirrorlist health
|
|
149
|
+
logger.info("Checking mirrorlist health")
|
|
150
|
+
mirror_health = await check_mirrorlist_health()
|
|
151
|
+
health_report["mirrors"] = mirror_health
|
|
152
|
+
|
|
153
|
+
if mirror_health.get("status") == "success":
|
|
154
|
+
if not mirror_health.get("data", {}).get("healthy", True):
|
|
155
|
+
health_report["issues"].append({
|
|
156
|
+
"type": "warning",
|
|
157
|
+
"message": "Mirrorlist configuration has issues",
|
|
158
|
+
"suggestion": "Run 'reflector' to update your mirrorlist"
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
# Overall health assessment
|
|
162
|
+
issue_count = len(health_report["issues"])
|
|
163
|
+
suggestion_count = len(health_report["suggestions"])
|
|
164
|
+
|
|
165
|
+
health_report["summary"] = {
|
|
166
|
+
"total_issues": issue_count,
|
|
167
|
+
"critical_issues": len([i for i in health_report["issues"] if i["type"] == "critical"]),
|
|
168
|
+
"warnings": len([i for i in health_report["issues"] if i["type"] == "warning"]),
|
|
169
|
+
"suggestions": suggestion_count
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
logger.info(f"Health check completed: {issue_count} issues, {suggestion_count} suggestions")
|
|
173
|
+
|
|
174
|
+
return health_report
|
|
175
|
+
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.error(f"Health check failed: {e}")
|
|
178
|
+
return {
|
|
179
|
+
"status": "error",
|
|
180
|
+
"error": str(e),
|
|
181
|
+
"issues": [],
|
|
182
|
+
"suggestions": [],
|
|
183
|
+
"summary": {
|
|
184
|
+
"total_issues": 1,
|
|
185
|
+
"critical_issues": 1,
|
|
186
|
+
"warnings": 0,
|
|
187
|
+
"suggestions": 0
|
|
188
|
+
}
|
|
189
|
+
}
|
arch_ops_server/tool_metadata.py
CHANGED
|
@@ -457,6 +457,25 @@ TOOL_METADATA = {
|
|
|
457
457
|
related_tools=["analyze_pacman_conf"],
|
|
458
458
|
prerequisite_tools=[]
|
|
459
459
|
),
|
|
460
|
+
"run_system_health_check": ToolMetadata(
|
|
461
|
+
name="run_system_health_check",
|
|
462
|
+
category="monitoring",
|
|
463
|
+
platform="arch",
|
|
464
|
+
permission="read",
|
|
465
|
+
workflow="diagnose",
|
|
466
|
+
related_tools=[
|
|
467
|
+
"get_system_info",
|
|
468
|
+
"check_disk_space",
|
|
469
|
+
"check_failed_services",
|
|
470
|
+
"get_pacman_cache_stats",
|
|
471
|
+
"check_updates_dry_run",
|
|
472
|
+
"check_critical_news",
|
|
473
|
+
"list_orphan_packages",
|
|
474
|
+
"check_database_freshness",
|
|
475
|
+
"check_mirrorlist_health"
|
|
476
|
+
],
|
|
477
|
+
prerequisite_tools=[]
|
|
478
|
+
),
|
|
460
479
|
}
|
|
461
480
|
|
|
462
481
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: arch-ops-server
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: MCP server bridging AI assistants with Arch Linux ecosystem (Wiki, AUR, official repos)
|
|
5
5
|
Keywords: arch-linux,mcp,model-context-protocol,aur,pacman,wiki,ai-assistant
|
|
6
6
|
Author: Nihal
|
|
@@ -52,17 +52,18 @@ Leverage AI to get output for digestible, structured results that are ready for
|
|
|
52
52
|
|
|
53
53
|
## Sneak Peak into what's available
|
|
54
54
|
|
|
55
|
-
<details
|
|
56
|
-
|
|
55
|
+
<details>
|
|
56
|
+
|
|
57
|
+
<summary>Using VS Code Sonnet 3.5 for Safe Installation from AUR</summary>
|
|
57
58
|
|
|
58
|
-

|
|
59
60
|
|
|
60
61
|
</details>
|
|
61
62
|
|
|
62
63
|
<details>
|
|
63
|
-
<summary>
|
|
64
|
+
<summary> Asking Claude Code Sonnet 4.5 for fedora equivalent command </summary>
|
|
64
65
|
|
|
65
|
-

|
|
66
67
|
|
|
67
68
|
</details>
|
|
68
69
|
|
|
@@ -71,11 +72,13 @@ Leverage AI to get output for digestible, structured results that are ready for
|
|
|
71
72
|
Direct access to Arch ecosystem data via custom URI schemes:
|
|
72
73
|
|
|
73
74
|
#### Documentation & Search
|
|
75
|
+
|
|
74
76
|
| URI Scheme | Example | Returns |
|
|
75
77
|
|------------|---------|---------|
|
|
76
78
|
| `archwiki://` | `archwiki://Installation_guide` | Markdown-formatted Wiki page |
|
|
77
79
|
|
|
78
80
|
#### Package Information
|
|
81
|
+
|
|
79
82
|
| URI Scheme | Example | Returns |
|
|
80
83
|
|------------|---------|---------|
|
|
81
84
|
| `archrepo://` | `archrepo://vim` | Official repository package details |
|
|
@@ -83,6 +86,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
83
86
|
| `aur://*/pkgbuild` | `aur://yay/pkgbuild` | Raw PKGBUILD with safety analysis |
|
|
84
87
|
|
|
85
88
|
#### System Packages (Arch only)
|
|
89
|
+
|
|
86
90
|
| URI Scheme | Example | Returns |
|
|
87
91
|
|------------|---------|---------|
|
|
88
92
|
| `pacman://installed` | `pacman://installed` | System installed packages list |
|
|
@@ -93,6 +97,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
93
97
|
| `pacman://database/freshness` | `pacman://database/freshness` | Package database sync status |
|
|
94
98
|
|
|
95
99
|
#### System Monitoring & Logs
|
|
100
|
+
|
|
96
101
|
| URI Scheme | Example | Returns |
|
|
97
102
|
|------------|---------|---------|
|
|
98
103
|
| `system://info` | `system://info` | System information (kernel, memory, uptime) |
|
|
@@ -103,6 +108,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
103
108
|
| `pacman://log/failed` | `pacman://log/failed` | Failed package transactions |
|
|
104
109
|
|
|
105
110
|
#### News & Updates
|
|
111
|
+
|
|
106
112
|
| URI Scheme | Example | Returns |
|
|
107
113
|
|------------|---------|---------|
|
|
108
114
|
| `archnews://latest` | `archnews://latest` | Latest Arch Linux news |
|
|
@@ -110,6 +116,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
110
116
|
| `archnews://since-update` | `archnews://since-update` | News since last system update |
|
|
111
117
|
|
|
112
118
|
#### Configuration
|
|
119
|
+
|
|
113
120
|
| URI Scheme | Example | Returns |
|
|
114
121
|
|------------|---------|---------|
|
|
115
122
|
| `config://pacman` | `config://pacman` | Parsed pacman.conf configuration |
|
|
@@ -120,6 +127,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
120
127
|
### Tools (Executable Functions)
|
|
121
128
|
|
|
122
129
|
#### Package Search & Information
|
|
130
|
+
|
|
123
131
|
| Tool | Description | Platform |
|
|
124
132
|
|------|-------------|----------|
|
|
125
133
|
| `search_archwiki` | Query Arch Wiki with ranked results | Any |
|
|
@@ -127,6 +135,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
127
135
|
| `get_official_package_info` | Get official package details (hybrid local/remote) | Any |
|
|
128
136
|
|
|
129
137
|
#### Package Lifecycle Management
|
|
138
|
+
|
|
130
139
|
| Tool | Description | Platform |
|
|
131
140
|
|------|-------------|----------|
|
|
132
141
|
| `check_updates_dry_run` | Check for available updates | Arch only |
|
|
@@ -135,6 +144,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
135
144
|
| `remove_packages_batch` | Remove multiple packages efficiently | Arch only |
|
|
136
145
|
|
|
137
146
|
#### Package Analysis & Maintenance
|
|
147
|
+
|
|
138
148
|
| Tool | Description | Platform |
|
|
139
149
|
|------|-------------|----------|
|
|
140
150
|
| `list_orphan_packages` | Find orphaned packages | Arch only |
|
|
@@ -145,6 +155,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
145
155
|
| `mark_as_dependency` | Allow package to be orphaned | Arch only |
|
|
146
156
|
|
|
147
157
|
#### Package Organization
|
|
158
|
+
|
|
148
159
|
| Tool | Description | Platform |
|
|
149
160
|
|------|-------------|----------|
|
|
150
161
|
| `find_package_owner` | Find which package owns a file | Arch only |
|
|
@@ -154,6 +165,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
154
165
|
| `list_group_packages` | Show packages in specific group | Arch only |
|
|
155
166
|
|
|
156
167
|
#### System Monitoring & Diagnostics
|
|
168
|
+
|
|
157
169
|
| Tool | Description | Platform |
|
|
158
170
|
|------|-------------|----------|
|
|
159
171
|
| `get_system_info` | System info (kernel, memory, uptime) | Any |
|
|
@@ -164,6 +176,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
164
176
|
| `check_database_freshness` | Check package database sync status | Arch only |
|
|
165
177
|
|
|
166
178
|
#### Transaction History & Logs
|
|
179
|
+
|
|
167
180
|
| Tool | Description | Platform |
|
|
168
181
|
|------|-------------|----------|
|
|
169
182
|
| `get_transaction_history` | Recent package transactions (install/upgrade/remove) | Arch only |
|
|
@@ -172,6 +185,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
172
185
|
| `get_database_sync_history` | Database sync events | Arch only |
|
|
173
186
|
|
|
174
187
|
#### News & Safety Checks
|
|
188
|
+
|
|
175
189
|
| Tool | Description | Platform |
|
|
176
190
|
|------|-------------|----------|
|
|
177
191
|
| `get_latest_news` | Fetch Arch Linux news from RSS | Any |
|
|
@@ -179,6 +193,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
179
193
|
| `get_news_since_last_update` | News posted since last system update | Arch only |
|
|
180
194
|
|
|
181
195
|
#### Mirror Management
|
|
196
|
+
|
|
182
197
|
| Tool | Description | Platform |
|
|
183
198
|
|------|-------------|----------|
|
|
184
199
|
| `list_active_mirrors` | Show configured mirrors | Arch only |
|
|
@@ -187,6 +202,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
187
202
|
| `check_mirrorlist_health` | Verify mirror configuration | Arch only |
|
|
188
203
|
|
|
189
204
|
#### Configuration Management
|
|
205
|
+
|
|
190
206
|
| Tool | Description | Platform |
|
|
191
207
|
|------|-------------|----------|
|
|
192
208
|
| `analyze_pacman_conf` | Parse pacman.conf settings | Arch only |
|
|
@@ -195,6 +211,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
195
211
|
| `get_parallel_downloads_setting` | Get parallel download config | Arch only |
|
|
196
212
|
|
|
197
213
|
#### Security Analysis
|
|
214
|
+
|
|
198
215
|
| Tool | Description | Platform |
|
|
199
216
|
|------|-------------|----------|
|
|
200
217
|
| `analyze_pkgbuild_safety` | Comprehensive PKGBUILD analysis (50+ red flags) | Any |
|
|
@@ -214,6 +231,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
214
231
|
## Installation
|
|
215
232
|
|
|
216
233
|
### Prerequisites
|
|
234
|
+
|
|
217
235
|
- Python 3.11+
|
|
218
236
|
- [uv](https://github.com/astral-sh/uv) (recommended) or pip
|
|
219
237
|
|
|
@@ -222,6 +240,7 @@ Direct access to Arch ecosystem data via custom URI schemes:
|
|
|
222
240
|
```bash
|
|
223
241
|
uvx arch-ops-server
|
|
224
242
|
```
|
|
243
|
+
|
|
225
244
|
---
|
|
226
245
|
|
|
227
246
|
## Configuration
|
|
@@ -254,4 +273,5 @@ This project is dual-licensed under your choice of:
|
|
|
254
273
|
|
|
255
274
|
You may use this software under the terms of either license. When redistributing or modifying this software, you may choose which license to apply.
|
|
256
275
|
|
|
257
|
-
By contributing to this project, you agree that your contributions will be licensed under both licenses.
|
|
276
|
+
By contributing to this project, you agree that your contributions will be licensed under both licenses.
|
|
277
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
arch_ops_server/__init__.py,sha256=
|
|
1
|
+
arch_ops_server/__init__.py,sha256=CfKcpYarSMJxh64krmGD6yjCsqgtgefttjdxfAu4S88,4343
|
|
2
2
|
arch_ops_server/aur.py,sha256=poYbh2DW7I1tZfCeNp_7e10fh9ZZx8HTnYZnKKZtflQ,49808
|
|
3
3
|
arch_ops_server/config.py,sha256=4mtpS28vXSMeEVGrTWTMwZEzgIyfl0oCAYEzF7SKxE8,11076
|
|
4
4
|
arch_ops_server/http_server.py,sha256=wZ3hY6o6EftbN1OZiTUau7861LB9ihKWap6gevev_No,31810
|
|
@@ -7,12 +7,13 @@ arch_ops_server/mirrors.py,sha256=Evt-g20cMOTZQl9FbbkbklFd0gKWz-I7vVNrmyQO19U,13
|
|
|
7
7
|
arch_ops_server/news.py,sha256=E97eASR24tq_EaVDYuamIoBl4a7QtBkpscOaUPuU0W4,9359
|
|
8
8
|
arch_ops_server/pacman.py,sha256=S1Gc53CA6o4--YavA03EkxL0dGCZNhoFFZjawlW_p20,38354
|
|
9
9
|
arch_ops_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
arch_ops_server/server.py,sha256=
|
|
10
|
+
arch_ops_server/server.py,sha256=VFUBCbk8cm5YbyAAfeghkxMPwNX-RbRvkD5lN1_E_UI,88313
|
|
11
11
|
arch_ops_server/system.py,sha256=JfBUB3KD0veulQ-IIK8IOC8Jn6lqtLMCtlnryiL1n7w,9221
|
|
12
|
-
arch_ops_server/
|
|
12
|
+
arch_ops_server/system_health_check.py,sha256=G_hgPaVz-6tQ_ZAoPEk7aX2rm4lIwbO0xdXcwdBDoaE,7196
|
|
13
|
+
arch_ops_server/tool_metadata.py,sha256=Z0ZhtS1bxo9T_erJlGx9Xf0fKk4oZ0L6UQ8gJu7WEcA,20468
|
|
13
14
|
arch_ops_server/utils.py,sha256=po7MVqCx-hsdx-lOgs7uGicjoUVMf6HvuNNYl2qyFH0,10112
|
|
14
15
|
arch_ops_server/wiki.py,sha256=XB_emMGXYF3Vn5likRICkGOa72YDZvOhtZBgp_d1gg8,7350
|
|
15
|
-
arch_ops_server-3.
|
|
16
|
-
arch_ops_server-3.
|
|
17
|
-
arch_ops_server-3.
|
|
18
|
-
arch_ops_server-3.
|
|
16
|
+
arch_ops_server-3.2.0.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
|
|
17
|
+
arch_ops_server-3.2.0.dist-info/entry_points.txt,sha256=ZS2crFEqE9TteC4j2HmYS1wKvoBOCCXT2FJXJW5C4-E,117
|
|
18
|
+
arch_ops_server-3.2.0.dist-info/METADATA,sha256=vvwpjNRHYK_FrH8b4mjcsdEjJHVprEzrDjpXHk3sk64,11356
|
|
19
|
+
arch_ops_server-3.2.0.dist-info/RECORD,,
|
|
File without changes
|