dayhoff-tools 1.8.1__tar.gz → 1.8.3__tar.gz

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 (32) hide show
  1. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/PKG-INFO +1 -1
  2. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/cli/engine_commands.py +69 -71
  3. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/pyproject.toml +1 -1
  4. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/README.md +0 -0
  5. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/__init__.py +0 -0
  6. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/chemistry/standardizer.py +0 -0
  7. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/chemistry/utils.py +0 -0
  8. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/cli/__init__.py +0 -0
  9. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/cli/cloud_commands.py +0 -0
  10. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/cli/main.py +0 -0
  11. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/cli/swarm_commands.py +0 -0
  12. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/cli/utility_commands.py +0 -0
  13. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/deployment/base.py +0 -0
  14. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/deployment/deploy_aws.py +0 -0
  15. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/deployment/deploy_gcp.py +0 -0
  16. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/deployment/deploy_utils.py +0 -0
  17. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/deployment/job_runner.py +0 -0
  18. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/deployment/processors.py +0 -0
  19. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/deployment/swarm.py +0 -0
  20. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/embedders.py +0 -0
  21. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/fasta.py +0 -0
  22. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/file_ops.py +0 -0
  23. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/h5.py +0 -0
  24. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/intake/gcp.py +0 -0
  25. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/intake/gtdb.py +0 -0
  26. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/intake/kegg.py +0 -0
  27. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/intake/mmseqs.py +0 -0
  28. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/intake/structure.py +0 -0
  29. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/intake/uniprot.py +0 -0
  30. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/logs.py +0 -0
  31. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/sqlite.py +0 -0
  32. {dayhoff_tools-1.8.1 → dayhoff_tools-1.8.3}/dayhoff_tools/warehouse.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dayhoff-tools
3
- Version: 1.8.1
3
+ Version: 1.8.3
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
@@ -841,21 +841,56 @@ def engine_status(
841
841
  else f"[bold]Started:[/bold] {launch_time.strftime('%Y-%m-%d %H:%M:%S')} ({format_duration(uptime)} ago)"
842
842
  )
843
843
 
844
+ # ---------------- Front-loaded summary ----------------
845
+ running_state = engine["state"].lower()
846
+ if running_state == "running":
847
+ run_disp = "[green]Running[/green]"
848
+ elif running_state == "pending":
849
+ run_disp = "[yellow]Starting...[/yellow]"
850
+ elif running_state == "stopped":
851
+ run_disp = "[dim]Stopped[/dim]"
852
+ else:
853
+ run_disp = engine["state"]
854
+
855
+ active_disp = (
856
+ "[green]Active[/green]"
857
+ if idle_detector.get("status") == "active"
858
+ else "[dim]Idle[/dim]"
859
+ )
860
+
861
+ top_lines = [
862
+ f"[blue]{engine['name']}[/blue] {run_disp} {active_disp}",
863
+ ]
864
+
865
+ # Studios summary next, with studio name in purple/magenta
866
+ studios_line = None
867
+ if attached_studios:
868
+ stu_texts = [
869
+ f"[magenta]{s.get('user', 'studio')}[/magenta] ({s.get('studio_id', 'unknown')})"
870
+ for s in attached_studios
871
+ ]
872
+ studios_line = "Studios: " + ", ".join(stu_texts)
873
+ top_lines.append(studios_line)
874
+
875
+ # Paragraph break
876
+ top_lines.append("")
877
+
878
+ # ---------------- Details block (white/default) ----------------
844
879
  status_lines = [
845
- f"[bold]Name:[/bold] {engine['name']}",
846
- f"[bold]Instance:[/bold] {engine['instance_id']}",
847
- f"[bold]Type:[/bold] {engine['engine_type']} ({engine['instance_type']})",
848
- f"[bold]Status:[/bold] {format_status(engine['state'], engine.get('ready'))}",
849
- f"[bold]User:[/bold] {engine['user']}",
850
- f"[bold]IP:[/bold] {engine.get('public_ip', 'N/A')}",
880
+ f"Name: {engine['name']}",
881
+ f"Instance: {engine['instance_id']}",
882
+ f"Type: {engine['engine_type']} ({engine['instance_type']})",
883
+ f"Status: {engine['state']}",
884
+ f"User: {engine['user']}",
885
+ f"IP: {engine.get('public_ip', 'N/A')}",
851
886
  started_line,
852
- f"[bold]$/hour:[/bold] ${hourly_cost:.2f}",
887
+ f"$/hour: ${hourly_cost:.2f}",
853
888
  ]
854
889
 
855
890
  # Disk usage (like list --detailed)
856
891
  if engine["state"].lower() == "running":
857
892
  disk_usage = get_disk_usage_via_ssm(engine["instance_id"]) or "-"
858
- status_lines.append(f"[bold]Disk:[/bold] {disk_usage}")
893
+ status_lines.append(f"Disk: {disk_usage}")
859
894
 
860
895
  # Health report (only if bootstrap finished)
861
896
  if stage_val == "finished":
@@ -954,16 +989,8 @@ def engine_status(
954
989
  except Exception:
955
990
  return None
956
991
 
957
- # Enrich missing fields or provide full fallback when API lacks details
958
- overlay = (
959
- _fetch_idle_summary_via_ssm(engine["instance_id"])
960
- if not idle_detector.get("available")
961
- or not any(
962
- k in idle_detector
963
- for k in ("ide_connections", "ssh_sessions", "coffee_lock")
964
- )
965
- else None
966
- )
992
+ # Always try to enrich from on-engine summary (fast, best-effort)
993
+ overlay = _fetch_idle_summary_via_ssm(engine["instance_id"])
967
994
  if overlay:
968
995
  # If API didn't indicate availability, replace entirely; otherwise fill gaps
969
996
  if not idle_detector.get("available"):
@@ -972,68 +999,37 @@ def engine_status(
972
999
  for k, v in overlay.items():
973
1000
  idle_detector.setdefault(k, v)
974
1001
 
975
- # Idle detector status (API and/or on-engine fallback)
1002
+ # Activity Sensors (show all with YES/no)
976
1003
  if idle_detector.get("available"):
977
1004
  status_lines.append("")
978
- status_lines.append("[bold]Idle Detector:[/bold]")
979
-
980
- # Overall status
981
- if idle_detector["status"] == "active":
982
- status_lines.append(" [green]✓ Engine ACTIVE[/green]")
983
- else:
984
- idle_seconds = idle_detector.get("idle_seconds", 0)
985
- idle_threshold = idle_detector.get("idle_threshold", 1800)
986
- idle_minutes = idle_seconds // 60
987
- threshold_minutes = idle_threshold // 60
988
- status_lines.append(
989
- f" [yellow]⏱ Engine IDLE ({idle_minutes}/{threshold_minutes} minutes)[/yellow]"
990
- )
991
-
992
- # Coffee lock
993
- if idle_detector.get("coffee_lock"):
994
- status_lines.append(
995
- f" • [cyan]☕ Caffeinated for another {idle_detector['coffee_lock']}[/cyan]"
1005
+ status_lines.append("[bold]Activity Sensors:[/bold]")
1006
+ reasons_raw = idle_detector.get("_reasons_raw", []) or []
1007
+ by_sensor: Dict[str, Dict[str, Any]] = {}
1008
+ for r in reasons_raw:
1009
+ nm = r.get("sensor")
1010
+ if nm:
1011
+ by_sensor[nm] = r
1012
+
1013
+ def _sensor_line(label: str, key: str, emoji: str) -> str:
1014
+ r = by_sensor.get(key, {})
1015
+ active = bool(r.get("active"))
1016
+ reason_txt = r.get("reason") or ("" if not active else "active")
1017
+ flag = "[green]YES[/green]" if active else "no"
1018
+ return (
1019
+ f" {emoji} {label}: {flag} {('- ' + reason_txt) if reason_txt else ''}"
996
1020
  )
997
1021
 
998
- # SSH sessions
999
- ssh_sessions = idle_detector.get("ssh_sessions", [])
1000
- if ssh_sessions:
1001
- status_lines.append(f" [blue]SSH Sessions ({len(ssh_sessions)}):[/blue]")
1002
- for session in ssh_sessions:
1003
- idle_time = session.get("idle_time")
1004
- if isinstance(idle_time, (int, float)):
1005
- idle_disp = f"{int(idle_time)//60}m"
1006
- else:
1007
- idle_disp = str(idle_time) if idle_time else "0m"
1008
- status_lines.append(
1009
- f" - {session.get('tty', 'pts/?')} (pid {session.get('pid', '?')}, idle {idle_disp}) from {session.get('from_ip', 'unknown')}"
1010
- )
1011
-
1012
- # IDE connections
1013
- ide_conn = idle_detector.get("ide_connections")
1014
- if ide_conn:
1015
- status_lines.append(
1016
- f" • [magenta]🖥 IDE connected ({ide_conn['connection_count']} connections)[/magenta]"
1017
- )
1018
-
1019
- # Sensors contributing to ACTIVE
1020
- reasons_raw = idle_detector.get("_reasons_raw")
1021
- if isinstance(reasons_raw, list):
1022
- active_sensors = [r for r in reasons_raw if r.get("active")]
1023
- if active_sensors:
1024
- status_lines.append("")
1025
- status_lines.append("[bold]Active Sensors:[/bold]")
1026
- for r in active_sensors:
1027
- sensor = r.get("sensor", "Sensor")
1028
- reason = r.get("reason") or "active"
1029
- status_lines.append(f" • {sensor}: {reason}")
1022
+ status_lines.append(_sensor_line("Coffee", "CoffeeLockSensor", "☕"))
1023
+ status_lines.append(_sensor_line("Shell", "ActiveLoginSensor", "🐚"))
1024
+ status_lines.append(_sensor_line("IDE", "IDEConnectionSensor", "🖥"))
1025
+ status_lines.append(_sensor_line("Docker", "DockerWorkloadSensor", "🐳"))
1030
1026
 
1031
1027
  # Audit one-liner (best-effort SSM fetch)
1032
1028
  try:
1033
1029
  last_audit = _fetch_last_audit_via_ssm(engine["instance_id"])
1034
1030
  if last_audit:
1035
1031
  status_lines.append("")
1036
- status_lines.append("[bold]Idle Audit:[/bold]")
1032
+ status_lines.append("[bold]Shutdown Audit:[/bold]")
1037
1033
  status_lines.append(f" • {_summarize_audit(last_audit)}")
1038
1034
  except Exception:
1039
1035
  pass
@@ -1044,8 +1040,10 @@ def engine_status(
1044
1040
  for studio in attached_studios:
1045
1041
  status_lines.append(f" • {studio['user']} ({studio['studio_id']})")
1046
1042
 
1043
+ # Combine top summary and details
1044
+ all_lines = top_lines + status_lines
1047
1045
  console.print(
1048
- Panel("\n".join(status_lines), title="Engine Status", border_style="blue")
1046
+ Panel("\n".join(all_lines), title="Engine Status", border_style="blue")
1049
1047
  )
1050
1048
 
1051
1049
  if show_log:
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
5
5
 
6
6
  [project]
7
7
  name = "dayhoff-tools"
8
- version = "1.8.1"
8
+ version = "1.8.3"
9
9
  description = "Common tools for all the repos at Dayhoff Labs"
10
10
  authors = [
11
11
  {name = "Daniel Martin-Alarcon", email = "dma@dayhofflabs.com"}
File without changes