runbooks 1.1.5__py3-none-any.whl → 1.1.7__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.
Files changed (65) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cli/commands/finops.py +29 -5
  3. runbooks/cli/commands/inventory.py +40 -87
  4. runbooks/common/accuracy_validator.py +6 -12
  5. runbooks/common/cli_decorators.py +61 -0
  6. runbooks/common/mcp_integration.py +38 -7
  7. runbooks/common/rich_utils.py +116 -2
  8. runbooks/inventory/CLAUDE.md +1 -1
  9. runbooks/inventory/aws_decorators.py +2 -3
  10. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  11. runbooks/inventory/check_controltower_readiness.py +152 -151
  12. runbooks/inventory/check_landingzone_readiness.py +85 -84
  13. runbooks/inventory/core/formatter.py +11 -0
  14. runbooks/inventory/draw_org_structure.py +8 -9
  15. runbooks/inventory/ec2_vpc_utils.py +2 -2
  16. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  17. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  18. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  19. runbooks/inventory/find_ec2_security_groups.py +48 -42
  20. runbooks/inventory/find_landingzone_versions.py +4 -6
  21. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  22. runbooks/inventory/inventory_modules.py +103 -91
  23. runbooks/inventory/list_cfn_stacks.py +9 -10
  24. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  25. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  26. runbooks/inventory/list_cfn_stacksets.py +8 -10
  27. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  28. runbooks/inventory/list_ds_directories.py +65 -53
  29. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  30. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  31. runbooks/inventory/list_ec2_instances.py +23 -28
  32. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  33. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  34. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  35. runbooks/inventory/list_guardduty_detectors.py +2 -4
  36. runbooks/inventory/list_iam_policies.py +2 -4
  37. runbooks/inventory/list_iam_roles.py +5 -7
  38. runbooks/inventory/list_iam_saml_providers.py +4 -6
  39. runbooks/inventory/list_lambda_functions.py +38 -38
  40. runbooks/inventory/list_org_accounts.py +6 -8
  41. runbooks/inventory/list_org_accounts_users.py +55 -44
  42. runbooks/inventory/list_rds_db_instances.py +31 -33
  43. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  44. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  45. runbooks/inventory/list_sns_topics.py +2 -4
  46. runbooks/inventory/list_ssm_parameters.py +4 -7
  47. runbooks/inventory/list_vpc_subnets.py +2 -4
  48. runbooks/inventory/list_vpcs.py +7 -10
  49. runbooks/inventory/mcp_inventory_validator.py +5 -3
  50. runbooks/inventory/organizations_discovery.py +8 -4
  51. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  52. runbooks/inventory/requirements.txt +0 -1
  53. runbooks/inventory/rich_inventory_display.py +2 -2
  54. runbooks/inventory/run_on_multi_accounts.py +3 -5
  55. runbooks/inventory/unified_validation_engine.py +3 -2
  56. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  57. runbooks/inventory/vpc_analyzer.py +3 -2
  58. runbooks/inventory/vpc_dependency_analyzer.py +2 -2
  59. runbooks/validation/terraform_drift_detector.py +16 -5
  60. {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/METADATA +3 -4
  61. {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/RECORD +65 -65
  62. {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/WHEEL +0 -0
  63. {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/entry_points.txt +0 -0
  64. {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/licenses/LICENSE +0 -0
  65. {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/top_level.txt +0 -0
@@ -64,14 +64,11 @@ from time import time
64
64
 
65
65
  from ArgumentsClass import CommonArguments
66
66
  from botocore.exceptions import ClientError
67
- from colorama import Fore, init
67
+ from runbooks.common.rich_utils import console
68
68
  from Inventory_Modules import display_results, find_ssm_parameters2, get_all_credentials
69
- from tqdm.auto import tqdm
70
69
 
71
- init()
72
70
  __version__ = "2024.05.07"
73
71
  begin_time = time()
74
- ERASE_LINE = "\x1b[2K"
75
72
 
76
73
 
77
74
  ##################
@@ -236,7 +233,7 @@ def find_ssm_parameters(f_credentialList):
236
233
  parameter_list.extend(find_ssm_parameters2(credential))
237
234
  # Optional verbose logging for parameter discovery progress (currently commented)
238
235
  # if verbose < 50 or len(parameter_list) == 0:
239
- # print(f"Found a running total of {len(parameter_list)} parameters in account {Fore.RED}{credential['AccountNumber']}{Fore.RESET} in region {Fore.RED}{credential['Region']}{Fore.RESET}")
236
+ # print(f"Found a running total of {len(parameter_list)} parameters in account [red]{credential['AccountNumber']} in region [red]{credential['Region']}")
240
237
  except ClientError as my_Error:
241
238
  # Handle AWS API authorization failures gracefully
242
239
  if "AuthFailure" in str(my_Error):
@@ -404,7 +401,7 @@ if __name__ == "__main__":
404
401
  # Display performance timing metrics for operational optimization
405
402
  if pTiming:
406
403
  print(ERASE_LINE)
407
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
404
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
408
405
 
409
406
  print()
410
407
 
@@ -424,7 +421,7 @@ if __name__ == "__main__":
424
421
  # Display completion message and output file information
425
422
  print("Thank you for using this script")
426
423
  print(
427
- f"Your output was saved to {Fore.GREEN}'{pFilename}-{datetime.now().strftime('%y-%m-%d--%H:%M:%S')}'{Fore.RESET}"
424
+ f"Your output was saved to [green]'{pFilename}-{datetime.now().strftime('%y-%m-%d--%H:%M:%S')}'"
428
425
  ) if pFilename is not None else None
429
426
 
430
427
  print()
@@ -76,10 +76,9 @@ from time import time
76
76
  import Inventory_Modules
77
77
  from ArgumentsClass import CommonArguments
78
78
  from botocore.exceptions import ClientError
79
- from colorama import Fore, init
79
+ from runbooks.common.rich_utils import console
80
80
  from Inventory_Modules import display_results, get_all_credentials
81
81
 
82
- init()
83
82
  __version__ = "2024.10.24"
84
83
 
85
84
  # TODO: Add Elastic IPs to this script as well.
@@ -384,7 +383,6 @@ def analyze_results(fSubnetsFound: list):
384
383
  ##################
385
384
 
386
385
 
387
- ERASE_LINE = "\x1b[2K"
388
386
  begin_time = time()
389
387
 
390
388
  if __name__ == "__main__":
@@ -426,7 +424,7 @@ if __name__ == "__main__":
426
424
  analyze_results(SubnetsFound)
427
425
  if pTiming:
428
426
  print(ERASE_LINE)
429
- print(f"{Fore.GREEN}This script completed in {time() - begin_time:.2f} seconds{Fore.RESET}")
427
+ print(f"[green]This script completed in {time() - begin_time:.2f} seconds")
430
428
 
431
429
  print()
432
430
  print("Thank you for using this script")
@@ -62,10 +62,9 @@ from time import time
62
62
  import Inventory_Modules
63
63
  from ArgumentsClass import CommonArguments
64
64
  from botocore.exceptions import ClientError
65
- from colorama import Fore, init
66
65
  from Inventory_Modules import display_results, get_all_credentials
66
+ from runbooks.common.rich_utils import console
67
67
 
68
- init()
69
68
  __version__ = "2024.01.26"
70
69
 
71
70
 
@@ -324,9 +323,9 @@ def find_all_vpcs(fAllCredentials, fDefaultOnly=False):
324
323
 
325
324
  for credential in fAllCredentials:
326
325
  logging.info(f"Beginning to queue data - starting with {credential['AccountId']}")
327
- print(
328
- f"{ERASE_LINE}Checking {credential['AccountId']} in region {credential['Region']} - {PlaceCount + 1} / {len(fAllCredentials)}",
329
- end="\r",
326
+ console.print(
327
+ f"Checking {credential['AccountId']} in region {credential['Region']} - {PlaceCount + 1} / {len(fAllCredentials)}",
328
+ end="",
330
329
  )
331
330
  # for region in fRegionList:
332
331
  try:
@@ -361,8 +360,6 @@ if __name__ == "__main__":
361
360
  verbose = args.loglevel
362
361
  logging.basicConfig(level=verbose, format="[%(filename)s:%(lineno)s - %(funcName)30s() ] %(message)s")
363
362
 
364
- ERASE_LINE = "\x1b[2K"
365
-
366
363
  begin_time = time()
367
364
 
368
365
  NumVpcsFound = 0
@@ -409,9 +406,9 @@ if __name__ == "__main__":
409
406
  logging.info(f"Threads all done - took {time() - begin_time:.2f} seconds")
410
407
 
411
408
  if pTiming:
412
- print(ERASE_LINE)
413
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
414
- print(ERASE_LINE)
409
+ console.print()
410
+ console.print(f"[green]This script took {time() - begin_time:.2f} seconds[/green]")
411
+ console.print()
415
412
  # Had to do this, because some of the VPCs that show up in the "sorted_AllVPCs" list are actually the same VPC, with a different CIDR range.
416
413
  Num_of_unique_VPCs = len(set([x["VpcId"] for x in sorted_AllVPCs]))
417
414
  print(
@@ -35,12 +35,13 @@ from pathlib import Path
35
35
  from typing import Any, Dict, List, Optional, Tuple, Union
36
36
 
37
37
  import boto3
38
- from rich.console import Console
39
- from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
38
+ from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
39
+ from runbooks.common.rich_utils import Progress
40
40
  from rich.table import Table
41
41
 
42
42
  from ..common.profile_utils import get_profile_for_operation, resolve_profile_for_operation_silent
43
43
  from ..common.rich_utils import (
44
+ Console,
44
45
  console as rich_console,
45
46
  create_table,
46
47
  format_cost,
@@ -1003,7 +1004,8 @@ class EnhancedMCPValidator:
1003
1004
  with ThreadPoolExecutor(max_workers=min(5, len(self.aws_sessions))) as executor:
1004
1005
  # Submit all validation tasks
1005
1006
  future_to_profile = {}
1006
- for profile, session in self.aws_sessions.items():
1007
+ for profile, session_info in self.aws_sessions.items():
1008
+ session = session_info["session"] # Extract boto3.Session from dict
1007
1009
  future = executor.submit(
1008
1010
  self._validate_profile_with_drift_detection, profile, session, runbooks_inventory
1009
1011
  )
@@ -24,17 +24,21 @@ from typing import Dict, List, Optional, Set, Tuple
24
24
 
25
25
  import boto3
26
26
  from botocore.exceptions import ClientError, NoCredentialsError
27
- from rich.console import Console
28
27
  from rich.panel import Panel
29
- from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
28
+ from rich.progress import BarColumn, SpinnerColumn, TextColumn, TimeElapsedColumn
30
29
  from rich.status import Status
31
30
  from rich.table import Table
32
31
 
33
- # Initialize Rich console
34
- console = Console()
32
+ # Test Mode Support: Use centralized rich_utils for console and Progress (test-aware)
33
+ # Issue: Rich Console/Progress write to StringIO buffer that Click CliRunner closes, causing ValueError
34
+ # Solution: rich_utils provides MockConsole/MockProgress in test mode, Rich components in production
35
+ import os
36
+ import sys
37
+ import re
35
38
 
36
39
  from ..utils.logger import configure_logger
37
40
  from ..common.performance_optimization_engine import get_optimization_engine
41
+ from ..common.rich_utils import console, Progress
38
42
 
39
43
  logger = configure_logger(__name__)
40
44
 
@@ -10,7 +10,7 @@ import Inventory_Modules
10
10
  import simplejson as json
11
11
  from account_class import aws_acct_access
12
12
  from ArgumentsClass import CommonArguments
13
- from colorama import Fore, init
13
+ from runbooks.common.rich_utils import console
14
14
  from Inventory_Modules import get_credentials_for_accounts_in_org
15
15
 
16
16
  """
@@ -18,7 +18,6 @@ This script was created to help solve a testing problem for the "move_stack_inst
18
18
  Originally, that script didn't have built-in recovery, so we needed this script to "recover" those stack-instance ids that might have been lost during the move_stack_instances.py run. However, that script now has built-in recovery, so this script isn't really needed. However, it can still be used to find any stack-instances that have been orphaned from their original stack-set, if that happens.
19
19
  """
20
20
 
21
- init()
22
21
  __version__ = "2024.05.18"
23
22
 
24
23
 
@@ -70,9 +69,9 @@ def setup_auth_and_regions(fProfile: str = None) -> (object, list, list):
70
69
  ChildAccounts = Inventory_Modules.RemoveCoreAccounts(ChildAccounts, pAccountsToSkip)
71
70
  AccountList = [account["AccountId"] for account in ChildAccounts]
72
71
 
73
- print(f"You asked to find stacks with this fragment list: {Fore.RED}{pFragments}{Fore.RESET}")
74
- print(f"\t\tin these accounts: {Fore.RED}{AccountList}{Fore.RESET}")
75
- print(f"\t\tin these regions: {Fore.RED}{RegionList}{Fore.RESET}")
72
+ print(f"You asked to find stacks with this fragment list: [red]{pFragments}")
73
+ print(f"\t\tin these accounts: [red]{AccountList}")
74
+ print(f"\t\tin these regions: [red]{RegionList}")
76
75
  return aws_acct, AccountList, RegionList
77
76
 
78
77
 
@@ -129,7 +128,7 @@ if __name__ == "__main__":
129
128
  Stacks = Inventory_Modules.find_stacks2(cred, region, pFragments)
130
129
  logging.warning(f"Account: {cred['AccountId']} | Region: {region} | Found {len(Stacks)} Stacks")
131
130
  print(
132
- f"{ERASE_LINE}{Fore.RED}Account: {cred['AccountId']} Region: {region} Found {len(Stacks)} Stacks{Fore.RESET} ({item_counter} of {len(AccountList) * len(RegionList)})",
131
+ f"{ERASE_LINE}[red]Account: {cred['AccountId']} Region: {region} Found {len(Stacks)} Stacks ({item_counter} of {len(AccountList) * len(RegionList)})",
133
132
  end="\r",
134
133
  )
135
134
  except Exception as my_Error:
@@ -162,7 +161,7 @@ if __name__ == "__main__":
162
161
  lAccountsAndRegions.append((StacksFound[i]["Account"], StacksFound[i]["Region"]))
163
162
  print(ERASE_LINE)
164
163
  print(
165
- f"{Fore.RED}Looked through {len(StacksFound)} Stacks across {len(AccountList)} accounts across {len(RegionList)} regions{Fore.RESET}"
164
+ f"[red]Looked through {len(StacksFound)} Stacks across {len(AccountList)} accounts across {len(RegionList)} regions"
166
165
  )
167
166
  print()
168
167
  if args.loglevel < 21: # INFO level
@@ -198,7 +197,7 @@ if __name__ == "__main__":
198
197
 
199
198
  if pTiming:
200
199
  print(ERASE_LINE)
201
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
200
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
202
201
 
203
202
  print()
204
203
  print("Thanks for using this script...")
@@ -1,7 +1,6 @@
1
1
  boto3>=1.26.84
2
2
  botocore>=1.29.84
3
3
  colorama>=0.4.6
4
- prettytable>=3.6.0
5
4
  simplejson>=3.18.3
6
5
  urllib3>=1.26.14
7
6
  randominfo>=2.0.2
@@ -21,9 +21,9 @@ from datetime import datetime
21
21
  from typing import Any, Dict, List, Optional
22
22
 
23
23
  from rich import box
24
- from rich.console import Console
25
24
  from rich.panel import Panel
26
- from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
25
+ from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn
26
+ from runbooks.common.rich_utils import Progress
27
27
  from rich.table import Table
28
28
  from rich.text import Text
29
29
  from rich.tree import Tree
@@ -7,14 +7,12 @@ import Inventory_Modules
7
7
  from account_class import aws_acct_access
8
8
  from ArgumentsClass import CommonArguments
9
9
  from botocore.exceptions import ClientError
10
- from colorama import Fore, init
11
- from rich.console import Console
10
+ from runbooks.common.rich_utils import console
12
11
  from rich.panel import Panel
13
12
 
14
- # Initialize Rich console
15
- console = Console()
13
+ # Initialize Rich console with test mode support
14
+ from runbooks.common.rich_utils import console
16
15
 
17
- init()
18
16
  __version__ = "2023.05.04"
19
17
 
20
18
  parser = CommonArguments()
@@ -38,12 +38,13 @@ from pathlib import Path
38
38
  from typing import Any, Dict, List, Optional, Tuple, Union
39
39
 
40
40
  import boto3
41
- from rich.console import Console
42
- from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
41
+ from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
42
+ from runbooks.common.rich_utils import Progress
43
43
  from rich.table import Table
44
44
 
45
45
  from ..common.profile_utils import get_profile_for_operation, resolve_profile_for_operation_silent
46
46
  from ..common.rich_utils import (
47
+ Console,
47
48
  console as rich_console,
48
49
  create_table,
49
50
  format_cost,
@@ -1042,7 +1042,7 @@ def display_results(
1042
1042
  ):
1043
1043
  from datetime import datetime
1044
1044
 
1045
- from colorama import Fore, init
1045
+ from runbooks.common.rich_utils import console
1046
1046
 
1047
1047
  init()
1048
1048
  """
@@ -34,9 +34,9 @@ import asyncio
34
34
 
35
35
  import boto3
36
36
  from botocore.exceptions import ClientError
37
- from rich.console import Console
38
37
  from rich.panel import Panel
39
- from rich.progress import Progress, SpinnerColumn, TextColumn
38
+ from rich.progress import SpinnerColumn, TextColumn
39
+ from runbooks.common.rich_utils import Progress
40
40
  from rich.table import Table
41
41
  from rich.tree import Tree
42
42
 
@@ -44,6 +44,7 @@ from runbooks.common.profile_utils import create_operational_session
44
44
  from runbooks.common.cross_account_manager import EnhancedCrossAccountManager, CrossAccountSession
45
45
  from runbooks.common.organizations_client import OrganizationAccount, get_unified_organizations_client
46
46
  from runbooks.common.rich_utils import (
47
+ Console,
47
48
  console,
48
49
  print_header,
49
50
  print_success,
@@ -43,10 +43,10 @@ from typing import Any, Dict, List, Optional, Set, Tuple
43
43
  import hashlib
44
44
  import boto3
45
45
  from botocore.exceptions import ClientError
46
- from rich.console import Console
47
46
  from rich.table import Table
48
47
  from rich.panel import Panel
49
- from rich.progress import Progress, SpinnerColumn, TextColumn
48
+ from rich.progress import SpinnerColumn, TextColumn
49
+ from runbooks.common.rich_utils import Progress
50
50
 
51
51
  from runbooks.common.rich_utils import (
52
52
  console,
@@ -242,6 +242,15 @@ class TerraformDriftDetector:
242
242
  drift_analysis, runbooks_resources, terraform_resources
243
243
  )
244
244
 
245
+ # CRITICAL: Enforce quality gate - block on <99.5% accuracy
246
+ accuracy_score = mcp_validation_result.get("accuracy_score", 0.0)
247
+ if accuracy_score < 99.5:
248
+ error_msg = f"MCP validation accuracy {accuracy_score:.1f}% below required threshold 99.5%"
249
+ console.print(f"[red]❌ MCP Validation FAILED: {accuracy_score:.1f}% < 99.5% required[/red]")
250
+ raise ValueError(error_msg)
251
+
252
+ console.print(f"[green]✅ MCP Validation PASSED: {accuracy_score:.1f}% ≥ 99.5% required[/green]")
253
+
245
254
  # Calculate metrics
246
255
  total_tf = len(terraform_resources)
247
256
  total_rb = len(runbooks_resources)
@@ -272,7 +281,7 @@ class TerraformDriftDetector:
272
281
  total_monthly_cost_impact=cost_metrics.get("total_monthly_cost", 0.0),
273
282
  high_cost_drifts=cost_metrics.get("high_cost_drifts", 0),
274
283
  cost_correlation_coverage=cost_metrics.get("correlation_coverage", 0.0),
275
- mcp_validation_accuracy=mcp_validation_result.get("accuracy_score", 95.0),
284
+ mcp_validation_accuracy=mcp_validation_result.get("accuracy_score", 0.0), # Pessimistic default
276
285
  # Analysis details
277
286
  drift_analysis=drift_analysis,
278
287
  missing_from_terraform=[
@@ -808,24 +817,26 @@ class TerraformDriftDetector:
808
817
  "validation_timestamp": datetime.now().isoformat(),
809
818
  }
810
819
 
811
- # Run MCP validation (simulated high accuracy)
820
+ # Run MCP validation with real accuracy calculation
812
821
  validation_result = await self.mcp_integrator.validate_vpc_operations(validation_data)
813
822
 
814
- # Calculate drift-specific accuracy metrics
815
- accuracy_score = 99.2 # High accuracy for direct comparison
823
+ # Use real accuracy from MCP validation (no hardcoded values)
824
+ accuracy_score = validation_result.accuracy_score # Real calculated accuracy
816
825
 
817
826
  return {
818
827
  "success": validation_result.success,
819
828
  "accuracy_score": accuracy_score,
820
829
  "validation_timestamp": datetime.now().isoformat(),
821
830
  "resources_validated": len(drift_analysis),
831
+ "total_validations": validation_result.performance_metrics.get("total_validations", 0),
832
+ "successful_validations": validation_result.performance_metrics.get("successful_validations", 0),
822
833
  }
823
834
 
824
835
  except Exception as e:
825
836
  print_warning(f"MCP validation error: {str(e)[:50]}...")
826
837
  return {
827
838
  "success": False,
828
- "accuracy_score": 95.0, # Default accuracy
839
+ "accuracy_score": 0.0, # Honest failure - no optimistic defaults
829
840
  "validation_timestamp": datetime.now().isoformat(),
830
841
  "error": str(e),
831
842
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runbooks
3
- Version: 1.1.5
3
+ Version: 1.1.7
4
4
  Summary: CloudOps Automation Toolkit with Enhanced Cloud Foundations Assessment for DevOps and SRE teams.
5
5
  Author-email: Maintainers <nnthanh101@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -31,19 +31,18 @@ Requires-Dist: pydantic>=2.10.0
31
31
  Requires-Dist: jinja2>=3.1.4
32
32
  Requires-Dist: werkzeug>=3.1.0
33
33
  Requires-Dist: markdown>=3.7.0
34
- Requires-Dist: prettytable>=3.16.0
35
34
  Requires-Dist: simplejson>=3.20.1
36
35
  Requires-Dist: python-dateutil>=2.9.0
37
36
  Requires-Dist: loguru>=0.7.3
38
- Requires-Dist: tqdm>=4.67.1
39
37
  Requires-Dist: graphviz>=0.20.1
40
- Requires-Dist: rich>=14.0.0
38
+ Requires-Dist: rich>=14.1.0
41
39
  Requires-Dist: reportlab>=3.6.1
42
40
  Requires-Dist: requests>=2.32.0
43
41
  Requires-Dist: packaging>=21.0
44
42
  Requires-Dist: pyyaml>=6.0.2
45
43
  Requires-Dist: jmespath>=1.0.1
46
44
  Requires-Dist: urllib3<1.27,>=1.26.18
45
+ Requires-Dist: openapi-spec-validator>=0.7.2
47
46
  Requires-Dist: mcp>=1.12.3
48
47
  Requires-Dist: pandas>=2.3.1
49
48
  Requires-Dist: ipython>=9.4.0