dh-cli 0.5.1__tar.gz → 0.5.2__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 (54) hide show
  1. {dh_cli-0.5.1 → dh_cli-0.5.2}/PKG-INFO +1 -1
  2. {dh_cli-0.5.1 → dh_cli-0.5.2}/pyproject.toml +1 -1
  3. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/engines_studios/engine_commands.py +89 -102
  4. {dh_cli-0.5.1 → dh_cli-0.5.2}/.gitignore +0 -0
  5. {dh_cli-0.5.1 → dh_cli-0.5.2}/LICENSE +0 -0
  6. {dh_cli-0.5.1 → dh_cli-0.5.2}/README.md +0 -0
  7. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/__init__.py +0 -0
  8. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/__init__.py +0 -0
  9. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/aws_batch.py +0 -0
  10. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/__init__.py +0 -0
  11. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/boltz.py +0 -0
  12. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/cancel.py +0 -0
  13. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/clean.py +0 -0
  14. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/embed_t5.py +0 -0
  15. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/finalize.py +0 -0
  16. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/list_jobs.py +0 -0
  17. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/local.py +0 -0
  18. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/logs.py +0 -0
  19. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/orca.py +0 -0
  20. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/protmpnn.py +0 -0
  21. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/protmpnn_to_boltz.py +0 -0
  22. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/retry.py +0 -0
  23. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/status.py +0 -0
  24. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/submit.py +0 -0
  25. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/train.py +0 -0
  26. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/commands/wait_for.py +0 -0
  27. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/fasta_utils.py +0 -0
  28. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/h5_utils.py +0 -0
  29. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/job_id.py +0 -0
  30. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/manifest.py +0 -0
  31. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/batch/s3_transport.py +0 -0
  32. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/cloud_commands.py +0 -0
  33. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/codeartifact.py +0 -0
  34. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/engines_studios/__init__.py +0 -0
  35. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/engines_studios/api_client.py +0 -0
  36. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/engines_studios/auth.py +0 -0
  37. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/engines_studios/progress.py +0 -0
  38. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/engines_studios/ssh_config.py +0 -0
  39. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/engines_studios/studio_commands.py +0 -0
  40. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/github_commands.py +0 -0
  41. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/hz/__init__.py +0 -0
  42. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/hz/deploy.py +0 -0
  43. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/hz/local.py +0 -0
  44. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/hz/test.py +0 -0
  45. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/hz/tf.py +0 -0
  46. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/hz/users.py +0 -0
  47. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/main.py +0 -0
  48. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/utility_commands.py +0 -0
  49. {dh_cli-0.5.1 → dh_cli-0.5.2}/src/dh_cli/warehouse.py +0 -0
  50. {dh_cli-0.5.1 → dh_cli-0.5.2}/tests/hz/test_init.py +0 -0
  51. {dh_cli-0.5.1 → dh_cli-0.5.2}/tests/hz/test_suites.py +0 -0
  52. {dh_cli-0.5.1 → dh_cli-0.5.2}/tests/hz/test_users.py +0 -0
  53. {dh_cli-0.5.1 → dh_cli-0.5.2}/tests/test_cloud_gcp.py +0 -0
  54. {dh_cli-0.5.1 → dh_cli-0.5.2}/tests/test_finalize_protmpnn.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dh-cli
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: Dayhoff Labs developer CLI
5
5
  Author-email: Dayhoff Labs <dev@dayhofflabs.com>
6
6
  License: # PolyForm Noncommercial License 1.0.0
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dh-cli"
7
- version = "0.5.1"
7
+ version = "0.5.2"
8
8
  description = "Dayhoff Labs developer CLI"
9
9
  requires-python = ">=3.11"
10
10
  readme = "README.md"
@@ -547,126 +547,113 @@ def list_engines(env: Optional[str]):
547
547
  click.echo("No engines found\n")
548
548
  return
549
549
 
550
- # Calculate dynamic width for Name column (longest name + 2 for padding)
551
- max_name_len = max((len(engine.get("name", "unknown")) for engine in engines), default=4)
552
- name_width = max(max_name_len + 2, len("Name") + 2)
553
-
554
- # Fixed widths for other columns
555
- state_width = 12
556
- user_width = 12
557
- type_width = 12
558
- disk_width = 6
559
- uptime_width = 8
560
-
561
- # Table top border
562
- click.echo(
563
- "╭"
564
- + "─" * (name_width + 1)
565
- + "┬"
566
- + "─" * (uptime_width + 1)
567
- + "┬"
568
- + "─" * (state_width + 1)
569
- + "┬"
570
- + "─" * (user_width + 1)
571
- + "┬"
572
- + "─" * (type_width + 1)
573
- + "┬"
574
- + "─" * (disk_width + 1)
575
- + "╮"
576
- )
550
+ # Sort: running engines first (alphabetical), then the rest (alphabetical)
551
+ def sort_key(e):
552
+ is_running = 0 if e.get("state") == "running" else 1
553
+ return (is_running, e.get("name", "").lower())
577
554
 
578
- # Table header
579
- click.echo(
580
- f"│ {'Name':<{name_width}}│ {'Uptime':>{uptime_width}}│ {'State':<{state_width}}│ {'User':<{user_width}}│ {'Type':<{type_width}}│ {'Disk':>{disk_width}}│"
581
- )
555
+ engines.sort(key=sort_key)
582
556
 
583
- # Header separator
584
- click.echo(
585
- "├"
586
- + "─" * (name_width + 1)
587
- + "┼"
588
- + "─" * (uptime_width + 1)
589
- + "┼"
590
- + "─" * (state_width + 1)
591
- + "┼"
592
- + "─" * (user_width + 1)
593
- + "┼"
594
- + "─" * (type_width + 1)
595
- + "┼"
596
- + "─" * (disk_width + 1)
597
- + "┤"
598
- )
557
+ # Pre-compute uptime text and uptime_seconds for each engine
558
+ from datetime import datetime
599
559
 
600
- # Table rows
560
+ uptime_data = []
601
561
  for engine in engines:
602
- name = engine.get("name", "unknown")
603
562
  state = engine.get("state", "unknown")
604
- user = engine.get("user", "unknown")
605
- engine_type = engine.get("engine_type", "unknown")
563
+ launch_time = engine.get("launch_time")
564
+ if state == "running" and launch_time:
565
+ text = format_time_ago(launch_time).removesuffix(" ago")
566
+ try:
567
+ dt = datetime.fromisoformat(launch_time.replace("Z", "+00:00"))
568
+ secs = (datetime.now(dt.tzinfo) - dt).total_seconds()
569
+ except Exception:
570
+ secs = 0
571
+ else:
572
+ text = "-"
573
+ secs = 0
574
+ uptime_data.append((text, secs))
606
575
 
607
- # Truncate if needed
608
- if len(name) > name_width - 1:
609
- name = name[: name_width - 1]
610
- if len(user) > user_width - 1:
611
- user = user[: user_width - 1]
612
- if len(engine_type) > type_width - 1:
613
- engine_type = engine_type[: type_width - 1]
576
+ # Pre-compute disk text for each engine
577
+ disk_data = []
578
+ for engine in engines:
579
+ pct = engine.get("boot_disk_usage_pct")
580
+ disk_data.append(f"{pct}%" if pct is not None else "-")
581
+
582
+ # Dynamic column widths: fit the longest value or header, plus padding
583
+ def col_width(header, values):
584
+ longest = max((len(v) for v in values), default=0)
585
+ return max(longest, len(header)) + 1
586
+
587
+ names = [engine.get("name", "unknown") for engine in engines]
588
+ states = [engine.get("state", "unknown") for engine in engines]
589
+ users = [engine.get("user", "unknown") for engine in engines]
590
+ types = [engine.get("engine_type", "unknown") for engine in engines]
591
+ uptimes = [ud[0] for ud in uptime_data]
592
+ disks = [d for d in disk_data]
593
+
594
+ nw = col_width("Name", names)
595
+ sw = col_width("State", states)
596
+ uw = col_width("User", users)
597
+ tw = col_width("Type", types)
598
+ utw = col_width("Uptime", uptimes)
599
+ dw = col_width("Disk", disks)
600
+
601
+ cols = [nw, sw, uw, tw, utw, dw]
602
+ headers = ["Name", "State", "User", "Type", "Uptime", "Disk"]
603
+ aligns = ["<", "<", "<", "<", ">", ">"]
604
+
605
+ def border(left, mid, right):
606
+ return left + mid.join("─" * (w + 1) for w in cols) + right
607
+
608
+ click.echo(border("╭", "┬", "╮"))
609
+ click.echo(
610
+ "│"
611
+ + "│".join(f" {h:{a}{w}}" for h, a, w in zip(headers, aligns, cols))
612
+ + "│"
613
+ )
614
+ click.echo(border("├", "┼", "┤"))
615
+
616
+ for i, engine in enumerate(engines):
617
+ name = names[i]
618
+ state = states[i]
619
+ user = users[i]
620
+ etype = types[i]
621
+ uptime_text, uptime_secs = uptime_data[i]
622
+ disk_text = disk_data[i]
614
623
 
615
- # Color the name (blue)
616
- name_display = f"\033[34m{name:<{name_width}}\033[0m"
624
+ name_d = f"\033[34m{name:<{nw}}\033[0m"
617
625
 
618
- # Color the state
619
626
  if state == "running":
620
- state_display = f"\033[32m{state:<{state_width}}\033[0m" # Green
621
- elif state in ["starting", "stopping", "pending"]:
622
- state_display = f"\033[33m{state:<{state_width}}\033[0m" # Yellow
627
+ state_d = f"\033[32m{state:<{sw}}\033[0m"
628
+ elif state in ("starting", "stopping", "pending"):
629
+ state_d = f"\033[33m{state:<{sw}}\033[0m"
623
630
  elif state == "stopped":
624
- state_display = f"\033[90m{state:<{state_width}}\033[0m" # Grey (dim)
631
+ state_d = f"\033[90m{state:<{sw}}\033[0m"
625
632
  else:
626
- state_display = f"{state:<{state_width}}" # No color for other states
633
+ state_d = f"{state:<{sw}}"
627
634
 
628
- # Compute uptime for running engines from launch_time
629
- launch_time = engine.get("launch_time")
630
- if state == "running" and launch_time:
631
- uptime_text = format_time_ago(launch_time).removesuffix(" ago")
632
- uptime_display = f"\033[32m{uptime_text:>{uptime_width}}\033[0m"
635
+ if uptime_text == "-":
636
+ uptime_d = f"\033[90m{uptime_text:>{utw}}\033[0m"
637
+ elif uptime_secs >= 86400:
638
+ uptime_d = f"\033[33m{uptime_text:>{utw}}\033[0m"
633
639
  else:
634
- uptime_display = f"\033[90m{'-':>{uptime_width}}\033[0m"
635
-
636
- # Color the disk usage
637
- usage_pct = engine.get("boot_disk_usage_pct")
638
- if usage_pct is not None:
639
- disk_text = f"{usage_pct}%"
640
- if usage_pct >= 90:
641
- disk_display = f"\033[31m{disk_text:>{disk_width}}\033[0m"
642
- elif usage_pct >= 70:
643
- disk_display = f"\033[33m{disk_text:>{disk_width}}\033[0m"
644
- else:
645
- disk_display = f"\033[32m{disk_text:>{disk_width}}\033[0m"
640
+ uptime_d = f"\033[32m{uptime_text:>{utw}}\033[0m"
641
+
642
+ pct = engine.get("boot_disk_usage_pct")
643
+ if disk_text == "-":
644
+ disk_d = f"\033[90m{disk_text:>{dw}}\033[0m"
645
+ elif pct >= 90:
646
+ disk_d = f"\033[31m{disk_text:>{dw}}\033[0m"
647
+ elif pct >= 70:
648
+ disk_d = f"\033[33m{disk_text:>{dw}}\033[0m"
646
649
  else:
647
- disk_display = f"\033[90m{'-':>{disk_width}}\033[0m"
650
+ disk_d = f"\033[32m{disk_text:>{dw}}\033[0m"
648
651
 
649
652
  click.echo(
650
- f"│ {name_display}│ {uptime_display}│ {state_display}│ {user:<{user_width}}│ {engine_type:<{type_width}}│ {disk_display}│"
653
+ f"│ {name_d}│ {state_d}│ {user:<{uw}}│ {etype:<{tw}}│ {uptime_d}│ {disk_d}│"
651
654
  )
652
655
 
653
- # Table bottom border
654
- click.echo(
655
- "╰"
656
- + "─" * (name_width + 1)
657
- + "┴"
658
- + "─" * (uptime_width + 1)
659
- + "┴"
660
- + "─" * (state_width + 1)
661
- + "┴"
662
- + "─" * (user_width + 1)
663
- + "┴"
664
- + "─" * (type_width + 1)
665
- + "┴"
666
- + "─" * (disk_width + 1)
667
- + "╯"
668
- )
669
-
656
+ click.echo(border("╰", "┴", "╯"))
670
657
  click.echo(f"Total: {len(engines)}\n")
671
658
 
672
659
  except Exception as e:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes