dayhoff-tools 1.4.7__tar.gz → 1.4.9__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.
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/PKG-INFO +1 -1
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/cli/engine_commands.py +69 -43
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/pyproject.toml +1 -1
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/README.md +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/__init__.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/chemistry/standardizer.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/chemistry/utils.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/cli/__init__.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/cli/cloud_commands.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/cli/main.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/cli/swarm_commands.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/cli/utility_commands.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/deployment/base.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/deployment/deploy_aws.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/deployment/deploy_gcp.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/deployment/deploy_utils.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/deployment/job_runner.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/deployment/processors.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/deployment/swarm.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/embedders.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/fasta.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/file_ops.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/h5.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/intake/gcp.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/intake/gtdb.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/intake/kegg.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/intake/mmseqs.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/intake/structure.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/intake/uniprot.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/logs.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/sqlite.py +0 -0
- {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.9}/dayhoff_tools/warehouse.py +0 -0
@@ -1618,33 +1618,12 @@ def attach_studio(
|
|
1618
1618
|
console.print("[red]❌ Failed to start engine[/red]")
|
1619
1619
|
raise typer.Exit(1)
|
1620
1620
|
console.print("[green]✓ Engine started[/green]")
|
1621
|
-
|
1622
|
-
#
|
1623
|
-
console.print("Waiting for engine to be ready (this can take a couple of minutes)…")
|
1624
|
-
# Progress classes already imported at module level – do not re-import to avoid UnboundLocalError
|
1625
|
-
|
1626
|
-
with Progress(SpinnerColumn(), TimeElapsedColumn(), TextColumn("[progress.description]{task.description}"), transient=True) as prog:
|
1627
|
-
prog.add_task("Awaiting engine readiness…", total=None)
|
1628
|
-
max_attempts = 30 # 30 × 10 s ≈ 5 min
|
1629
|
-
for _ in range(max_attempts):
|
1630
|
-
time.sleep(10)
|
1631
|
-
|
1632
|
-
status_resp = make_api_request("GET", f"/engines/{engine['instance_id']}")
|
1633
|
-
if status_resp.status_code != 200:
|
1634
|
-
continue
|
1635
|
-
|
1636
|
-
detailed = status_resp.json().get("engine", {})
|
1637
|
-
state_ok = detailed.get("state", "").lower() == "running"
|
1638
|
-
ready_ok = detailed.get("ready") is True
|
1639
|
-
|
1640
|
-
if state_ok and ready_ok:
|
1641
|
-
break
|
1642
|
-
else:
|
1643
|
-
console.print("[yellow]Engine is still starting up. If attachment fails, wait a little longer and retry.[/yellow]")
|
1621
|
+
# No further waiting here – attachment attempts below handle retry logic while the
|
1622
|
+
# engine finishes booting.
|
1644
1623
|
else:
|
1645
1624
|
raise typer.Exit(1)
|
1646
1625
|
|
1647
|
-
#
|
1626
|
+
# Retrieve SSH public key (required for authorised_keys provisioning)
|
1648
1627
|
try:
|
1649
1628
|
public_key = get_ssh_public_key()
|
1650
1629
|
except FileNotFoundError as e:
|
@@ -1655,34 +1634,81 @@ def attach_studio(
|
|
1655
1634
|
|
1656
1635
|
with Progress(
|
1657
1636
|
SpinnerColumn(),
|
1637
|
+
TimeElapsedColumn(),
|
1658
1638
|
TextColumn("[progress.description]{task.description}"),
|
1659
1639
|
transient=True,
|
1660
|
-
) as
|
1661
|
-
task =
|
1640
|
+
) as prog:
|
1641
|
+
task = prog.add_task("Attaching studio (engine is still booting)…", total=None)
|
1642
|
+
ATTEMPT_LIMIT = 40 # ~6-7 min max (40 × 10 s)
|
1643
|
+
RETRY_DELAY = 10
|
1644
|
+
for attempt in range(ATTEMPT_LIMIT):
|
1645
|
+
success, error_msg = _attempt_studio_attach(studio, engine, target_user, public_key)
|
1662
1646
|
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1647
|
+
if success:
|
1648
|
+
break # success!
|
1649
|
+
|
1650
|
+
# Update spinner description with attempt number
|
1651
|
+
prog.update(task, description=f"Attaching studio (engine is still booting)… {attempt+1}/{ATTEMPT_LIMIT}")
|
1652
|
+
|
1653
|
+
if error_msg:
|
1654
|
+
console.print(f"[red]❌ Failed to attach studio: {error_msg}[/red]")
|
1655
|
+
return
|
1656
|
+
|
1657
|
+
time.sleep(RETRY_DELAY)
|
1658
|
+
|
1659
|
+
else:
|
1660
|
+
console.print("[yellow]Engine is still starting up – please retry in a minute.[/yellow]")
|
1661
|
+
return
|
1662
|
+
|
1663
|
+
# Successful attach path
|
1664
|
+
console.print(f"[green]✓ Studio attached successfully![/green]")
|
1672
1665
|
|
1673
|
-
|
1666
|
+
# Update SSH config - use target_user for the connection
|
1667
|
+
update_ssh_config_entry(engine["name"], engine["instance_id"], target_user)
|
1668
|
+
console.print(f"[green]✓ SSH config updated[/green]")
|
1669
|
+
console.print(f"\nConnect with: [cyan]ssh {engine['name']}[/cyan]")
|
1670
|
+
console.print(f"Files are at: [cyan]/studios/{target_user}[/cyan]")
|
1671
|
+
|
1672
|
+
|
1673
|
+
def _attempt_studio_attach(studio, engine, target_user, public_key):
|
1674
|
+
response = make_api_request(
|
1675
|
+
"POST",
|
1676
|
+
f"/studios/{studio['studio_id']}/attach",
|
1677
|
+
json_data={
|
1678
|
+
"vm_id": engine["instance_id"],
|
1679
|
+
"user": target_user,
|
1680
|
+
"public_key": public_key,
|
1681
|
+
},
|
1682
|
+
)
|
1674
1683
|
|
1675
1684
|
if response.status_code == 200:
|
1676
|
-
|
1685
|
+
return True, None
|
1677
1686
|
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
console.print(f"Files are at: [cyan]/studios/{target_user}[/cyan]")
|
1687
|
+
# --- determine if we should retry ---
|
1688
|
+
recoverable = False
|
1689
|
+
if response.status_code in (409, 503):
|
1690
|
+
recoverable = True
|
1683
1691
|
else:
|
1692
|
+
err_msg = response.json().get("error", "").lower()
|
1693
|
+
RECOVERABLE_PATTERNS = [
|
1694
|
+
"not ready",
|
1695
|
+
"still starting",
|
1696
|
+
"initializing",
|
1697
|
+
"failed to mount",
|
1698
|
+
"device busy",
|
1699
|
+
"not available",
|
1700
|
+
"pending", # VM state pending
|
1701
|
+
]
|
1702
|
+
if any(p in err_msg for p in RECOVERABLE_PATTERNS):
|
1703
|
+
recoverable = True
|
1704
|
+
|
1705
|
+
if not recoverable:
|
1706
|
+
# fatal – abort immediately and show message
|
1684
1707
|
error = response.json().get("error", "Unknown error")
|
1685
|
-
|
1708
|
+
return False, error
|
1709
|
+
|
1710
|
+
# otherwise wait and retry
|
1711
|
+
return False, None
|
1686
1712
|
|
1687
1713
|
|
1688
1714
|
@studio_app.command("detach")
|
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
|
|
5
5
|
|
6
6
|
[project]
|
7
7
|
name = "dayhoff-tools"
|
8
|
-
version = "1.4.
|
8
|
+
version = "1.4.9"
|
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
|
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
|
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
|