dayhoff-tools 1.9.4__py3-none-any.whl → 1.9.5__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.
@@ -778,15 +778,24 @@ def engine_status(
778
778
  return "[green]Active[/green]"
779
779
  if running_state in ("stopped", "stopping"):
780
780
  return "[dim]N/A[/dim]"
781
- idle_seconds_v = idle_info.get("idle_seconds")
782
- thresh_v = idle_info.get("idle_threshold")
783
- if isinstance(idle_seconds_v, (int, float)) and isinstance(
784
- thresh_v, (int, float)
785
- ):
786
- return (
787
- f"[yellow]Idle {int(idle_seconds_v)//60}m/{int(thresh_v)//60}m[/yellow]"
788
- )
789
- return "[yellow]Idle[/yellow]"
781
+
782
+ # If we don't have idle info at all, show N/A
783
+ if not idle_info.get("available"):
784
+ return "[dim]N/A[/dim]"
785
+
786
+ # If idle, show time/threshold if available, otherwise show uncertainty
787
+ if idle_info.get("status") == "idle":
788
+ idle_seconds_v = idle_info.get("idle_seconds")
789
+ thresh_v = idle_info.get("idle_threshold")
790
+ if isinstance(idle_seconds_v, (int, float)) and isinstance(thresh_v, (int, float)):
791
+ return f"[yellow]Idle {int(idle_seconds_v)//60}m/{int(thresh_v)//60}m[/yellow]"
792
+ elif isinstance(thresh_v, (int, float)):
793
+ return f"[yellow]Idle ?/{int(thresh_v)//60}m[/yellow]"
794
+ else:
795
+ return "[yellow]Idle ?/?[/yellow]"
796
+
797
+ # Default to N/A if we can't determine status
798
+ return "[dim]N/A[/dim]"
790
799
 
791
800
  active_disp = _compute_active_disp(idle_detector)
792
801
 
@@ -916,8 +925,12 @@ def engine_status(
916
925
  },
917
926
  )
918
927
  cid = res["Command"]["CommandId"]
919
- time.sleep(1)
920
- inv = ssm.get_command_invocation(CommandId=cid, InstanceId=instance_id)
928
+ # Wait up to 2 seconds for SSM command to complete (was 1 second)
929
+ for _ in range(4): # 4 * 0.5 = 2 seconds
930
+ time.sleep(0.5)
931
+ inv = ssm.get_command_invocation(CommandId=cid, InstanceId=instance_id)
932
+ if inv["Status"] in ["Success", "Failed"]:
933
+ break
921
934
  if inv["Status"] != "Success":
922
935
  return None
923
936
  content = inv["StandardOutputContent"].strip()
@@ -1037,23 +1050,10 @@ def engine_status(
1037
1050
  status_lines.append(_sensor_line(" IDE ", "IDEConnectionSensor", "🖥"))
1038
1051
  status_lines.append(_sensor_line("Docker", "DockerWorkloadSensor", "🐳"))
1039
1052
 
1040
- # If we have elapsed idle seconds and threshold, reflect that in the header
1041
- try:
1042
- if idle_detector.get("status") == "idle":
1043
- idle_secs = int(idle_detector.get("idle_seconds", 0))
1044
- thresh_secs = int(idle_detector.get("idle_threshold", 0))
1045
- if thresh_secs > 0:
1046
- active_disp = (
1047
- f"[yellow]Idle {idle_secs//60}m/{thresh_secs//60}m[/yellow]"
1048
- )
1049
- # Rewrite top header line (index 0) to include updated display
1050
- all_header = top_lines[0]
1051
- # Replace the portion after two spaces (name and running state fixed)
1052
- top_lines[0] = (
1053
- f"[blue]{engine['name']}[/blue] {run_disp} {active_disp}\n"
1054
- )
1055
- except Exception:
1056
- pass
1053
+ # Recompute display with latest idle detector data
1054
+ active_disp = _compute_active_disp(idle_detector)
1055
+ # Rewrite top header line (index 0) to include updated display
1056
+ top_lines[0] = f"[blue]{engine['name']}[/blue] {run_disp} {active_disp}\n"
1057
1057
 
1058
1058
  # Combine top summary and details
1059
1059
  all_lines = top_lines + status_lines
@@ -2631,6 +2631,9 @@ def idle_timeout_cmd(
2631
2631
  set: Optional[str] = typer.Option(
2632
2632
  None, "--set", "-s", help="New timeout (e.g., 2h30m, 45m)"
2633
2633
  ),
2634
+ status: bool = typer.Option(
2635
+ False, "--status", help="Show detailed idle status (faster than full engine status)"
2636
+ ),
2634
2637
  ):
2635
2638
  """Show or set the engine idle-detector timeout."""
2636
2639
  check_aws_sso()
@@ -2645,9 +2648,90 @@ def idle_timeout_cmd(
2645
2648
  engine = resolve_engine(name_or_id, engines)
2646
2649
 
2647
2650
  ssm = boto3.client("ssm", region_name="us-east-1")
2651
+
2652
+ # Handle --status flag for quick idle status check
2653
+ if status:
2654
+ console.print(f"[bold]Idle Status for {engine['name']}:[/bold]")
2655
+
2656
+ # Fetch state file with longer timeout for reliability
2657
+ try:
2658
+ resp = ssm.send_command(
2659
+ InstanceIds=[engine["instance_id"]],
2660
+ DocumentName="AWS-RunShellScript",
2661
+ Parameters={
2662
+ "commands": [
2663
+ "cat /var/run/idle-detector/last_state.json 2>/dev/null || echo '{}'"
2664
+ ],
2665
+ "executionTimeout": ["10"],
2666
+ },
2667
+ )
2668
+ cid = resp["Command"]["CommandId"]
2669
+
2670
+ # Wait up to 3 seconds for result
2671
+ for _ in range(6): # 6 * 0.5 = 3 seconds
2672
+ time.sleep(0.5)
2673
+ inv = ssm.get_command_invocation(
2674
+ CommandId=cid, InstanceId=engine["instance_id"]
2675
+ )
2676
+ if inv["Status"] in ["Success", "Failed"]:
2677
+ break
2678
+
2679
+ if inv["Status"] == "Success":
2680
+ import json
2681
+ content = inv["StandardOutputContent"].strip()
2682
+ if content and content != "{}":
2683
+ data = json.loads(content)
2684
+
2685
+ # Extract key values
2686
+ is_idle = data.get("idle", False)
2687
+ timeout_sec = data.get("timeout_sec", "?")
2688
+ idle_seconds = data.get("idle_seconds", 0) if is_idle else 0
2689
+
2690
+ # Display status
2691
+ if is_idle:
2692
+ if isinstance(timeout_sec, int) and isinstance(idle_seconds, int):
2693
+ console.print(f" Status: [yellow]Idle {idle_seconds//60}m/{timeout_sec//60}m[/yellow]")
2694
+ remaining = max(0, timeout_sec - idle_seconds)
2695
+ console.print(f" Time until shutdown: [red]{remaining//60}m {remaining%60}s[/red]")
2696
+ else:
2697
+ console.print(f" Status: [yellow]Idle (timing unavailable)[/yellow]")
2698
+ else:
2699
+ console.print(f" Status: [green]Active[/green]")
2700
+ if isinstance(timeout_sec, int):
2701
+ console.print(f" Timeout: {timeout_sec//60}m ({timeout_sec}s)")
2702
+
2703
+ # Show activity sensors
2704
+ console.print("\n[bold]Activity Sensors:[/bold]")
2705
+ reasons = data.get("reasons", [])
2706
+ for r in reasons:
2707
+ sensor = r.get("sensor", "Unknown")
2708
+ active = r.get("active", False)
2709
+ reason = r.get("reason", "")
2710
+
2711
+ # Map sensor names to icons and labels
2712
+ sensor_map = {
2713
+ "CoffeeLockSensor": ("☕", "Coffee"),
2714
+ "ActiveLoginSensor": ("🐚", "SSH"),
2715
+ "IDEConnectionSensor": ("🖥", "IDE"),
2716
+ "DockerWorkloadSensor": ("🐳", "Docker"),
2717
+ }
2718
+
2719
+ icon, label = sensor_map.get(sensor, ("?", sensor))
2720
+ status_str = "[green]YES[/green]" if active else "[dim]no[/dim]"
2721
+ console.print(f" {icon} {label:8} {status_str} {reason if active else ''}")
2722
+
2723
+ else:
2724
+ console.print("[yellow]⚠️ Could not read idle detector state[/yellow]")
2725
+ else:
2726
+ console.print(f"[red]❌ Failed to get status: {inv.get('StandardErrorContent', 'Unknown error')}[/red]")
2727
+
2728
+ except Exception as e:
2729
+ console.print(f"[red]❌ Error fetching idle status: {e}[/red]")
2730
+
2731
+ return
2648
2732
 
2649
2733
  if set is None:
2650
- # Show current
2734
+ # Show current timeout setting
2651
2735
  resp = ssm.send_command(
2652
2736
  InstanceIds=[engine["instance_id"]],
2653
2737
  DocumentName="AWS-RunShellScript",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dayhoff-tools
3
- Version: 1.9.4
3
+ Version: 1.9.5
4
4
  Summary: Common tools for all the repos at Dayhoff Labs
5
5
  Author: Daniel Martin-Alarcon
6
6
  Author-email: dma@dayhofflabs.com
@@ -3,7 +3,7 @@ dayhoff_tools/chemistry/standardizer.py,sha256=uMn7VwHnx02nc404eO6fRuS4rsl4dvSPf
3
3
  dayhoff_tools/chemistry/utils.py,sha256=jt-7JgF-GeeVC421acX-bobKbLU_X94KNOW24p_P-_M,2257
4
4
  dayhoff_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  dayhoff_tools/cli/cloud_commands.py,sha256=33qcWLmq-FwEXMdL3F0OHm-5Stlh2r65CldyEZgQ1no,40904
6
- dayhoff_tools/cli/engine_commands.py,sha256=sJroAFeJqHBMwONTLTFUNfuJf0PvN6xu-OBoVS_b4uU,107111
6
+ dayhoff_tools/cli/engine_commands.py,sha256=XFCaHWHhPIsOU43iyERel_hk3CgHPYFN3p8kVaQhuT0,111329
7
7
  dayhoff_tools/cli/main.py,sha256=LoFs3SI4fdCjP4pdxEAhri-_q0dmNYupmBCRE4KbBac,5933
8
8
  dayhoff_tools/cli/swarm_commands.py,sha256=5EyKj8yietvT5lfoz8Zx0iQvVaNgc3SJX1z2zQR6o6M,5614
9
9
  dayhoff_tools/cli/utility_commands.py,sha256=WQTHOh1MttuxaJjl2c6zMa4x7_JuaKMQgcyotYrU3GA,25883
@@ -27,7 +27,7 @@ dayhoff_tools/intake/uniprot.py,sha256=BZYJQF63OtPcBBnQ7_P9gulxzJtqyorgyuDiPeOJq
27
27
  dayhoff_tools/logs.py,sha256=DKdeP0k0kliRcilwvX0mUB2eipO5BdWUeHwh-VnsICs,838
28
28
  dayhoff_tools/sqlite.py,sha256=jV55ikF8VpTfeQqqlHSbY8OgfyfHj8zgHNpZjBLos_E,18672
29
29
  dayhoff_tools/warehouse.py,sha256=UETBtZD3r7WgvURqfGbyHlT7cxoiVq8isjzMuerKw8I,24475
30
- dayhoff_tools-1.9.4.dist-info/METADATA,sha256=fZi-XFFqeuhdjIIEJKkVN2bpMjyIO6q2G6RcaCURZLk,2914
31
- dayhoff_tools-1.9.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
- dayhoff_tools-1.9.4.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
33
- dayhoff_tools-1.9.4.dist-info/RECORD,,
30
+ dayhoff_tools-1.9.5.dist-info/METADATA,sha256=fw3aufIuu3G9jI14oECpLoIUP52JhhT0kpZQufTN5uo,2914
31
+ dayhoff_tools-1.9.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
+ dayhoff_tools-1.9.5.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
33
+ dayhoff_tools-1.9.5.dist-info/RECORD,,