dayhoff-tools 1.9.12__py3-none-any.whl → 1.9.13__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.
@@ -217,8 +217,12 @@ def list_engines(
217
217
 
218
218
  def engine_status(
219
219
  name_or_id: str = typer.Argument(help="Engine name or instance ID"),
220
- detailed: bool = typer.Option(False, "--detailed", "-d", help="Show detailed status (slower)"),
221
- show_log: bool = typer.Option(False, "--show-log", help="Show bootstrap log (requires --detailed)"),
220
+ detailed: bool = typer.Option(
221
+ False, "--detailed", "-d", help="Show detailed status (slower)"
222
+ ),
223
+ show_log: bool = typer.Option(
224
+ False, "--show-log", help="Show bootstrap log (requires --detailed)"
225
+ ),
222
226
  ):
223
227
  """Show engine status and information."""
224
228
  check_aws_sso()
@@ -477,6 +481,47 @@ def engine_status(
477
481
  except Exception:
478
482
  pass
479
483
 
484
+ # Slack notifications status (detailed view only)
485
+ try:
486
+ ssm = boto3.client("ssm", region_name="us-east-1")
487
+ resp = ssm.send_command(
488
+ InstanceIds=[engine["instance_id"]],
489
+ DocumentName="AWS-RunShellScript",
490
+ Parameters={
491
+ "commands": ["grep '^SLACK_NOTIFY_' /etc/engine.env || true"],
492
+ "executionTimeout": ["10"],
493
+ },
494
+ )
495
+ cid = resp["Command"]["CommandId"]
496
+ time.sleep(1)
497
+ inv = ssm.get_command_invocation(
498
+ CommandId=cid, InstanceId=engine["instance_id"]
499
+ )
500
+ if inv["Status"] == "Success":
501
+ settings_raw = inv["StandardOutputContent"].strip()
502
+ settings = {}
503
+ for line in settings_raw.splitlines():
504
+ if "=" in line:
505
+ key, value = line.split("=", 1)
506
+ settings[key.strip()] = value.strip().lower()
507
+
508
+ status_lines.append("")
509
+ status_lines.append("[bold]Slack Notifications:[/bold]")
510
+
511
+ def _setting_line(label: str, key: str) -> str:
512
+ val = settings.get(key, "false") # Default to false if not set
513
+ status = "[green]on[/green]" if val == "true" else "[dim]off[/dim]"
514
+ return f" - {label:15} {status}"
515
+
516
+ status_lines.append(_setting_line("Idle Start", "SLACK_NOTIFY_IDLE_START"))
517
+ status_lines.append(
518
+ _setting_line("IDE Disconnect", "SLACK_NOTIFY_IDE_DISCONNECT")
519
+ )
520
+ status_lines.append(_setting_line("Warnings", "SLACK_NOTIFY_WARNINGS"))
521
+ status_lines.append(_setting_line("Shutdown", "SLACK_NOTIFY_SHUTDOWN"))
522
+ except Exception:
523
+ pass
524
+
480
525
  # Activity Sensors (show all with YES/no)
481
526
  if idle_detector.get("available"):
482
527
  status_lines.append("")
@@ -564,10 +609,14 @@ def _format_idle_status_display(
564
609
  ):
565
610
  remaining = max(0, int(thresh_v) - int(idle_seconds_v))
566
611
  remaining_mins = remaining // 60
567
- if remaining_mins == 0:
568
- return f"[yellow]Idle {int(idle_seconds_v)//60}m/{int(thresh_v)//60}m: [red]<1m[/red] left[/yellow]"
612
+ remaining_secs = remaining % 60
613
+
614
+ if remaining < 60:
615
+ time_left_str = f"[red]{remaining}s[/red] left"
569
616
  else:
570
- return f"[yellow]Idle {int(idle_seconds_v)//60}m/{int(thresh_v)//60}m: [red]{remaining_mins}m[/red] left[/yellow]"
617
+ time_left_str = f"[red]{remaining_mins}m {remaining_secs}s[/red] left"
618
+
619
+ return f"[yellow]Idle {int(idle_seconds_v)//60}m/{int(thresh_v)//60}m: {time_left_str}[/yellow]"
571
620
  elif isinstance(thresh_v, (int, float)):
572
621
  return f"[yellow]Idle ?/{int(thresh_v)//60}m[/yellow]"
573
622
  else:
@@ -11,12 +11,7 @@ from botocore.exceptions import ClientError
11
11
  from rich.progress import Progress, SpinnerColumn, TextColumn
12
12
  from rich.prompt import Confirm
13
13
 
14
- from .shared import (
15
- check_aws_sso,
16
- console,
17
- make_api_request,
18
- resolve_engine,
19
- )
14
+ from .shared import check_aws_sso, console, make_api_request, resolve_engine
20
15
 
21
16
 
22
17
  def coffee(
@@ -123,9 +118,11 @@ def idle_timeout_cmd(
123
118
  set: Optional[str] = typer.Option(
124
119
  None, "--set", "-s", help="New timeout (e.g., 2h30m, 45m)"
125
120
  ),
126
-
121
+ slack: Optional[str] = typer.Option(
122
+ None, "--slack", help="Set Slack notifications: none, default, all"
123
+ ),
127
124
  ):
128
- """Show or set the engine idle-detector timeout."""
125
+ """Show or set engine idle-detector settings."""
129
126
  check_aws_sso()
130
127
 
131
128
  # Resolve engine
@@ -139,7 +136,56 @@ def idle_timeout_cmd(
139
136
 
140
137
  ssm = boto3.client("ssm", region_name="us-east-1")
141
138
 
142
- if set is None:
139
+ # Handle slack notifications change
140
+ if slack:
141
+ slack = slack.lower()
142
+ if slack not in ["none", "default", "all"]:
143
+ console.print("[red]❌ Invalid slack option. Use: none, default, all[/red]")
144
+ raise typer.Exit(1)
145
+
146
+ console.print(f"Setting Slack notifications to [bold]{slack}[/bold]...")
147
+
148
+ if slack == "none":
149
+ settings = {
150
+ "SLACK_NOTIFY_WARNINGS": "false",
151
+ "SLACK_NOTIFY_IDLE_START": "false",
152
+ "SLACK_NOTIFY_IDE_DISCONNECT": "false",
153
+ "SLACK_NOTIFY_SHUTDOWN": "false",
154
+ }
155
+ elif slack == "default":
156
+ settings = {
157
+ "SLACK_NOTIFY_WARNINGS": "true",
158
+ "SLACK_NOTIFY_IDLE_START": "false",
159
+ "SLACK_NOTIFY_IDE_DISCONNECT": "false",
160
+ "SLACK_NOTIFY_SHUTDOWN": "true",
161
+ }
162
+ else: # all
163
+ settings = {
164
+ "SLACK_NOTIFY_WARNINGS": "true",
165
+ "SLACK_NOTIFY_IDLE_START": "true",
166
+ "SLACK_NOTIFY_IDE_DISCONNECT": "true",
167
+ "SLACK_NOTIFY_SHUTDOWN": "true",
168
+ }
169
+
170
+ commands = []
171
+ for key, value in settings.items():
172
+ # Use a robust sed command that adds the line if it doesn't exist
173
+ commands.append(
174
+ f"grep -q '^{key}=' /etc/engine.env && sudo sed -i 's|^{key}=.*|{key}={value}|' /etc/engine.env || echo '{key}={value}' | sudo tee -a /etc/engine.env > /dev/null"
175
+ )
176
+
177
+ commands.append("sudo systemctl restart engine-idle-detector.service")
178
+
179
+ resp = ssm.send_command(
180
+ InstanceIds=[engine["instance_id"]],
181
+ DocumentName="AWS-RunShellScript",
182
+ Parameters={"commands": commands, "executionTimeout": ["60"]},
183
+ )
184
+ cid = resp["Command"]["CommandId"]
185
+ time.sleep(2) # Give it a moment to process
186
+ console.print(f"[green]✓ Slack notifications updated to '{slack}'[/green]")
187
+
188
+ if set is None and not slack:
143
189
  # Show current timeout setting
144
190
  resp = ssm.send_command(
145
191
  InstanceIds=[engine["instance_id"]],
@@ -164,7 +210,7 @@ def idle_timeout_cmd(
164
210
  console.print("[red]❌ Could not retrieve idle timeout[/red]")
165
211
  return
166
212
 
167
- # ----- set new value -----
213
+ # ----- set new value for timeout -----
168
214
  m = re.match(r"^(?:(\d+)h)?(?:(\d+)m)?$", set)
169
215
  if not m:
170
216
  console.print("[red]❌ Invalid duration format. Use e.g. 2h, 45m, 1h30m[/red]")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dayhoff-tools
3
- Version: 1.9.12
3
+ Version: 1.9.13
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
@@ -4,9 +4,9 @@ dayhoff_tools/chemistry/utils.py,sha256=jt-7JgF-GeeVC421acX-bobKbLU_X94KNOW24p_P
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
6
  dayhoff_tools/cli/engine/__init__.py,sha256=CGJ2blhWIIEsVb8HoLibZjSlMFRTSYZOO4zDQTtY3SY,9300
7
- dayhoff_tools/cli/engine/engine_core.py,sha256=IEU2m93qArFx-EdVHnepWKvLwlNUAcT7ytIAqeOY6a0,25147
7
+ dayhoff_tools/cli/engine/engine_core.py,sha256=ItfqW3ZWoHEWLOgtWPYT-SO1idxk5RBBCIe8w9xxf3w,26959
8
8
  dayhoff_tools/cli/engine/engine_lifecycle.py,sha256=_Dk-EZs_qbm8APdOuGOuxhlbK6RgkkoLk2nrwKoo1-A,4519
9
- dayhoff_tools/cli/engine/engine_maintenance.py,sha256=Vz4FpbM0eyfl9tTM6Q8z0ZzS2Ug5gAE-uKVbqBHkznU,13761
9
+ dayhoff_tools/cli/engine/engine_maintenance.py,sha256=xZgEgpjwTDI2RpoN7P6E7MEGiIA8RJpb0MkCT9u2R14,15889
10
10
  dayhoff_tools/cli/engine/engine_management.py,sha256=s_H3FtMlKsdfzR8pwV-j2W2QX-Fypkqj2kPC0aTqC1A,19072
11
11
  dayhoff_tools/cli/engine/shared.py,sha256=Ecx6I1jtzmxQDn3BezKpgpQ4SJeZf4SZjUCLg-67p80,16844
12
12
  dayhoff_tools/cli/engine/studio_commands.py,sha256=VwTQujz32-uMcYusDRE73SdzRpgvIkv7ZAF4zRv6AzA,30266
@@ -33,7 +33,7 @@ dayhoff_tools/intake/uniprot.py,sha256=BZYJQF63OtPcBBnQ7_P9gulxzJtqyorgyuDiPeOJq
33
33
  dayhoff_tools/logs.py,sha256=DKdeP0k0kliRcilwvX0mUB2eipO5BdWUeHwh-VnsICs,838
34
34
  dayhoff_tools/sqlite.py,sha256=jV55ikF8VpTfeQqqlHSbY8OgfyfHj8zgHNpZjBLos_E,18672
35
35
  dayhoff_tools/warehouse.py,sha256=UETBtZD3r7WgvURqfGbyHlT7cxoiVq8isjzMuerKw8I,24475
36
- dayhoff_tools-1.9.12.dist-info/METADATA,sha256=EkiO1dejnv9KxAs8X7ycLdfGUlph70tyIeNPWQc-12U,2915
37
- dayhoff_tools-1.9.12.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
38
- dayhoff_tools-1.9.12.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
39
- dayhoff_tools-1.9.12.dist-info/RECORD,,
36
+ dayhoff_tools-1.9.13.dist-info/METADATA,sha256=Sf1ILU9mg5Cvs7NepbxAB0NJSuKM_gMqrKopTykTqDM,2915
37
+ dayhoff_tools-1.9.13.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
38
+ dayhoff_tools-1.9.13.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
39
+ dayhoff_tools-1.9.13.dist-info/RECORD,,