pomera-ai-commander 0.1.0

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.
Files changed (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +680 -0
  3. package/bin/pomera-ai-commander.js +62 -0
  4. package/core/__init__.py +66 -0
  5. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  7. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  8. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  9. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  10. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  11. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  12. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  13. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  14. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  15. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  16. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  17. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  18. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  19. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  20. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  21. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  22. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  23. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  24. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  25. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  26. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  27. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  28. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  29. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  30. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  31. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  32. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  33. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  34. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  35. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  36. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  37. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  38. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  39. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  40. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  41. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  42. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  43. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  44. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  45. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  46. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  47. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  48. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  49. package/core/app_context.py +482 -0
  50. package/core/async_text_processor.py +422 -0
  51. package/core/backup_manager.py +656 -0
  52. package/core/backup_recovery_manager.py +1034 -0
  53. package/core/content_hash_cache.py +509 -0
  54. package/core/context_menu.py +313 -0
  55. package/core/data_validator.py +1067 -0
  56. package/core/database_connection_manager.py +745 -0
  57. package/core/database_curl_settings_manager.py +609 -0
  58. package/core/database_promera_ai_settings_manager.py +447 -0
  59. package/core/database_schema.py +412 -0
  60. package/core/database_schema_manager.py +396 -0
  61. package/core/database_settings_manager.py +1508 -0
  62. package/core/database_settings_manager_interface.py +457 -0
  63. package/core/dialog_manager.py +735 -0
  64. package/core/efficient_line_numbers.py +511 -0
  65. package/core/error_handler.py +747 -0
  66. package/core/error_service.py +431 -0
  67. package/core/event_consolidator.py +512 -0
  68. package/core/mcp/__init__.py +43 -0
  69. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  70. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  71. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  72. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  73. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  74. package/core/mcp/protocol.py +288 -0
  75. package/core/mcp/schema.py +251 -0
  76. package/core/mcp/server_stdio.py +299 -0
  77. package/core/mcp/tool_registry.py +2345 -0
  78. package/core/memory_efficient_text_widget.py +712 -0
  79. package/core/migration_manager.py +915 -0
  80. package/core/migration_test_suite.py +1086 -0
  81. package/core/migration_validator.py +1144 -0
  82. package/core/optimized_find_replace.py +715 -0
  83. package/core/optimized_pattern_engine.py +424 -0
  84. package/core/optimized_search_highlighter.py +553 -0
  85. package/core/performance_monitor.py +675 -0
  86. package/core/persistence_manager.py +713 -0
  87. package/core/progressive_stats_calculator.py +632 -0
  88. package/core/regex_pattern_cache.py +530 -0
  89. package/core/regex_pattern_library.py +351 -0
  90. package/core/search_operation_manager.py +435 -0
  91. package/core/settings_defaults_registry.py +1087 -0
  92. package/core/settings_integrity_validator.py +1112 -0
  93. package/core/settings_serializer.py +558 -0
  94. package/core/settings_validator.py +1824 -0
  95. package/core/smart_stats_calculator.py +710 -0
  96. package/core/statistics_update_manager.py +619 -0
  97. package/core/stats_config_manager.py +858 -0
  98. package/core/streaming_text_handler.py +723 -0
  99. package/core/task_scheduler.py +596 -0
  100. package/core/update_pattern_library.py +169 -0
  101. package/core/visibility_monitor.py +596 -0
  102. package/core/widget_cache.py +498 -0
  103. package/mcp.json +61 -0
  104. package/package.json +57 -0
  105. package/pomera.py +7483 -0
  106. package/pomera_mcp_server.py +144 -0
  107. package/tools/__init__.py +5 -0
  108. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  109. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  110. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  111. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  112. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  113. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  114. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  115. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  116. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  117. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  118. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  119. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  120. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  121. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  122. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  123. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  124. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  125. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  126. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  127. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  128. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  129. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  130. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  131. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  132. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  133. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  134. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  135. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  136. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  137. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  138. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  139. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  140. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  141. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  142. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  143. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  144. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  145. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  146. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  147. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  148. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  149. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  150. package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
  151. package/tools/ai_tools.py +2892 -0
  152. package/tools/ascii_art_generator.py +353 -0
  153. package/tools/base64_tools.py +184 -0
  154. package/tools/base_tool.py +511 -0
  155. package/tools/case_tool.py +309 -0
  156. package/tools/column_tools.py +396 -0
  157. package/tools/cron_tool.py +885 -0
  158. package/tools/curl_history.py +601 -0
  159. package/tools/curl_processor.py +1208 -0
  160. package/tools/curl_settings.py +503 -0
  161. package/tools/curl_tool.py +5467 -0
  162. package/tools/diff_viewer.py +1072 -0
  163. package/tools/email_extraction_tool.py +249 -0
  164. package/tools/email_header_analyzer.py +426 -0
  165. package/tools/extraction_tools.py +250 -0
  166. package/tools/find_replace.py +1751 -0
  167. package/tools/folder_file_reporter.py +1463 -0
  168. package/tools/folder_file_reporter_adapter.py +480 -0
  169. package/tools/generator_tools.py +1217 -0
  170. package/tools/hash_generator.py +256 -0
  171. package/tools/html_tool.py +657 -0
  172. package/tools/huggingface_helper.py +449 -0
  173. package/tools/jsonxml_tool.py +730 -0
  174. package/tools/line_tools.py +419 -0
  175. package/tools/list_comparator.py +720 -0
  176. package/tools/markdown_tools.py +562 -0
  177. package/tools/mcp_widget.py +1417 -0
  178. package/tools/notes_widget.py +973 -0
  179. package/tools/number_base_converter.py +373 -0
  180. package/tools/regex_extractor.py +572 -0
  181. package/tools/slug_generator.py +311 -0
  182. package/tools/sorter_tools.py +459 -0
  183. package/tools/string_escape_tool.py +393 -0
  184. package/tools/text_statistics_tool.py +366 -0
  185. package/tools/text_wrapper.py +431 -0
  186. package/tools/timestamp_converter.py +422 -0
  187. package/tools/tool_loader.py +710 -0
  188. package/tools/translator_tools.py +523 -0
  189. package/tools/url_link_extractor.py +262 -0
  190. package/tools/url_parser.py +205 -0
  191. package/tools/whitespace_tools.py +356 -0
  192. package/tools/word_frequency_counter.py +147 -0
@@ -0,0 +1,1824 @@
1
+ """
2
+ Settings Validation and Integrity Checking System
3
+
4
+ This module provides comprehensive validation for settings data before storage,
5
+ including type validation, data integrity checks for critical settings,
6
+ validation rules for encrypted data, and consistency checks across related settings.
7
+
8
+ Based on production codebase analysis of 45 Python files with complex settings patterns.
9
+ """
10
+
11
+ import re
12
+ import json
13
+ import logging
14
+ from typing import Any, Dict, List, Tuple, Optional, Set, Union, Callable, TYPE_CHECKING
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+
18
+ if TYPE_CHECKING:
19
+ from typing import Self
20
+
21
+
22
+ class SettingsValidator:
23
+ """
24
+ Comprehensive validator for settings data with integrity checking.
25
+
26
+ Features:
27
+ - Type validation for all setting values before storage
28
+ - Data integrity checks for critical settings (tab counts, required fields)
29
+ - Validation rules for encrypted data and sensitive information
30
+ - Consistency checks across related settings
31
+ - Custom validation rules for specific tools and configurations
32
+ """
33
+
34
+ def __init__(self):
35
+ """Initialize the settings validator."""
36
+ self.logger = logging.getLogger(__name__)
37
+
38
+ # Validation rules registry
39
+ self.validation_rules = {}
40
+ self.integrity_rules = {}
41
+ self.consistency_rules = {}
42
+
43
+ # Initialize built-in validation rules
44
+ self._initialize_core_validation_rules()
45
+ self._initialize_tool_validation_rules()
46
+ self._initialize_integrity_rules()
47
+ self._initialize_consistency_rules()
48
+
49
+ # Encryption validation patterns
50
+ self.encryption_pattern = re.compile(r'^ENC:[A-Za-z0-9+/=]+$')
51
+
52
+ # Critical settings that must always be valid
53
+ self.critical_settings = {
54
+ 'export_path',
55
+ 'debug_level',
56
+ 'selected_tool',
57
+ 'active_input_tab',
58
+ 'active_output_tab'
59
+ }
60
+
61
+ # Required tool settings
62
+ self.required_tool_settings = {
63
+ 'cURL Tool': {'default_timeout', 'follow_redirects', 'verify_ssl'},
64
+ 'JSON/XML Tool': {'operation', 'json_indent', 'xml_indent'},
65
+ 'Case Tool': {'mode'},
66
+ 'Find & Replace': {'case_sensitive', 'use_regex'}
67
+ }
68
+
69
+ def validate_setting_value(self, key: str, value: Any, context: Optional[Dict[str, Any]] = None) -> 'ValidationResult':
70
+ """
71
+ Validate a single setting value with comprehensive checks.
72
+
73
+ Args:
74
+ key: Setting key/path
75
+ value: Setting value to validate
76
+ context: Additional context for validation (tool name, related settings, etc.)
77
+
78
+ Returns:
79
+ ValidationResult with validation status and details
80
+ """
81
+ result = ValidationResult(key, value)
82
+
83
+ try:
84
+ # Basic type validation
85
+ type_result = self._validate_type(key, value, context)
86
+ result.add_check('type_validation', type_result)
87
+
88
+ # Value range/format validation
89
+ format_result = self._validate_format(key, value, context)
90
+ result.add_check('format_validation', format_result)
91
+
92
+ # Security validation (for sensitive data)
93
+ security_result = self._validate_security(key, value, context)
94
+ result.add_check('security_validation', security_result)
95
+
96
+ # Custom validation rules
97
+ custom_result = self._validate_custom_rules(key, value, context)
98
+ result.add_check('custom_validation', custom_result)
99
+
100
+ # Mark as critical if this is a critical setting
101
+ if key in self.critical_settings:
102
+ result.is_critical = True
103
+
104
+ self.logger.debug(f"Validated setting '{key}': {result.is_valid}")
105
+ return result
106
+
107
+ except Exception as e:
108
+ self.logger.error(f"Validation failed for setting '{key}': {e}")
109
+ result.add_error(f"Validation exception: {e}")
110
+ return result
111
+
112
+ def validate_tool_settings(self, tool_name: str, settings: Dict[str, Any]) -> 'ToolValidationResult':
113
+ """
114
+ Validate all settings for a specific tool.
115
+
116
+ Args:
117
+ tool_name: Name of the tool
118
+ settings: Tool settings dictionary
119
+
120
+ Returns:
121
+ ToolValidationResult with comprehensive validation status
122
+ """
123
+ result = ToolValidationResult(tool_name, settings)
124
+
125
+ try:
126
+ # Check required settings
127
+ required_result = self._check_required_settings(tool_name, settings)
128
+ result.add_check('required_settings', required_result)
129
+
130
+ # Validate individual settings
131
+ for key, value in settings.items():
132
+ context = {'tool_name': tool_name, 'all_settings': settings}
133
+ setting_result = self.validate_setting_value(f"{tool_name}.{key}", value, context)
134
+ result.add_setting_result(key, setting_result)
135
+
136
+ # Tool-specific integrity checks
137
+ integrity_result = self._check_tool_integrity(tool_name, settings)
138
+ result.add_check('integrity_check', integrity_result)
139
+
140
+ self.logger.debug(f"Validated tool '{tool_name}': {result.is_valid}")
141
+ return result
142
+
143
+ except Exception as e:
144
+ self.logger.error(f"Tool validation failed for '{tool_name}': {e}")
145
+ result.add_error(f"Tool validation exception: {e}")
146
+ return result
147
+
148
+ def validate_settings_consistency(self, all_settings: Dict[str, Any]) -> 'ConsistencyValidationResult':
149
+ """
150
+ Validate consistency across all settings.
151
+
152
+ Args:
153
+ all_settings: Complete settings dictionary
154
+
155
+ Returns:
156
+ ConsistencyValidationResult with cross-setting validation status
157
+ """
158
+ result = ConsistencyValidationResult(all_settings)
159
+
160
+ try:
161
+ # Tab consistency checks
162
+ tab_result = self._validate_tab_consistency(all_settings)
163
+ result.add_check('tab_consistency', tab_result)
164
+
165
+ # Tool selection consistency
166
+ tool_result = self._validate_tool_selection_consistency(all_settings)
167
+ result.add_check('tool_selection', tool_result)
168
+
169
+ # Performance settings consistency
170
+ perf_result = self._validate_performance_consistency(all_settings)
171
+ result.add_check('performance_consistency', perf_result)
172
+
173
+ # Font settings consistency
174
+ font_result = self._validate_font_consistency(all_settings)
175
+ result.add_check('font_consistency', font_result)
176
+
177
+ # Dialog settings consistency
178
+ dialog_result = self._validate_dialog_consistency(all_settings)
179
+ result.add_check('dialog_consistency', dialog_result)
180
+
181
+ # Integrity checks
182
+ for rule_name, rule_func in self.integrity_rules.items():
183
+ integrity_result = rule_func(all_settings)
184
+ result.add_check(rule_name, integrity_result)
185
+
186
+ # Custom consistency rules
187
+ for rule_name, rule_func in self.consistency_rules.items():
188
+ custom_result = rule_func(all_settings)
189
+ result.add_check(rule_name, custom_result)
190
+
191
+ self.logger.debug(f"Settings consistency validation: {result.is_valid}")
192
+ return result
193
+
194
+ except Exception as e:
195
+ self.logger.error(f"Consistency validation failed: {e}")
196
+ result.add_error(f"Consistency validation exception: {e}")
197
+ return result
198
+
199
+ def validate_complete_settings(self, settings: Dict[str, Any]) -> 'CompleteValidationResult':
200
+ """
201
+ Perform complete validation of all settings including type validation,
202
+ integrity checks, and consistency validation.
203
+
204
+ Args:
205
+ settings: Complete settings dictionary
206
+
207
+ Returns:
208
+ CompleteValidationResult with comprehensive validation status
209
+ """
210
+ result = CompleteValidationResult(settings)
211
+
212
+ try:
213
+ # 1. Validate core settings
214
+ core_settings = {k: v for k, v in settings.items()
215
+ if k not in ('tool_settings', 'performance_settings', 'font_settings', 'dialog_settings')}
216
+
217
+ for key, value in core_settings.items():
218
+ setting_result = self.validate_setting_value(key, value)
219
+ result.add_core_setting_result(key, setting_result)
220
+
221
+ # 2. Validate tool settings
222
+ tool_settings = settings.get('tool_settings', {})
223
+ if isinstance(tool_settings, dict):
224
+ for tool_name, tool_config in tool_settings.items():
225
+ if isinstance(tool_config, dict):
226
+ tool_result = self.validate_tool_settings(tool_name, tool_config)
227
+ result.add_tool_result(tool_name, tool_result)
228
+
229
+ # 3. Validate consistency across all settings
230
+ consistency_result = self.validate_settings_consistency(settings)
231
+ result.add_consistency_result(consistency_result)
232
+
233
+ self.logger.info(f"Complete settings validation: {result.is_valid} ({len(result.errors)} errors, {len(result.warnings)} warnings)")
234
+ return result
235
+
236
+ except Exception as e:
237
+ self.logger.error(f"Complete validation failed: {e}")
238
+ result.add_error(f"Complete validation exception: {e}")
239
+ return result
240
+
241
+ def _validate_type(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
242
+ """Validate the type of a setting value."""
243
+ result = CheckResult('type_validation')
244
+
245
+ try:
246
+ # Get expected type for this key
247
+ expected_type = self._get_expected_type(key, context)
248
+
249
+ if expected_type is None:
250
+ # No specific type requirement
251
+ result.passed = True
252
+ result.message = "No type constraint"
253
+ return result
254
+
255
+ # Check type compatibility
256
+ if isinstance(expected_type, tuple):
257
+ # Multiple allowed types
258
+ if any(isinstance(value, t) for t in expected_type):
259
+ result.passed = True
260
+ result.message = f"Type {type(value).__name__} is allowed"
261
+ else:
262
+ result.passed = False
263
+ result.message = f"Type {type(value).__name__} not in allowed types {expected_type}"
264
+ else:
265
+ # Single expected type
266
+ if isinstance(value, expected_type):
267
+ result.passed = True
268
+ result.message = f"Type {type(value).__name__} matches expected {expected_type.__name__}"
269
+ else:
270
+ result.passed = False
271
+ result.message = f"Type {type(value).__name__} does not match expected {expected_type.__name__}"
272
+
273
+ return result
274
+
275
+ except Exception as e:
276
+ result.passed = False
277
+ result.message = f"Type validation error: {e}"
278
+ return result
279
+
280
+ def _validate_format(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
281
+ """Validate the format/range of a setting value."""
282
+ result = CheckResult('format_validation')
283
+
284
+ try:
285
+ # Get validation rule for this key
286
+ rule = self.validation_rules.get(key)
287
+
288
+ if rule is None:
289
+ # Check for pattern-based rules
290
+ rule = self._get_pattern_rule(key, context)
291
+
292
+ if rule is None:
293
+ result.passed = True
294
+ result.message = "No format constraint"
295
+ return result
296
+
297
+ # Apply validation rule
298
+ if callable(rule):
299
+ rule_result = rule(value, context)
300
+ if isinstance(rule_result, bool):
301
+ result.passed = rule_result
302
+ result.message = "Custom rule validation"
303
+ elif isinstance(rule_result, dict):
304
+ result.passed = rule_result.get('valid', False)
305
+ result.message = rule_result.get('message', 'Custom rule validation')
306
+ else:
307
+ result.passed = bool(rule_result)
308
+ result.message = "Custom rule validation"
309
+ else:
310
+ # Pattern-based rule
311
+ if isinstance(value, str) and hasattr(rule, 'match'):
312
+ result.passed = bool(rule.match(value))
313
+ result.message = f"Pattern match: {rule.pattern}"
314
+ else:
315
+ result.passed = False
316
+ result.message = f"Cannot apply pattern rule to {type(value).__name__}"
317
+
318
+ return result
319
+
320
+ except Exception as e:
321
+ result.passed = False
322
+ result.message = f"Format validation error: {e}"
323
+ return result
324
+
325
+ def _validate_security(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
326
+ """Validate security aspects of a setting value."""
327
+ result = CheckResult('security_validation')
328
+
329
+ try:
330
+ # Check for sensitive data patterns
331
+ if self._is_sensitive_key(key):
332
+ if isinstance(value, str):
333
+ # Check if encrypted properly
334
+ if key.upper().endswith('KEY') or key.upper().endswith('TOKEN'):
335
+ if not self._is_encrypted_or_placeholder(value):
336
+ result.passed = False
337
+ result.message = "Sensitive data should be encrypted or placeholder"
338
+ result.severity = 'warning' # Not critical, but recommended
339
+ return result
340
+
341
+ # Check for obvious secrets in plain text
342
+ if self._contains_obvious_secret(value):
343
+ result.passed = False
344
+ result.message = "Possible plain text secret detected"
345
+ result.severity = 'warning'
346
+ return result
347
+
348
+ result.passed = True
349
+ result.message = "Security validation passed"
350
+ return result
351
+
352
+ except Exception as e:
353
+ result.passed = False
354
+ result.message = f"Security validation error: {e}"
355
+ return result
356
+
357
+ def _validate_custom_rules(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
358
+ """Apply custom validation rules for specific settings."""
359
+ result = CheckResult('custom_validation')
360
+
361
+ try:
362
+ # Tab index validation
363
+ if key in ('active_input_tab', 'active_output_tab'):
364
+ if not isinstance(value, int) or not (0 <= value < 7):
365
+ result.passed = False
366
+ result.message = "Tab index must be integer between 0 and 6"
367
+ return result
368
+
369
+ # Debug level validation
370
+ elif key == 'debug_level':
371
+ valid_levels = {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'}
372
+ if value not in valid_levels:
373
+ result.passed = False
374
+ result.message = f"Debug level must be one of {valid_levels}"
375
+ return result
376
+
377
+ # Export path validation
378
+ elif key == 'export_path':
379
+ if not isinstance(value, str) or not value.strip():
380
+ result.passed = False
381
+ result.message = "Export path must be non-empty string"
382
+ return result
383
+
384
+ # Check if path is reasonable (not validating existence)
385
+ try:
386
+ Path(value) # This will raise if path is invalid
387
+ except Exception:
388
+ result.passed = False
389
+ result.message = "Export path format is invalid"
390
+ return result
391
+
392
+ # Tool name validation
393
+ elif key == 'selected_tool':
394
+ if not isinstance(value, str) or not value.strip():
395
+ result.passed = False
396
+ result.message = "Selected tool must be non-empty string"
397
+ return result
398
+
399
+ result.passed = True
400
+ result.message = "Custom validation passed"
401
+ return result
402
+
403
+ except Exception as e:
404
+ result.passed = False
405
+ result.message = f"Custom validation error: {e}"
406
+ return result
407
+
408
+ def _check_required_settings(self, tool_name: str, settings: Dict[str, Any]) -> 'CheckResult':
409
+ """Check that all required settings are present for a tool."""
410
+ result = CheckResult('required_settings')
411
+
412
+ try:
413
+ required = self.required_tool_settings.get(tool_name, set())
414
+ missing = required - set(settings.keys())
415
+
416
+ if missing:
417
+ result.passed = False
418
+ result.message = f"Missing required settings: {missing}"
419
+ else:
420
+ result.passed = True
421
+ result.message = "All required settings present"
422
+
423
+ return result
424
+
425
+ except Exception as e:
426
+ result.passed = False
427
+ result.message = f"Required settings check error: {e}"
428
+ return result
429
+
430
+ def _check_tool_integrity(self, tool_name: str, settings: Dict[str, Any]) -> 'CheckResult':
431
+ """Check tool-specific integrity constraints."""
432
+ result = CheckResult('tool_integrity')
433
+
434
+ try:
435
+ # cURL Tool specific checks
436
+ if tool_name == 'cURL Tool':
437
+ return self._validate_curl_tool_integrity(settings)
438
+
439
+ # AI Tools specific checks
440
+ elif tool_name in ('Google AI', 'OpenAI', 'Anthropic'):
441
+ return self._validate_ai_tool_integrity(settings)
442
+
443
+ # JSON/XML Tool specific checks
444
+ elif tool_name == 'JSON/XML Tool':
445
+ return self._validate_json_xml_tool_integrity(settings)
446
+
447
+ # Default: basic integrity check
448
+ result.passed = True
449
+ result.message = "Basic integrity check passed"
450
+ return result
451
+
452
+ except Exception as e:
453
+ result.passed = False
454
+ result.message = f"Tool integrity check error: {e}"
455
+ return result
456
+
457
+ def _validate_curl_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
458
+ """Validate cURL tool specific integrity."""
459
+ result = CheckResult('curl_integrity')
460
+
461
+ try:
462
+ # Check timeout is reasonable
463
+ timeout = settings.get('default_timeout', 90)
464
+ if not isinstance(timeout, (int, float)) or timeout <= 0 or timeout > 3600:
465
+ result.passed = False
466
+ result.message = "Default timeout must be between 1 and 3600 seconds"
467
+ return result
468
+
469
+ # Check max_redirects is reasonable
470
+ max_redirects = settings.get('max_redirects', 10)
471
+ if not isinstance(max_redirects, int) or max_redirects < 0 or max_redirects > 50:
472
+ result.passed = False
473
+ result.message = "Max redirects must be between 0 and 50"
474
+ return result
475
+
476
+ # Check history settings
477
+ max_history = settings.get('max_history_items', 100)
478
+ if not isinstance(max_history, int) or max_history < 0 or max_history > 10000:
479
+ result.passed = False
480
+ result.message = "Max history items must be between 0 and 10000"
481
+ return result
482
+
483
+ # Validate history structure if present
484
+ history = settings.get('history', [])
485
+ if not isinstance(history, list):
486
+ result.passed = False
487
+ result.message = "History must be a list"
488
+ return result
489
+
490
+ # Check history entries structure
491
+ for i, entry in enumerate(history[:5]): # Check first 5 entries
492
+ if not isinstance(entry, dict):
493
+ result.passed = False
494
+ result.message = f"History entry {i} must be a dictionary"
495
+ return result
496
+
497
+ required_fields = {'timestamp', 'method', 'url', 'status_code'}
498
+ missing_fields = required_fields - set(entry.keys())
499
+ if missing_fields:
500
+ result.passed = False
501
+ result.message = f"History entry {i} missing fields: {missing_fields}"
502
+ return result
503
+
504
+ result.passed = True
505
+ result.message = "cURL tool integrity validated"
506
+ return result
507
+
508
+ except Exception as e:
509
+ result.passed = False
510
+ result.message = f"cURL integrity validation error: {e}"
511
+ return result
512
+
513
+ def _validate_ai_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
514
+ """Validate AI tool specific integrity."""
515
+ result = CheckResult('ai_tool_integrity')
516
+
517
+ try:
518
+ # Check temperature range
519
+ temperature = settings.get('temperature', 0.7)
520
+ if not isinstance(temperature, (int, float)) or not (0.0 <= temperature <= 2.0):
521
+ result.passed = False
522
+ result.message = "Temperature must be between 0.0 and 2.0"
523
+ return result
524
+
525
+ # Check token limits
526
+ max_tokens = settings.get('maxOutputTokens', 8192)
527
+ if not isinstance(max_tokens, int) or max_tokens <= 0 or max_tokens > 100000:
528
+ result.passed = False
529
+ result.message = "Max output tokens must be between 1 and 100000"
530
+ return result
531
+
532
+ # Check model list
533
+ models_list = settings.get('MODELS_LIST', [])
534
+ if not isinstance(models_list, list) or not models_list:
535
+ result.passed = False
536
+ result.message = "Models list must be non-empty list"
537
+ return result
538
+
539
+ # Check current model is in list
540
+ current_model = settings.get('MODEL')
541
+ if current_model and current_model not in models_list:
542
+ result.passed = False
543
+ result.message = "Current model must be in models list"
544
+ return result
545
+
546
+ result.passed = True
547
+ result.message = "AI tool integrity validated"
548
+ return result
549
+
550
+ except Exception as e:
551
+ result.passed = False
552
+ result.message = f"AI tool integrity validation error: {e}"
553
+ return result
554
+
555
+ def _validate_json_xml_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
556
+ """Validate JSON/XML tool specific integrity."""
557
+ result = CheckResult('json_xml_integrity')
558
+
559
+ try:
560
+ # Check indent values
561
+ for indent_key in ('json_indent', 'xml_indent'):
562
+ indent = settings.get(indent_key, 2)
563
+ if not isinstance(indent, int) or indent < 0 or indent > 10:
564
+ result.passed = False
565
+ result.message = f"{indent_key} must be between 0 and 10"
566
+ return result
567
+
568
+ # Check operation is valid
569
+ operation = settings.get('operation')
570
+ valid_operations = {
571
+ 'json_to_xml', 'xml_to_json', 'format_json', 'format_xml',
572
+ 'minify_json', 'validate_json', 'validate_xml'
573
+ }
574
+ if operation and operation not in valid_operations:
575
+ result.passed = False
576
+ result.message = f"Operation must be one of {valid_operations}"
577
+ return result
578
+
579
+ result.passed = True
580
+ result.message = "JSON/XML tool integrity validated"
581
+ return result
582
+
583
+ except Exception as e:
584
+ result.passed = False
585
+ result.message = f"JSON/XML integrity validation error: {e}"
586
+ return result
587
+
588
+ def _validate_tab_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
589
+ """Validate tab-related consistency."""
590
+ result = CheckResult('tab_consistency')
591
+
592
+ try:
593
+ # Check tab arrays exist and have correct length
594
+ input_tabs = settings.get('input_tabs', [])
595
+ output_tabs = settings.get('output_tabs', [])
596
+
597
+ if not isinstance(input_tabs, list) or len(input_tabs) != 7:
598
+ result.passed = False
599
+ result.message = "input_tabs must be list of length 7"
600
+ return result
601
+
602
+ if not isinstance(output_tabs, list) or len(output_tabs) != 7:
603
+ result.passed = False
604
+ result.message = "output_tabs must be list of length 7"
605
+ return result
606
+
607
+ # Check active tab indices are valid
608
+ active_input = settings.get('active_input_tab', 0)
609
+ active_output = settings.get('active_output_tab', 0)
610
+
611
+ if not isinstance(active_input, int) or not (0 <= active_input < 7):
612
+ result.passed = False
613
+ result.message = "active_input_tab must be integer between 0 and 6"
614
+ return result
615
+
616
+ if not isinstance(active_output, int) or not (0 <= active_output < 7):
617
+ result.passed = False
618
+ result.message = "active_output_tab must be integer between 0 and 6"
619
+ return result
620
+
621
+ result.passed = True
622
+ result.message = "Tab consistency validated"
623
+ return result
624
+
625
+ except Exception as e:
626
+ result.passed = False
627
+ result.message = f"Tab consistency validation error: {e}"
628
+ return result
629
+
630
+ def _validate_tool_selection_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
631
+ """Validate tool selection consistency."""
632
+ result = CheckResult('tool_selection_consistency')
633
+
634
+ try:
635
+ selected_tool = settings.get('selected_tool')
636
+ tool_settings = settings.get('tool_settings', {})
637
+
638
+ if selected_tool and selected_tool not in tool_settings:
639
+ result.passed = False
640
+ result.message = f"Selected tool '{selected_tool}' not found in tool_settings"
641
+ return result
642
+
643
+ result.passed = True
644
+ result.message = "Tool selection consistency validated"
645
+ return result
646
+
647
+ except Exception as e:
648
+ result.passed = False
649
+ result.message = f"Tool selection consistency validation error: {e}"
650
+ return result
651
+
652
+ def _validate_performance_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
653
+ """Validate performance settings consistency."""
654
+ result = CheckResult('performance_consistency')
655
+
656
+ try:
657
+ perf_settings = settings.get('performance_settings', {})
658
+
659
+ if not isinstance(perf_settings, dict):
660
+ result.passed = True # Optional settings
661
+ result.message = "Performance settings not configured"
662
+ return result
663
+
664
+ # Check mode consistency
665
+ mode = perf_settings.get('mode', 'automatic')
666
+ if mode not in ('automatic', 'manual', 'disabled'):
667
+ result.passed = False
668
+ result.message = "Performance mode must be 'automatic', 'manual', or 'disabled'"
669
+ return result
670
+
671
+ # If disabled, other settings should be disabled too
672
+ if mode == 'disabled':
673
+ for category in ('async_processing', 'caching', 'memory_management', 'ui_optimizations'):
674
+ category_settings = perf_settings.get(category, {})
675
+ if isinstance(category_settings, dict) and category_settings.get('enabled', False):
676
+ result.passed = False
677
+ result.message = f"Performance mode is disabled but {category} is enabled"
678
+ return result
679
+
680
+ result.passed = True
681
+ result.message = "Performance consistency validated"
682
+ return result
683
+
684
+ except Exception as e:
685
+ result.passed = False
686
+ result.message = f"Performance consistency validation error: {e}"
687
+ return result
688
+
689
+ def _validate_font_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
690
+ """Validate font settings consistency."""
691
+ result = CheckResult('font_consistency')
692
+
693
+ try:
694
+ font_settings = settings.get('font_settings', {})
695
+
696
+ if not isinstance(font_settings, dict):
697
+ result.passed = True # Optional settings
698
+ result.message = "Font settings not configured"
699
+ return result
700
+
701
+ # Check required font types
702
+ for font_type in ('text_font', 'interface_font'):
703
+ font_config = font_settings.get(font_type, {})
704
+ if not isinstance(font_config, dict):
705
+ continue
706
+
707
+ # Check required properties
708
+ if 'family' in font_config and 'size' in font_config:
709
+ size = font_config['size']
710
+ if not isinstance(size, (int, float)) or size <= 0 or size > 72:
711
+ result.passed = False
712
+ result.message = f"{font_type} size must be between 1 and 72"
713
+ return result
714
+
715
+ result.passed = True
716
+ result.message = "Font consistency validated"
717
+ return result
718
+
719
+ except Exception as e:
720
+ result.passed = False
721
+ result.message = f"Font consistency validation error: {e}"
722
+ return result
723
+
724
+ def _validate_dialog_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
725
+ """Validate dialog settings consistency."""
726
+ result = CheckResult('dialog_consistency')
727
+
728
+ try:
729
+ dialog_settings = settings.get('dialog_settings', {})
730
+
731
+ if not isinstance(dialog_settings, dict):
732
+ result.passed = True # Optional settings
733
+ result.message = "Dialog settings not configured"
734
+ return result
735
+
736
+ # Error dialogs must always be enabled and locked
737
+ error_config = dialog_settings.get('error', {})
738
+ if isinstance(error_config, dict):
739
+ if not error_config.get('enabled', True):
740
+ result.passed = False
741
+ result.message = "Error dialogs must be enabled"
742
+ return result
743
+
744
+ if not error_config.get('locked', True):
745
+ result.passed = False
746
+ result.message = "Error dialogs must be locked"
747
+ return result
748
+
749
+ result.passed = True
750
+ result.message = "Dialog consistency validated"
751
+ return result
752
+
753
+ except Exception as e:
754
+ result.passed = False
755
+ result.message = f"Dialog consistency validation error: {e}"
756
+ return result
757
+
758
+ def _initialize_core_validation_rules(self):
759
+ """Initialize validation rules for core settings."""
760
+ self.validation_rules.update({
761
+ 'debug_level': lambda v, c: v in {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'},
762
+ 'active_input_tab': lambda v, c: isinstance(v, int) and 0 <= v < 7,
763
+ 'active_output_tab': lambda v, c: isinstance(v, int) and 0 <= v < 7,
764
+ 'export_path': lambda v, c: isinstance(v, str) and v.strip(),
765
+ 'selected_tool': lambda v, c: isinstance(v, str) and v.strip()
766
+ })
767
+
768
+ def _initialize_tool_validation_rules(self):
769
+ """Initialize validation rules for tool-specific settings."""
770
+ # cURL Tool validation rules
771
+ self.validation_rules.update({
772
+ 'cURL Tool.default_timeout': lambda v, c: isinstance(v, (int, float)) and 1 <= v <= 3600,
773
+ 'cURL Tool.max_redirects': lambda v, c: isinstance(v, int) and 0 <= v <= 50,
774
+ 'cURL Tool.max_history_items': lambda v, c: isinstance(v, int) and 0 <= v <= 10000,
775
+ 'cURL Tool.history_retention_days': lambda v, c: isinstance(v, int) and 1 <= v <= 365,
776
+ 'cURL Tool.auth_timeout_minutes': lambda v, c: isinstance(v, int) and 1 <= v <= 1440,
777
+ 'cURL Tool.download_chunk_size': lambda v, c: isinstance(v, int) and 1024 <= v <= 1048576,
778
+ 'cURL Tool.max_log_size_mb': lambda v, c: isinstance(v, (int, float)) and 1 <= v <= 100,
779
+ 'cURL Tool.connection_pool_size': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
780
+ 'cURL Tool.retry_attempts': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
781
+ 'cURL Tool.retry_delay_seconds': lambda v, c: isinstance(v, (int, float)) and 0 <= v <= 60,
782
+ })
783
+
784
+ # AI Tools validation rules
785
+ ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI', 'AWS Bedrock', 'LM Studio']
786
+ for tool in ai_tools:
787
+ self.validation_rules.update({
788
+ f'{tool}.temperature': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 2.0,
789
+ f'{tool}.max_tokens': lambda v, c: isinstance(v, int) and 1 <= v <= 100000,
790
+ f'{tool}.maxOutputTokens': lambda v, c: isinstance(v, int) and 1 <= v <= 100000,
791
+ f'{tool}.MAX_TOKENS': lambda v, c: isinstance(v, str) and v.isdigit() and 1 <= int(v) <= 100000,
792
+ f'{tool}.CONTEXT_WINDOW': lambda v, c: isinstance(v, str) and v.isdigit() and 1 <= int(v) <= 1000000,
793
+ f'{tool}.topK': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
794
+ f'{tool}.top_k': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
795
+ f'{tool}.topP': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 1.0,
796
+ f'{tool}.top_p': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 1.0,
797
+ f'{tool}.candidateCount': lambda v, c: isinstance(v, int) and 1 <= v <= 10,
798
+ f'{tool}.frequency_penalty': lambda v, c: isinstance(v, (int, float)) and -2.0 <= v <= 2.0,
799
+ f'{tool}.presence_penalty': lambda v, c: isinstance(v, (int, float)) and -2.0 <= v <= 2.0,
800
+ f'{tool}.repetition_penalty': lambda v, c: isinstance(v, (int, float)) and 0.1 <= v <= 2.0,
801
+ })
802
+
803
+ # JSON/XML Tool validation rules
804
+ self.validation_rules.update({
805
+ 'JSON/XML Tool.json_indent': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
806
+ 'JSON/XML Tool.xml_indent': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
807
+ 'JSON/XML Tool.operation': lambda v, c: v in {
808
+ 'json_to_xml', 'xml_to_json', 'format_json', 'format_xml',
809
+ 'minify_json', 'validate_json', 'validate_xml'
810
+ },
811
+ })
812
+
813
+ # Generator Tools validation rules
814
+ self.validation_rules.update({
815
+ 'Generator Tools.Strong Password Generator.length': lambda v, c: isinstance(v, int) and 4 <= v <= 128,
816
+ 'Generator Tools.Strong Password Generator.letters_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
817
+ 'Generator Tools.Strong Password Generator.numbers_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
818
+ 'Generator Tools.Strong Password Generator.symbols_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
819
+ 'Generator Tools.Repeating Text Generator.times': lambda v, c: isinstance(v, int) and 1 <= v <= 10000,
820
+ 'Generator Tools.Lorem Ipsum Generator.count': lambda v, c: isinstance(v, int) and 1 <= v <= 1000,
821
+ 'Generator Tools.UUID/GUID Generator.version': lambda v, c: v in {1, 3, 4, 5},
822
+ 'Generator Tools.UUID/GUID Generator.count': lambda v, c: isinstance(v, int) and 1 <= v <= 1000,
823
+ })
824
+
825
+ # Folder File Reporter validation rules
826
+ self.validation_rules.update({
827
+ 'Folder File Reporter.recursion_depth': lambda v, c: isinstance(v, int) and 0 <= v <= 20,
828
+ 'Folder File Reporter.recursion_mode': lambda v, c: v in {'none', 'single', 'full'},
829
+ 'Folder File Reporter.size_format': lambda v, c: v in {'bytes', 'human', 'kb', 'mb', 'gb'},
830
+ })
831
+
832
+ # Cron Tool validation rules
833
+ self.validation_rules.update({
834
+ 'Cron Tool.next_runs_count': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
835
+ 'Cron Tool.action': lambda v, c: v in {'parse_explain', 'generate', 'validate'},
836
+ })
837
+
838
+ def _initialize_integrity_rules(self):
839
+ """Initialize integrity checking rules."""
840
+ # Critical settings that must always be present and valid
841
+ self.integrity_rules.update({
842
+ 'tab_arrays_length': self._check_tab_arrays_integrity,
843
+ 'active_tab_indices': self._check_active_tab_integrity,
844
+ 'tool_settings_structure': self._check_tool_settings_structure,
845
+ 'performance_settings_hierarchy': self._check_performance_hierarchy,
846
+ 'font_settings_completeness': self._check_font_settings_completeness,
847
+ 'dialog_settings_locked_fields': self._check_dialog_locked_fields,
848
+ 'ai_model_consistency': self._check_ai_model_consistency,
849
+ 'curl_history_structure': self._check_curl_history_structure,
850
+ 'encrypted_data_format': self._check_encrypted_data_format,
851
+ 'generator_tools_percentages': self._check_generator_percentages,
852
+ })
853
+
854
+ def _check_tab_arrays_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
855
+ """Check that tab arrays have correct structure and length."""
856
+ result = CheckResult('tab_arrays_integrity')
857
+
858
+ try:
859
+ input_tabs = settings.get('input_tabs', [])
860
+ output_tabs = settings.get('output_tabs', [])
861
+
862
+ # Check arrays exist and are lists
863
+ if not isinstance(input_tabs, list):
864
+ result.passed = False
865
+ result.message = "input_tabs must be a list"
866
+ return result
867
+
868
+ if not isinstance(output_tabs, list):
869
+ result.passed = False
870
+ result.message = "output_tabs must be a list"
871
+ return result
872
+
873
+ # Check correct length (must be exactly 7)
874
+ if len(input_tabs) != 7:
875
+ result.passed = False
876
+ result.message = f"input_tabs must have exactly 7 elements, found {len(input_tabs)}"
877
+ return result
878
+
879
+ if len(output_tabs) != 7:
880
+ result.passed = False
881
+ result.message = f"output_tabs must have exactly 7 elements, found {len(output_tabs)}"
882
+ return result
883
+
884
+ # Check all elements are strings
885
+ for i, tab in enumerate(input_tabs):
886
+ if not isinstance(tab, str):
887
+ result.passed = False
888
+ result.message = f"input_tabs[{i}] must be string, found {type(tab).__name__}"
889
+ return result
890
+
891
+ for i, tab in enumerate(output_tabs):
892
+ if not isinstance(tab, str):
893
+ result.passed = False
894
+ result.message = f"output_tabs[{i}] must be string, found {type(tab).__name__}"
895
+ return result
896
+
897
+ result.passed = True
898
+ result.message = "Tab arrays integrity validated"
899
+ return result
900
+
901
+ except Exception as e:
902
+ result.passed = False
903
+ result.message = f"Tab arrays integrity check error: {e}"
904
+ return result
905
+
906
+ def _check_active_tab_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
907
+ """Check that active tab indices are valid."""
908
+ result = CheckResult('active_tab_integrity')
909
+
910
+ try:
911
+ active_input = settings.get('active_input_tab', 0)
912
+ active_output = settings.get('active_output_tab', 0)
913
+
914
+ # Check types
915
+ if not isinstance(active_input, int):
916
+ result.passed = False
917
+ result.message = f"active_input_tab must be integer, found {type(active_input).__name__}"
918
+ return result
919
+
920
+ if not isinstance(active_output, int):
921
+ result.passed = False
922
+ result.message = f"active_output_tab must be integer, found {type(active_output).__name__}"
923
+ return result
924
+
925
+ # Check ranges (0-6 for 7 tabs)
926
+ if not (0 <= active_input < 7):
927
+ result.passed = False
928
+ result.message = f"active_input_tab must be 0-6, found {active_input}"
929
+ return result
930
+
931
+ if not (0 <= active_output < 7):
932
+ result.passed = False
933
+ result.message = f"active_output_tab must be 0-6, found {active_output}"
934
+ return result
935
+
936
+ result.passed = True
937
+ result.message = "Active tab indices validated"
938
+ return result
939
+
940
+ except Exception as e:
941
+ result.passed = False
942
+ result.message = f"Active tab integrity check error: {e}"
943
+ return result
944
+
945
+ def _check_tool_settings_structure(self, settings: Dict[str, Any]) -> 'CheckResult':
946
+ """Check that tool_settings has proper structure."""
947
+ result = CheckResult('tool_settings_structure')
948
+
949
+ try:
950
+ tool_settings = settings.get('tool_settings', {})
951
+
952
+ if not isinstance(tool_settings, dict):
953
+ result.passed = False
954
+ result.message = "tool_settings must be a dictionary"
955
+ return result
956
+
957
+ # Check that each tool's settings is a dictionary
958
+ for tool_name, tool_config in tool_settings.items():
959
+ if not isinstance(tool_name, str):
960
+ result.passed = False
961
+ result.message = f"Tool name must be string, found {type(tool_name).__name__}"
962
+ return result
963
+
964
+ if not isinstance(tool_config, dict):
965
+ result.passed = False
966
+ result.message = f"Tool '{tool_name}' settings must be dictionary, found {type(tool_config).__name__}"
967
+ return result
968
+
969
+ result.passed = True
970
+ result.message = "Tool settings structure validated"
971
+ return result
972
+
973
+ except Exception as e:
974
+ result.passed = False
975
+ result.message = f"Tool settings structure check error: {e}"
976
+ return result
977
+
978
+ def _check_performance_hierarchy(self, settings: Dict[str, Any]) -> 'CheckResult':
979
+ """Check performance settings hierarchy integrity."""
980
+ result = CheckResult('performance_hierarchy')
981
+
982
+ try:
983
+ perf_settings = settings.get('performance_settings', {})
984
+
985
+ if not isinstance(perf_settings, dict):
986
+ result.passed = True # Optional settings
987
+ result.message = "Performance settings not configured"
988
+ return result
989
+
990
+ # Check required categories exist and are dictionaries
991
+ required_categories = ['async_processing', 'caching', 'memory_management', 'ui_optimizations']
992
+ for category in required_categories:
993
+ if category in perf_settings:
994
+ category_settings = perf_settings[category]
995
+ if not isinstance(category_settings, dict):
996
+ result.passed = False
997
+ result.message = f"Performance category '{category}' must be dictionary"
998
+ return result
999
+
1000
+ # Check 'enabled' field exists and is boolean
1001
+ if 'enabled' in category_settings:
1002
+ enabled = category_settings['enabled']
1003
+ if not isinstance(enabled, bool):
1004
+ result.passed = False
1005
+ result.message = f"Performance category '{category}.enabled' must be boolean"
1006
+ return result
1007
+
1008
+ result.passed = True
1009
+ result.message = "Performance hierarchy validated"
1010
+ return result
1011
+
1012
+ except Exception as e:
1013
+ result.passed = False
1014
+ result.message = f"Performance hierarchy check error: {e}"
1015
+ return result
1016
+
1017
+ def _check_font_settings_completeness(self, settings: Dict[str, Any]) -> 'CheckResult':
1018
+ """Check font settings completeness."""
1019
+ result = CheckResult('font_settings_completeness')
1020
+
1021
+ try:
1022
+ font_settings = settings.get('font_settings', {})
1023
+
1024
+ if not isinstance(font_settings, dict):
1025
+ result.passed = True # Optional settings
1026
+ result.message = "Font settings not configured"
1027
+ return result
1028
+
1029
+ # Check required font types
1030
+ required_fonts = ['text_font', 'interface_font']
1031
+ for font_type in required_fonts:
1032
+ if font_type in font_settings:
1033
+ font_config = font_settings[font_type]
1034
+ if not isinstance(font_config, dict):
1035
+ result.passed = False
1036
+ result.message = f"Font type '{font_type}' must be dictionary"
1037
+ return result
1038
+
1039
+ # Check required properties
1040
+ required_props = ['family', 'size']
1041
+ for prop in required_props:
1042
+ if prop not in font_config:
1043
+ result.passed = False
1044
+ result.message = f"Font '{font_type}' missing required property '{prop}'"
1045
+ return result
1046
+
1047
+ if prop == 'size':
1048
+ size = font_config[prop]
1049
+ if not isinstance(size, (int, float)) or size <= 0 or size > 72:
1050
+ result.passed = False
1051
+ result.message = f"Font '{font_type}' size must be 1-72, found {size}"
1052
+ return result
1053
+
1054
+ result.passed = True
1055
+ result.message = "Font settings completeness validated"
1056
+ return result
1057
+
1058
+ except Exception as e:
1059
+ result.passed = False
1060
+ result.message = f"Font settings completeness check error: {e}"
1061
+ return result
1062
+
1063
+ def _check_dialog_locked_fields(self, settings: Dict[str, Any]) -> 'CheckResult':
1064
+ """Check that dialog settings locked fields are properly configured."""
1065
+ result = CheckResult('dialog_locked_fields')
1066
+
1067
+ try:
1068
+ dialog_settings = settings.get('dialog_settings', {})
1069
+
1070
+ if not isinstance(dialog_settings, dict):
1071
+ result.passed = True # Optional settings
1072
+ result.message = "Dialog settings not configured"
1073
+ return result
1074
+
1075
+ # Error dialogs must be enabled and locked
1076
+ if 'error' in dialog_settings:
1077
+ error_config = dialog_settings['error']
1078
+ if isinstance(error_config, dict):
1079
+ # Must be enabled
1080
+ if not error_config.get('enabled', True):
1081
+ result.passed = False
1082
+ result.message = "Error dialogs must be enabled"
1083
+ return result
1084
+
1085
+ # Must be locked
1086
+ if not error_config.get('locked', True):
1087
+ result.passed = False
1088
+ result.message = "Error dialogs must be locked"
1089
+ return result
1090
+
1091
+ result.passed = True
1092
+ result.message = "Dialog locked fields validated"
1093
+ return result
1094
+
1095
+ except Exception as e:
1096
+ result.passed = False
1097
+ result.message = f"Dialog locked fields check error: {e}"
1098
+ return result
1099
+
1100
+ def _check_ai_model_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
1101
+ """Check AI model consistency across tools."""
1102
+ result = CheckResult('ai_model_consistency')
1103
+
1104
+ try:
1105
+ tool_settings = settings.get('tool_settings', {})
1106
+ ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI', 'AWS Bedrock', 'LM Studio']
1107
+
1108
+ for tool_name in ai_tools:
1109
+ if tool_name in tool_settings:
1110
+ tool_config = tool_settings[tool_name]
1111
+ if isinstance(tool_config, dict):
1112
+ # Check model is in models list
1113
+ current_model = tool_config.get('MODEL')
1114
+ models_list = tool_config.get('MODELS_LIST', [])
1115
+
1116
+ if current_model and isinstance(models_list, list) and models_list:
1117
+ if current_model not in models_list:
1118
+ result.passed = False
1119
+ result.message = f"AI tool '{tool_name}' current model '{current_model}' not in models list"
1120
+ return result
1121
+
1122
+ # Check models list is not empty
1123
+ if 'MODELS_LIST' in tool_config:
1124
+ if not isinstance(models_list, list) or not models_list:
1125
+ result.passed = False
1126
+ result.message = f"AI tool '{tool_name}' models list must be non-empty list"
1127
+ return result
1128
+
1129
+ result.passed = True
1130
+ result.message = "AI model consistency validated"
1131
+ return result
1132
+
1133
+ except Exception as e:
1134
+ result.passed = False
1135
+ result.message = f"AI model consistency check error: {e}"
1136
+ return result
1137
+
1138
+ def _check_curl_history_structure(self, settings: Dict[str, Any]) -> 'CheckResult':
1139
+ """Check cURL history structure integrity."""
1140
+ result = CheckResult('curl_history_structure')
1141
+
1142
+ try:
1143
+ tool_settings = settings.get('tool_settings', {})
1144
+ curl_settings = tool_settings.get('cURL Tool', {})
1145
+
1146
+ if not isinstance(curl_settings, dict):
1147
+ result.passed = True # Tool not configured
1148
+ result.message = "cURL Tool not configured"
1149
+ return result
1150
+
1151
+ history = curl_settings.get('history', [])
1152
+ if not isinstance(history, list):
1153
+ result.passed = False
1154
+ result.message = "cURL history must be a list"
1155
+ return result
1156
+
1157
+ # Check history entries structure (first 5 entries)
1158
+ required_fields = {'timestamp', 'method', 'url', 'status_code', 'success'}
1159
+ for i, entry in enumerate(history[:5]):
1160
+ if not isinstance(entry, dict):
1161
+ result.passed = False
1162
+ result.message = f"cURL history entry {i} must be dictionary"
1163
+ return result
1164
+
1165
+ missing_fields = required_fields - set(entry.keys())
1166
+ if missing_fields:
1167
+ result.passed = False
1168
+ result.message = f"cURL history entry {i} missing fields: {missing_fields}"
1169
+ return result
1170
+
1171
+ # Validate timestamp format
1172
+ timestamp = entry.get('timestamp')
1173
+ if isinstance(timestamp, str):
1174
+ try:
1175
+ datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
1176
+ except ValueError:
1177
+ result.passed = False
1178
+ result.message = f"cURL history entry {i} has invalid timestamp format"
1179
+ return result
1180
+
1181
+ result.passed = True
1182
+ result.message = "cURL history structure validated"
1183
+ return result
1184
+
1185
+ except Exception as e:
1186
+ result.passed = False
1187
+ result.message = f"cURL history structure check error: {e}"
1188
+ return result
1189
+
1190
+ def _check_encrypted_data_format(self, settings: Dict[str, Any]) -> 'CheckResult':
1191
+ """Check encrypted data format integrity."""
1192
+ result = CheckResult('encrypted_data_format')
1193
+
1194
+ try:
1195
+ # Recursively check for encrypted values
1196
+ encrypted_count = 0
1197
+ invalid_encrypted = []
1198
+
1199
+ def check_encrypted_recursive(obj, path=""):
1200
+ nonlocal encrypted_count, invalid_encrypted
1201
+
1202
+ if isinstance(obj, dict):
1203
+ for key, value in obj.items():
1204
+ current_path = f"{path}.{key}" if path else key
1205
+ if isinstance(value, str) and value.startswith('ENC:'):
1206
+ encrypted_count += 1
1207
+ # Validate base64 format after ENC: prefix
1208
+ encrypted_data = value[4:] # Remove 'ENC:' prefix
1209
+ try:
1210
+ import base64
1211
+ base64.b64decode(encrypted_data, validate=True)
1212
+ except Exception:
1213
+ invalid_encrypted.append(current_path)
1214
+ elif isinstance(value, (dict, list)):
1215
+ check_encrypted_recursive(value, current_path)
1216
+ elif isinstance(obj, list):
1217
+ for i, item in enumerate(obj):
1218
+ current_path = f"{path}[{i}]" if path else f"[{i}]"
1219
+ check_encrypted_recursive(item, current_path)
1220
+
1221
+ check_encrypted_recursive(settings)
1222
+
1223
+ if invalid_encrypted:
1224
+ result.passed = False
1225
+ result.message = f"Invalid encrypted data format in: {invalid_encrypted}"
1226
+ return result
1227
+
1228
+ result.passed = True
1229
+ result.message = f"Encrypted data format validated ({encrypted_count} encrypted values found)"
1230
+ return result
1231
+
1232
+ except Exception as e:
1233
+ result.passed = False
1234
+ result.message = f"Encrypted data format check error: {e}"
1235
+ return result
1236
+
1237
+ def _check_generator_percentages(self, settings: Dict[str, Any]) -> 'CheckResult':
1238
+ """Check generator tools percentage consistency."""
1239
+ result = CheckResult('generator_percentages')
1240
+
1241
+ try:
1242
+ tool_settings = settings.get('tool_settings', {})
1243
+ generator_tools = tool_settings.get('Generator Tools', {})
1244
+
1245
+ if not isinstance(generator_tools, dict):
1246
+ result.passed = True # Tool not configured
1247
+ result.message = "Generator Tools not configured"
1248
+ return result
1249
+
1250
+ password_gen = generator_tools.get('Strong Password Generator', {})
1251
+ if isinstance(password_gen, dict):
1252
+ letters_pct = password_gen.get('letters_percent', 0)
1253
+ numbers_pct = password_gen.get('numbers_percent', 0)
1254
+ symbols_pct = password_gen.get('symbols_percent', 0)
1255
+
1256
+ # Check percentages are valid
1257
+ if not all(isinstance(pct, int) and 0 <= pct <= 100 for pct in [letters_pct, numbers_pct, symbols_pct]):
1258
+ result.passed = False
1259
+ result.message = "Password generator percentages must be integers 0-100"
1260
+ return result
1261
+
1262
+ # Check percentages sum to 100
1263
+ total = letters_pct + numbers_pct + symbols_pct
1264
+ if total != 100:
1265
+ result.passed = False
1266
+ result.message = f"Password generator percentages must sum to 100, found {total}"
1267
+ return result
1268
+
1269
+ result.passed = True
1270
+ result.message = "Generator percentages validated"
1271
+ return result
1272
+
1273
+ except Exception as e:
1274
+ result.passed = False
1275
+ result.message = f"Generator percentages check error: {e}"
1276
+ return result
1277
+
1278
+ def _initialize_consistency_rules(self):
1279
+ """Initialize consistency checking rules."""
1280
+ # Cross-setting consistency rules
1281
+ self.consistency_rules.update({
1282
+ 'selected_tool_exists': self._check_selected_tool_exists,
1283
+ 'performance_mode_consistency': self._check_performance_mode_consistency,
1284
+ 'ai_tools_api_keys': self._check_ai_tools_api_keys,
1285
+ 'curl_timeout_consistency': self._check_curl_timeout_consistency,
1286
+ 'font_fallback_consistency': self._check_font_fallback_consistency,
1287
+ 'dialog_hierarchy_consistency': self._check_dialog_hierarchy_consistency,
1288
+ 'generator_tools_completeness': self._check_generator_tools_completeness,
1289
+ 'folder_reporter_path_consistency': self._check_folder_reporter_paths,
1290
+ })
1291
+
1292
+ def _check_selected_tool_exists(self, settings: Dict[str, Any]) -> 'CheckResult':
1293
+ """Check that selected tool exists in tool_settings."""
1294
+ result = CheckResult('selected_tool_exists')
1295
+
1296
+ try:
1297
+ selected_tool = settings.get('selected_tool')
1298
+ tool_settings = settings.get('tool_settings', {})
1299
+
1300
+ if selected_tool:
1301
+ if not isinstance(selected_tool, str):
1302
+ result.passed = False
1303
+ result.message = "selected_tool must be string"
1304
+ return result
1305
+
1306
+ if selected_tool not in tool_settings:
1307
+ result.passed = False
1308
+ result.message = f"Selected tool '{selected_tool}' not found in tool_settings"
1309
+ return result
1310
+
1311
+ result.passed = True
1312
+ result.message = "Selected tool consistency validated"
1313
+ return result
1314
+
1315
+ except Exception as e:
1316
+ result.passed = False
1317
+ result.message = f"Selected tool consistency check error: {e}"
1318
+ return result
1319
+
1320
+ def _check_performance_mode_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
1321
+ """Check performance mode consistency across categories."""
1322
+ result = CheckResult('performance_mode_consistency')
1323
+
1324
+ try:
1325
+ perf_settings = settings.get('performance_settings', {})
1326
+
1327
+ if not isinstance(perf_settings, dict):
1328
+ result.passed = True
1329
+ result.message = "Performance settings not configured"
1330
+ return result
1331
+
1332
+ mode = perf_settings.get('mode', 'automatic')
1333
+
1334
+ # If mode is disabled, all categories should be disabled
1335
+ if mode == 'disabled':
1336
+ categories = ['async_processing', 'caching', 'memory_management', 'ui_optimizations']
1337
+ for category in categories:
1338
+ category_settings = perf_settings.get(category, {})
1339
+ if isinstance(category_settings, dict) and category_settings.get('enabled', False):
1340
+ result.passed = False
1341
+ result.message = f"Performance mode is disabled but {category} is enabled"
1342
+ return result
1343
+
1344
+ result.passed = True
1345
+ result.message = "Performance mode consistency validated"
1346
+ return result
1347
+
1348
+ except Exception as e:
1349
+ result.passed = False
1350
+ result.message = f"Performance mode consistency check error: {e}"
1351
+ return result
1352
+
1353
+ def _check_ai_tools_api_keys(self, settings: Dict[str, Any]) -> 'CheckResult':
1354
+ """Check AI tools have proper API key configuration."""
1355
+ result = CheckResult('ai_tools_api_keys')
1356
+
1357
+ try:
1358
+ tool_settings = settings.get('tool_settings', {})
1359
+ ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI']
1360
+
1361
+ missing_keys = []
1362
+ placeholder_keys = []
1363
+
1364
+ for tool_name in ai_tools:
1365
+ if tool_name in tool_settings:
1366
+ tool_config = tool_settings[tool_name]
1367
+ if isinstance(tool_config, dict):
1368
+ api_key = tool_config.get('API_KEY', '')
1369
+
1370
+ if not api_key:
1371
+ missing_keys.append(tool_name)
1372
+ elif api_key == 'putinyourkey':
1373
+ placeholder_keys.append(tool_name)
1374
+
1375
+ # This is a warning, not an error - users might not have all API keys
1376
+ if missing_keys or placeholder_keys:
1377
+ result.passed = True # Don't fail validation
1378
+ result.severity = 'warning'
1379
+ warnings = []
1380
+ if missing_keys:
1381
+ warnings.append(f"Missing API keys: {missing_keys}")
1382
+ if placeholder_keys:
1383
+ warnings.append(f"Placeholder API keys: {placeholder_keys}")
1384
+ result.message = "; ".join(warnings)
1385
+ else:
1386
+ result.passed = True
1387
+ result.message = "AI tools API keys validated"
1388
+
1389
+ return result
1390
+
1391
+ except Exception as e:
1392
+ result.passed = False
1393
+ result.message = f"AI tools API keys check error: {e}"
1394
+ return result
1395
+
1396
+ def _check_curl_timeout_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
1397
+ """Check cURL timeout settings consistency."""
1398
+ result = CheckResult('curl_timeout_consistency')
1399
+
1400
+ try:
1401
+ tool_settings = settings.get('tool_settings', {})
1402
+ curl_settings = tool_settings.get('cURL Tool', {})
1403
+
1404
+ if not isinstance(curl_settings, dict):
1405
+ result.passed = True
1406
+ result.message = "cURL Tool not configured"
1407
+ return result
1408
+
1409
+ default_timeout = curl_settings.get('default_timeout', 90)
1410
+ auth_timeout_minutes = curl_settings.get('auth_timeout_minutes', 60)
1411
+
1412
+ # Convert auth timeout to seconds for comparison
1413
+ auth_timeout_seconds = auth_timeout_minutes * 60
1414
+
1415
+ # Auth timeout should be reasonable compared to default timeout
1416
+ if auth_timeout_seconds < default_timeout:
1417
+ result.passed = False
1418
+ result.message = f"Auth timeout ({auth_timeout_seconds}s) should not be less than default timeout ({default_timeout}s)"
1419
+ return result
1420
+
1421
+ result.passed = True
1422
+ result.message = "cURL timeout consistency validated"
1423
+ return result
1424
+
1425
+ except Exception as e:
1426
+ result.passed = False
1427
+ result.message = f"cURL timeout consistency check error: {e}"
1428
+ return result
1429
+
1430
+ def _check_font_fallback_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
1431
+ """Check font fallback consistency across platforms."""
1432
+ result = CheckResult('font_fallback_consistency')
1433
+
1434
+ try:
1435
+ font_settings = settings.get('font_settings', {})
1436
+
1437
+ if not isinstance(font_settings, dict):
1438
+ result.passed = True
1439
+ result.message = "Font settings not configured"
1440
+ return result
1441
+
1442
+ for font_type in ['text_font', 'interface_font']:
1443
+ if font_type in font_settings:
1444
+ font_config = font_settings[font_type]
1445
+ if isinstance(font_config, dict):
1446
+ # Check that if fallback_family exists, platform-specific fallbacks also exist
1447
+ has_fallback = 'fallback_family' in font_config
1448
+ has_mac_fallback = 'fallback_family_mac' in font_config
1449
+ has_linux_fallback = 'fallback_family_linux' in font_config
1450
+
1451
+ if has_fallback and not (has_mac_fallback and has_linux_fallback):
1452
+ result.passed = False
1453
+ result.message = f"Font '{font_type}' has fallback but missing platform-specific fallbacks"
1454
+ return result
1455
+
1456
+ result.passed = True
1457
+ result.message = "Font fallback consistency validated"
1458
+ return result
1459
+
1460
+ except Exception as e:
1461
+ result.passed = False
1462
+ result.message = f"Font fallback consistency check error: {e}"
1463
+ return result
1464
+
1465
+ def _check_dialog_hierarchy_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
1466
+ """Check dialog settings hierarchy consistency."""
1467
+ result = CheckResult('dialog_hierarchy_consistency')
1468
+
1469
+ try:
1470
+ dialog_settings = settings.get('dialog_settings', {})
1471
+
1472
+ if not isinstance(dialog_settings, dict):
1473
+ result.passed = True
1474
+ result.message = "Dialog settings not configured"
1475
+ return result
1476
+
1477
+ # Check that error dialogs cannot be disabled
1478
+ if 'error' in dialog_settings:
1479
+ error_config = dialog_settings['error']
1480
+ if isinstance(error_config, dict):
1481
+ if not error_config.get('enabled', True):
1482
+ result.passed = False
1483
+ result.message = "Error dialogs cannot be disabled"
1484
+ return result
1485
+
1486
+ if not error_config.get('locked', True):
1487
+ result.passed = False
1488
+ result.message = "Error dialogs must be locked"
1489
+ return result
1490
+
1491
+ # Check that confirmation dialogs have default_action if enabled
1492
+ if 'confirmation' in dialog_settings:
1493
+ confirm_config = dialog_settings['confirmation']
1494
+ if isinstance(confirm_config, dict) and confirm_config.get('enabled', False):
1495
+ if 'default_action' not in confirm_config:
1496
+ result.passed = False
1497
+ result.message = "Enabled confirmation dialogs must have default_action"
1498
+ return result
1499
+
1500
+ result.passed = True
1501
+ result.message = "Dialog hierarchy consistency validated"
1502
+ return result
1503
+
1504
+ except Exception as e:
1505
+ result.passed = False
1506
+ result.message = f"Dialog hierarchy consistency check error: {e}"
1507
+ return result
1508
+
1509
+ def _check_generator_tools_completeness(self, settings: Dict[str, Any]) -> 'CheckResult':
1510
+ """Check generator tools completeness and consistency."""
1511
+ result = CheckResult('generator_tools_completeness')
1512
+
1513
+ try:
1514
+ tool_settings = settings.get('tool_settings', {})
1515
+ generator_tools = tool_settings.get('Generator Tools', {})
1516
+
1517
+ if not isinstance(generator_tools, dict):
1518
+ result.passed = True
1519
+ result.message = "Generator Tools not configured"
1520
+ return result
1521
+
1522
+ # Check UUID generator consistency
1523
+ if 'UUID/GUID Generator' in generator_tools:
1524
+ uuid_config = generator_tools['UUID/GUID Generator']
1525
+ if isinstance(uuid_config, dict):
1526
+ version = uuid_config.get('version', 4)
1527
+ namespace = uuid_config.get('namespace', '')
1528
+ name = uuid_config.get('name', '')
1529
+
1530
+ # Version 3 and 5 require namespace and name
1531
+ if version in (3, 5):
1532
+ if not namespace or not name:
1533
+ result.passed = False
1534
+ result.message = f"UUID version {version} requires namespace and name"
1535
+ return result
1536
+
1537
+ result.passed = True
1538
+ result.message = "Generator tools completeness validated"
1539
+ return result
1540
+
1541
+ except Exception as e:
1542
+ result.passed = False
1543
+ result.message = f"Generator tools completeness check error: {e}"
1544
+ return result
1545
+
1546
+ def _check_folder_reporter_paths(self, settings: Dict[str, Any]) -> 'CheckResult':
1547
+ """Check folder reporter path consistency."""
1548
+ result = CheckResult('folder_reporter_paths')
1549
+
1550
+ try:
1551
+ tool_settings = settings.get('tool_settings', {})
1552
+ folder_reporter = tool_settings.get('Folder File Reporter', {})
1553
+
1554
+ if not isinstance(folder_reporter, dict):
1555
+ result.passed = True
1556
+ result.message = "Folder File Reporter not configured"
1557
+ return result
1558
+
1559
+ last_input = folder_reporter.get('last_input_folder', '')
1560
+ last_output = folder_reporter.get('last_output_folder', '')
1561
+
1562
+ # Check paths are valid format (if provided)
1563
+ for path_name, path_value in [('last_input_folder', last_input), ('last_output_folder', last_output)]:
1564
+ if path_value and isinstance(path_value, str):
1565
+ try:
1566
+ Path(path_value) # Basic path validation
1567
+ except Exception:
1568
+ result.passed = False
1569
+ result.message = f"Invalid path format in {path_name}: {path_value}"
1570
+ return result
1571
+
1572
+ result.passed = True
1573
+ result.message = "Folder reporter paths validated"
1574
+ return result
1575
+
1576
+ except Exception as e:
1577
+ result.passed = False
1578
+ result.message = f"Folder reporter paths check error: {e}"
1579
+ return result
1580
+
1581
+ def _get_expected_type(self, key: str, context: Optional[Dict[str, Any]]) -> Optional[type]:
1582
+ """Get expected type for a setting key."""
1583
+ type_map = {
1584
+ 'debug_level': str,
1585
+ 'active_input_tab': int,
1586
+ 'active_output_tab': int,
1587
+ 'export_path': str,
1588
+ 'selected_tool': str,
1589
+ 'input_tabs': list,
1590
+ 'output_tabs': list,
1591
+ 'tool_settings': dict,
1592
+ 'performance_settings': dict,
1593
+ 'font_settings': dict,
1594
+ 'dialog_settings': dict
1595
+ }
1596
+ return type_map.get(key)
1597
+
1598
+ def _get_pattern_rule(self, key: str, context: Optional[Dict[str, Any]]):
1599
+ """Get pattern-based validation rule for a key."""
1600
+ # Add pattern-based rules as needed
1601
+ return None
1602
+
1603
+ def _is_sensitive_key(self, key: str) -> bool:
1604
+ """Check if a key represents sensitive data."""
1605
+ sensitive_patterns = {
1606
+ 'key', 'token', 'password', 'secret', 'auth', 'credential'
1607
+ }
1608
+ key_lower = key.lower()
1609
+ return any(pattern in key_lower for pattern in sensitive_patterns)
1610
+
1611
+ def _is_encrypted_or_placeholder(self, value: str) -> bool:
1612
+ """Check if a value is encrypted or a placeholder."""
1613
+ if self.encryption_pattern.match(value):
1614
+ return True
1615
+
1616
+ placeholder_patterns = {
1617
+ 'putinyourkey', 'your_key_here', 'enter_key', 'api_key_here',
1618
+ 'your_token', 'enter_token', 'placeholder'
1619
+ }
1620
+ return value.lower() in placeholder_patterns
1621
+
1622
+ def _contains_obvious_secret(self, value: Any) -> bool:
1623
+ """Check if value contains obvious secrets."""
1624
+ if not isinstance(value, str) or len(value) < 10:
1625
+ return False
1626
+
1627
+ # Look for patterns that suggest real API keys/tokens
1628
+ # This is a basic heuristic - real implementation might be more sophisticated
1629
+ if re.match(r'^[A-Za-z0-9]{20,}$', value): # Long alphanumeric string
1630
+ return True
1631
+
1632
+ if re.match(r'^sk-[A-Za-z0-9]{40,}$', value): # OpenAI-style key
1633
+ return True
1634
+
1635
+ return False
1636
+
1637
+
1638
+ # Result classes for validation
1639
+ class CheckResult:
1640
+ """Result of a single validation check."""
1641
+
1642
+ def __init__(self, check_name: str):
1643
+ self.check_name = check_name
1644
+ self.passed = False
1645
+ self.message = ""
1646
+ self.severity = 'error' # 'error', 'warning', 'info'
1647
+
1648
+ def __bool__(self):
1649
+ return self.passed
1650
+
1651
+
1652
+ class ValidationResult:
1653
+ """Result of validating a single setting."""
1654
+
1655
+ def __init__(self, key: str, value: Any):
1656
+ self.key = key
1657
+ self.value = value
1658
+ self.checks = {}
1659
+ self.errors = []
1660
+ self.warnings = []
1661
+ self.is_critical = False
1662
+
1663
+ def add_check(self, check_name: str, result: CheckResult):
1664
+ """Add a check result."""
1665
+ self.checks[check_name] = result
1666
+
1667
+ if not result.passed:
1668
+ if result.severity == 'error':
1669
+ self.errors.append(f"{check_name}: {result.message}")
1670
+ elif result.severity == 'warning':
1671
+ self.warnings.append(f"{check_name}: {result.message}")
1672
+
1673
+ def add_error(self, message: str):
1674
+ """Add an error message."""
1675
+ self.errors.append(message)
1676
+
1677
+ def add_warning(self, message: str):
1678
+ """Add a warning message."""
1679
+ self.warnings.append(message)
1680
+
1681
+ @property
1682
+ def is_valid(self) -> bool:
1683
+ """Check if validation passed (no errors)."""
1684
+ return len(self.errors) == 0
1685
+
1686
+ @property
1687
+ def has_warnings(self) -> bool:
1688
+ """Check if there are warnings."""
1689
+ return len(self.warnings) > 0
1690
+
1691
+
1692
+ class ToolValidationResult:
1693
+ """Result of validating all settings for a tool."""
1694
+
1695
+ def __init__(self, tool_name: str, settings: Dict[str, Any]):
1696
+ self.tool_name = tool_name
1697
+ self.settings = settings
1698
+ self.checks = {}
1699
+ self.setting_results = {}
1700
+ self.errors = []
1701
+ self.warnings = []
1702
+
1703
+ def add_check(self, check_name: str, result: CheckResult):
1704
+ """Add a tool-level check result."""
1705
+ self.checks[check_name] = result
1706
+
1707
+ if not result.passed:
1708
+ if result.severity == 'error':
1709
+ self.errors.append(f"{check_name}: {result.message}")
1710
+ elif result.severity == 'warning':
1711
+ self.warnings.append(f"{check_name}: {result.message}")
1712
+
1713
+ def add_setting_result(self, setting_key: str, result: ValidationResult):
1714
+ """Add a setting validation result."""
1715
+ self.setting_results[setting_key] = result
1716
+
1717
+ # Aggregate errors and warnings
1718
+ self.errors.extend([f"{setting_key}: {err}" for err in result.errors])
1719
+ self.warnings.extend([f"{setting_key}: {warn}" for warn in result.warnings])
1720
+
1721
+ def add_error(self, message: str):
1722
+ """Add an error message."""
1723
+ self.errors.append(message)
1724
+
1725
+ @property
1726
+ def is_valid(self) -> bool:
1727
+ """Check if all validation passed."""
1728
+ return len(self.errors) == 0
1729
+
1730
+
1731
+ class ConsistencyValidationResult:
1732
+ """Result of validating consistency across all settings."""
1733
+
1734
+ def __init__(self, settings: Dict[str, Any]):
1735
+ self.settings = settings
1736
+ self.checks = {}
1737
+ self.errors = []
1738
+ self.warnings = []
1739
+
1740
+ def add_check(self, check_name: str, result: CheckResult):
1741
+ """Add a consistency check result."""
1742
+ self.checks[check_name] = result
1743
+
1744
+ if not result.passed:
1745
+ if result.severity == 'error':
1746
+ self.errors.append(f"{check_name}: {result.message}")
1747
+ elif result.severity == 'warning':
1748
+ self.warnings.append(f"{check_name}: {result.message}")
1749
+
1750
+ def add_error(self, message: str):
1751
+ """Add an error message."""
1752
+ self.errors.append(message)
1753
+
1754
+ @property
1755
+ def is_valid(self) -> bool:
1756
+ """Check if all consistency checks passed."""
1757
+ return len(self.errors) == 0
1758
+
1759
+
1760
+ class CompleteValidationResult:
1761
+ """Result of complete settings validation including all checks."""
1762
+
1763
+ def __init__(self, settings: Dict[str, Any]):
1764
+ self.settings = settings
1765
+ self.core_setting_results = {}
1766
+ self.tool_results = {}
1767
+ self.consistency_result = None
1768
+ self.errors = []
1769
+ self.warnings = []
1770
+
1771
+ def add_core_setting_result(self, setting_key: str, result: ValidationResult):
1772
+ """Add a core setting validation result."""
1773
+ self.core_setting_results[setting_key] = result
1774
+
1775
+ # Aggregate errors and warnings
1776
+ self.errors.extend([f"core.{setting_key}: {err}" for err in result.errors])
1777
+ self.warnings.extend([f"core.{setting_key}: {warn}" for warn in result.warnings])
1778
+
1779
+ def add_tool_result(self, tool_name: str, result: ToolValidationResult):
1780
+ """Add a tool validation result."""
1781
+ self.tool_results[tool_name] = result
1782
+
1783
+ # Aggregate errors and warnings
1784
+ self.errors.extend([f"tool.{tool_name}: {err}" for err in result.errors])
1785
+ self.warnings.extend([f"tool.{tool_name}: {warn}" for warn in result.warnings])
1786
+
1787
+ def add_consistency_result(self, result: ConsistencyValidationResult):
1788
+ """Add consistency validation result."""
1789
+ self.consistency_result = result
1790
+
1791
+ # Aggregate errors and warnings
1792
+ self.errors.extend([f"consistency: {err}" for err in result.errors])
1793
+ self.warnings.extend([f"consistency: {warn}" for warn in result.warnings])
1794
+
1795
+ def add_error(self, message: str):
1796
+ """Add an error message."""
1797
+ self.errors.append(message)
1798
+
1799
+ def add_warning(self, message: str):
1800
+ """Add a warning message."""
1801
+ self.warnings.append(message)
1802
+
1803
+ @property
1804
+ def is_valid(self) -> bool:
1805
+ """Check if all validation passed (no errors)."""
1806
+ return len(self.errors) == 0
1807
+
1808
+ @property
1809
+ def has_warnings(self) -> bool:
1810
+ """Check if there are warnings."""
1811
+ return len(self.warnings) > 0
1812
+
1813
+ def get_summary(self) -> Dict[str, Any]:
1814
+ """Get validation summary."""
1815
+ return {
1816
+ 'is_valid': self.is_valid,
1817
+ 'error_count': len(self.errors),
1818
+ 'warning_count': len(self.warnings),
1819
+ 'core_settings_validated': len(self.core_setting_results),
1820
+ 'tools_validated': len(self.tool_results),
1821
+ 'consistency_checks': len(self.consistency_result.checks) if self.consistency_result else 0,
1822
+ 'errors': self.errors,
1823
+ 'warnings': self.warnings
1824
+ }