dayhoff-tools 1.13.7__py3-none-any.whl → 1.13.9__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.
@@ -120,7 +120,7 @@ class StudioManagerClient:
120
120
  """
121
121
  url = f"{self.api_url}{path}"
122
122
  response = requests.request(method, url, **kwargs)
123
-
123
+
124
124
  # Parse error body if request failed
125
125
  if not response.ok:
126
126
  try:
@@ -128,10 +128,10 @@ class StudioManagerClient:
128
128
  error_message = error_body.get("error", response.text)
129
129
  except Exception:
130
130
  error_message = response.text or f"HTTP {response.status_code}"
131
-
131
+
132
132
  # Raise exception with the actual error message from API
133
133
  raise RuntimeError(error_message)
134
-
134
+
135
135
  return response.json()
136
136
 
137
137
  # Engine operations
@@ -75,6 +75,7 @@ def _update_ssh_config_silent(client: StudioManagerClient, env: str) -> bool:
75
75
  config_entries.append(f"\nHost {name}\n")
76
76
  config_entries.append(f" HostName {instance_id}\n")
77
77
  config_entries.append(f" User {user}\n")
78
+ config_entries.append(f" ForwardAgent yes\n")
78
79
  config_entries.append(
79
80
  f" ProxyCommand aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --profile {profile}\n"
80
81
  )
@@ -886,6 +887,7 @@ def config_ssh(clean: bool, all: bool, admin: bool, env: Optional[str]):
886
887
  config_entries.append(f"\nHost {name}\n")
887
888
  config_entries.append(f" HostName {instance_id}\n")
888
889
  config_entries.append(f" User {username}\n")
890
+ config_entries.append(f" ForwardAgent yes\n")
889
891
  config_entries.append(
890
892
  f" ProxyCommand aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --profile {aws_profile}\n"
891
893
  )
@@ -207,8 +207,12 @@ def format_idle_state(
207
207
 
208
208
  if isinstance(value, list):
209
209
  if value: # Only show non-empty lists
210
- # All lists shown with bullets, no header for containers/connections/sessions
211
- if key in ["containers", "connections", "sessions"]:
210
+ # Show workload containers with clear header
211
+ if key == "containers":
212
+ # Show actual workload container names that are keeping engine active
213
+ for item in value[:5]:
214
+ lines.append(f" • {item}")
215
+ elif key in ["connections", "sessions"]:
212
216
  # Just show the items with bullets, no header
213
217
  for item in value[:5]:
214
218
  lines.append(f" • {item}")
@@ -20,7 +20,9 @@ def colorize(text: str, color_code: str) -> str:
20
20
  return f"\033[{color_code}m{text}\033[0m"
21
21
 
22
22
 
23
- def format_list_output(studios: list[dict[str, Any]], engines_map: dict[str, str], env: str = "dev") -> None:
23
+ def format_list_output(
24
+ studios: list[dict[str, Any]], engines_map: dict[str, str], env: str = "dev"
25
+ ) -> None:
24
26
  """Format and print studio list output matching the actual CLI."""
25
27
 
26
28
  # Header with blue account name
@@ -31,7 +33,9 @@ def format_list_output(studios: list[dict[str, Any]], engines_map: dict[str, str
31
33
  return
32
34
 
33
35
  # Calculate dynamic width for User column (longest user + 2 for padding)
34
- max_user_len = max((len(studio.get("user", "unknown")) for studio in studios), default=4)
36
+ max_user_len = max(
37
+ (len(studio.get("user", "unknown")) for studio in studios), default=4
38
+ )
35
39
  user_width = max(max_user_len + 2, len("User") + 2)
36
40
 
37
41
  # Calculate dynamic width for Attached To column
@@ -41,7 +45,9 @@ def format_list_output(studios: list[dict[str, Any]], engines_map: dict[str, str
41
45
  instance_id = studio["attached_to"]
42
46
  engine_name = engines_map.get(instance_id, "unknown")
43
47
  max_attached_len = max(max_attached_len, len(engine_name))
44
- attached_width = max(max_attached_len + 2, len("Attached To") + 2, 3) # At least 3 for "-"
48
+ attached_width = max(
49
+ max_attached_len + 2, len("Attached To") + 2, 3
50
+ ) # At least 3 for "-"
45
51
 
46
52
  # Fixed widths for other columns
47
53
  status_width = 12
@@ -49,13 +55,39 @@ def format_list_output(studios: list[dict[str, Any]], engines_map: dict[str, str
49
55
  id_width = 25
50
56
 
51
57
  # Table top border
52
- print("╭" + "─" * (user_width + 1) + "┬" + "─" * (status_width + 1) + "┬" + "─" * (attached_width + 1) + "┬" + "─" * (size_width + 1) + "┬" + "─" * (id_width + 1) + "╮")
58
+ print(
59
+ "╭"
60
+ + "─" * (user_width + 1)
61
+ + "┬"
62
+ + "─" * (status_width + 1)
63
+ + "┬"
64
+ + "─" * (attached_width + 1)
65
+ + "┬"
66
+ + "─" * (size_width + 1)
67
+ + "┬"
68
+ + "─" * (id_width + 1)
69
+ + "╮"
70
+ )
53
71
 
54
72
  # Table header
55
- print(f"│ {'User':<{user_width}}│ {'Status':<{status_width}}│ {'Attached To':<{attached_width}}│ {'Size':<{size_width}}│ {'Studio ID':<{id_width}}│")
73
+ print(
74
+ f"│ {'User':<{user_width}}│ {'Status':<{status_width}}│ {'Attached To':<{attached_width}}│ {'Size':<{size_width}}│ {'Studio ID':<{id_width}}│"
75
+ )
56
76
 
57
77
  # Header separator
58
- print("├" + "─" * (user_width + 1) + "┼" + "─" * (status_width + 1) + "┼" + "─" * (attached_width + 1) + "┼" + "─" * (size_width + 1) + "┼" + "─" * (id_width + 1) + "┤")
78
+ print(
79
+ "├"
80
+ + "─" * (user_width + 1)
81
+ + "┼"
82
+ + "─" * (status_width + 1)
83
+ + "┼"
84
+ + "─" * (attached_width + 1)
85
+ + "┼"
86
+ + "─" * (size_width + 1)
87
+ + "┼"
88
+ + "─" * (id_width + 1)
89
+ + "┤"
90
+ )
59
91
 
60
92
  # Table rows
61
93
  for studio in studios:
@@ -67,7 +99,7 @@ def format_list_output(studios: list[dict[str, Any]], engines_map: dict[str, str
67
99
 
68
100
  # Truncate if needed
69
101
  if len(user) > user_width - 1:
70
- user = user[:user_width - 1]
102
+ user = user[: user_width - 1]
71
103
 
72
104
  # Color the user (blue)
73
105
  user_display = colorize(f"{user:<{user_width}}", "34")
@@ -75,7 +107,9 @@ def format_list_output(studios: list[dict[str, Any]], engines_map: dict[str, str
75
107
  # Format status - display "in-use" as "attached" in purple
76
108
  if status == "in-use":
77
109
  display_status = "attached"
78
- status_display = colorize(f"{display_status:<{status_width}}", "35") # Purple
110
+ status_display = colorize(
111
+ f"{display_status:<{status_width}}", "35"
112
+ ) # Purple
79
113
  elif status == "available":
80
114
  status_display = colorize(f"{status:<{status_width}}", "32") # Green
81
115
  elif status in ["attaching", "detaching"]:
@@ -99,10 +133,24 @@ def format_list_output(studios: list[dict[str, Any]], engines_map: dict[str, str
99
133
  # Color the studio ID (grey)
100
134
  studio_id_display = colorize(f"{studio_id:<{id_width}}", "90")
101
135
 
102
- print(f"│ {user_display}│ {status_display}│ {attached_display}│ {size:<{size_width}}│ {studio_id_display}│")
136
+ print(
137
+ f"│ {user_display}│ {status_display}│ {attached_display}│ {size:<{size_width}}│ {studio_id_display}│"
138
+ )
103
139
 
104
140
  # Table bottom border
105
- print("╰" + "─" * (user_width + 1) + "┴" + "─" * (status_width + 1) + "┴" + "─" * (attached_width + 1) + "┴" + "─" * (size_width + 1) + "┴" + "─" * (id_width + 1) + "╯")
141
+ print(
142
+ "╰"
143
+ + "─" * (user_width + 1)
144
+ + "┴"
145
+ + "─" * (status_width + 1)
146
+ + "┴"
147
+ + "─" * (attached_width + 1)
148
+ + "┴"
149
+ + "─" * (size_width + 1)
150
+ + "┴"
151
+ + "─" * (id_width + 1)
152
+ + "╯"
153
+ )
106
154
 
107
155
  print(f"Total: {len(studios)}\n")
108
156
 
@@ -309,7 +357,9 @@ def main():
309
357
  print("=" * 80 + "\n")
310
358
 
311
359
  env = args.env if args.env else scenario_data["env"]
312
- format_list_output(scenario_data["studios"], scenario_data["engines_map"], env)
360
+ format_list_output(
361
+ scenario_data["studios"], scenario_data["engines_map"], env
362
+ )
313
363
  print() # Extra newline between scenarios
314
364
  else:
315
365
  # Show specific scenario
@@ -322,4 +372,3 @@ def main():
322
372
 
323
373
  if __name__ == "__main__":
324
374
  main()
325
-
@@ -222,9 +222,18 @@ def studio_status(user: Optional[str], env: Optional[str]):
222
222
  click.echo(f"User: {studio['user']}")
223
223
  # Status in blue
224
224
  click.echo(f"Status: \033[34m{studio['status']}\033[0m")
225
- # Attached to in blue (if present)
225
+ # Attached to in blue (if present) - resolve instance ID to engine name
226
226
  if studio.get("attached_to"):
227
- click.echo(f"Attached to: \033[34m{studio['attached_to']}\033[0m")
227
+ instance_id = studio["attached_to"]
228
+ # Try to resolve instance ID to engine name
229
+ engine_name = instance_id # Default to instance ID if not found
230
+ try:
231
+ engine = client.get_engine_by_name(instance_id)
232
+ if engine:
233
+ engine_name = engine.get("name", instance_id)
234
+ except Exception:
235
+ pass # Fall back to instance ID
236
+ click.echo(f"Attached to: \033[34m{engine_name}\033[0m")
228
237
  click.echo(f"Account: {env}")
229
238
  click.echo(f"Size: {studio['size_gb']}GB")
230
239
  if studio.get("created_at"):
@@ -272,7 +281,9 @@ def list_studios(env: Optional[str]):
272
281
  engines_map[engine["instance_id"]] = engine["name"]
273
282
 
274
283
  # Calculate dynamic width for User column (longest user + 2 for padding)
275
- max_user_len = max((len(studio.get("user", "unknown")) for studio in studios), default=4)
284
+ max_user_len = max(
285
+ (len(studio.get("user", "unknown")) for studio in studios), default=4
286
+ )
276
287
  user_width = max(max_user_len + 2, len("User") + 2)
277
288
 
278
289
  # Calculate dynamic width for Attached To column
@@ -282,7 +293,9 @@ def list_studios(env: Optional[str]):
282
293
  instance_id = studio["attached_to"]
283
294
  engine_name = engines_map.get(instance_id, "unknown")
284
295
  max_attached_len = max(max_attached_len, len(engine_name))
285
- attached_width = max(max_attached_len + 2, len("Attached To") + 2, 3) # At least 3 for "-"
296
+ attached_width = max(
297
+ max_attached_len + 2, len("Attached To") + 2, 3
298
+ ) # At least 3 for "-"
286
299
 
287
300
  # Fixed widths for other columns - reordered to [User, Status, Attached To, Size, Studio ID]
288
301
  status_width = 12
@@ -290,7 +303,19 @@ def list_studios(env: Optional[str]):
290
303
  id_width = 25
291
304
 
292
305
  # Table top border
293
- click.echo("╭" + "─" * (user_width + 1) + "┬" + "─" * (status_width + 1) + "┬" + "─" * (attached_width + 1) + "┬" + "─" * (size_width + 1) + "┬" + "─" * (id_width + 1) + "╮")
306
+ click.echo(
307
+ "╭"
308
+ + "─" * (user_width + 1)
309
+ + "┬"
310
+ + "─" * (status_width + 1)
311
+ + "┬"
312
+ + "─" * (attached_width + 1)
313
+ + "┬"
314
+ + "─" * (size_width + 1)
315
+ + "┬"
316
+ + "─" * (id_width + 1)
317
+ + "╮"
318
+ )
294
319
 
295
320
  # Table header - reordered to [User, Status, Attached To, Size, Studio ID]
296
321
  click.echo(
@@ -298,7 +323,19 @@ def list_studios(env: Optional[str]):
298
323
  )
299
324
 
300
325
  # Header separator
301
- click.echo("├" + "─" * (user_width + 1) + "┼" + "─" * (status_width + 1) + "┼" + "─" * (attached_width + 1) + "┼" + "─" * (size_width + 1) + "┼" + "─" * (id_width + 1) + "┤")
326
+ click.echo(
327
+ "├"
328
+ + "─" * (user_width + 1)
329
+ + "┼"
330
+ + "─" * (status_width + 1)
331
+ + "┼"
332
+ + "─" * (attached_width + 1)
333
+ + "┼"
334
+ + "─" * (size_width + 1)
335
+ + "┼"
336
+ + "─" * (id_width + 1)
337
+ + "┤"
338
+ )
302
339
 
303
340
  # Table rows
304
341
  for studio in studios:
@@ -310,7 +347,7 @@ def list_studios(env: Optional[str]):
310
347
 
311
348
  # Truncate if needed
312
349
  if len(user) > user_width - 1:
313
- user = user[:user_width - 1]
350
+ user = user[: user_width - 1]
314
351
 
315
352
  # Color the user (blue)
316
353
  user_display = f"\033[34m{user:<{user_width}}\033[0m"
@@ -318,7 +355,9 @@ def list_studios(env: Optional[str]):
318
355
  # Format status - display "in-use" as "attached" in purple
319
356
  if status == "in-use":
320
357
  display_status = "attached"
321
- status_display = f"\033[35m{display_status:<{status_width}}\033[0m" # Purple
358
+ status_display = (
359
+ f"\033[35m{display_status:<{status_width}}\033[0m" # Purple
360
+ )
322
361
  elif status == "available":
323
362
  status_display = f"\033[32m{status:<{status_width}}\033[0m" # Green
324
363
  elif status in ["attaching", "detaching"]:
@@ -326,9 +365,13 @@ def list_studios(env: Optional[str]):
326
365
  elif status == "attached":
327
366
  status_display = f"\033[35m{status:<{status_width}}\033[0m" # Purple
328
367
  elif status == "error":
329
- status_display = f"\033[31m{status:<{status_width}}\033[0m" # Red for error
368
+ status_display = (
369
+ f"\033[31m{status:<{status_width}}\033[0m" # Red for error
370
+ )
330
371
  else:
331
- status_display = f"{status:<{status_width}}" # No color for other states
372
+ status_display = (
373
+ f"{status:<{status_width}}" # No color for other states
374
+ )
332
375
 
333
376
  # Format Attached To column
334
377
  if attached_to:
@@ -347,7 +390,19 @@ def list_studios(env: Optional[str]):
347
390
  )
348
391
 
349
392
  # Table bottom border
350
- click.echo("╰" + "─" * (user_width + 1) + "┴" + "─" * (status_width + 1) + "┴" + "─" * (attached_width + 1) + "┴" + "─" * (size_width + 1) + "┴" + "─" * (id_width + 1) + "╯")
393
+ click.echo(
394
+ "╰"
395
+ + "─" * (user_width + 1)
396
+ + "┴"
397
+ + "─" * (status_width + 1)
398
+ + "┴"
399
+ + "─" * (attached_width + 1)
400
+ + "┴"
401
+ + "─" * (size_width + 1)
402
+ + "┴"
403
+ + "─" * (id_width + 1)
404
+ + "╯"
405
+ )
351
406
 
352
407
  click.echo(f"Total: {len(studios)}\n")
353
408
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dayhoff-tools
3
- Version: 1.13.7
3
+ Version: 1.13.9
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
@@ -11,20 +11,20 @@ dayhoff_tools/cli/engine1/engine_management.py,sha256=s_H3FtMlKsdfzR8pwV-j2W2QX-
11
11
  dayhoff_tools/cli/engine1/shared.py,sha256=Ecx6I1jtzmxQDn3BezKpgpQ4SJeZf4SZjUCLg-67p80,16844
12
12
  dayhoff_tools/cli/engine1/studio_commands.py,sha256=VwTQujz32-uMcYusDRE73SdzRpgvIkv7ZAF4zRv6AzA,30266
13
13
  dayhoff_tools/cli/engines_studios/__init__.py,sha256=E6aG0C6qjJnJuClemSKRFlYvLUL49MQZOvfqNQ7SDKs,159
14
- dayhoff_tools/cli/engines_studios/api_client.py,sha256=McZyyh5H36gkrK6s6Z7s9hl7yLLSLrHjsBtR4Opg6Ko,13491
14
+ dayhoff_tools/cli/engines_studios/api_client.py,sha256=7puTEpr3ukcguZr5aqRaSXLKM9Et799lnXwiywPvPz8,13459
15
15
  dayhoff_tools/cli/engines_studios/auth.py,sha256=rwetV5hp4jSvK8FyvKgXCnezLOZx1aW8oiSDc6U83iE,5189
16
16
  dayhoff_tools/cli/engines_studios/engine-studio-cli.md,sha256=or4k7ZZKPMTkvu67PdcUTE2_cxjnj0HQxxTuJZR1uiA,29924
17
- dayhoff_tools/cli/engines_studios/engine_commands.py,sha256=HZKWKgKcSADQ6giW5kq8UwYPY4nCrVofE3PNB0bFPi8,38249
18
- dayhoff_tools/cli/engines_studios/progress.py,sha256=SMahdG2YmO5bEPSONrfAXVTdS6m_69Ep02t3hc2DdKQ,9264
17
+ dayhoff_tools/cli/engines_studios/engine_commands.py,sha256=8utssuiLfxrff3Pk3PjMSUIVMU83mm9N8-Ny1uQr9es,38371
18
+ dayhoff_tools/cli/engines_studios/progress.py,sha256=VW70t7sEHlbLpCi6LOaY19Rp1Kx6Rt9CKNnmaZWFteA,9496
19
19
  dayhoff_tools/cli/engines_studios/simulators/cli-simulators.md,sha256=FZJl6nehdr2Duht2cx3yijcak0yKyOaHTrTzvFTAfZs,4976
20
20
  dayhoff_tools/cli/engines_studios/simulators/demo.sh,sha256=8tYABSCxLNXqGs-4r071V9mpKNZ5DTQ34WZ-v3d5s94,5364
21
21
  dayhoff_tools/cli/engines_studios/simulators/engine_list_simulator.py,sha256=vUrB6gV9UX74L5uCRMTVcPmk_1ZOuP1jYJf6cMP7dOE,9525
22
22
  dayhoff_tools/cli/engines_studios/simulators/engine_status_simulator.py,sha256=KUm3gA2MiRgGrQV7KURhb5zabM18-30z_ugRjiq5iso,13024
23
23
  dayhoff_tools/cli/engines_studios/simulators/idle_status_simulator.py,sha256=F_MfEXdPKNVDCKgJV72QyU2oMG8hLt-Bwic4yFadRXE,17570
24
24
  dayhoff_tools/cli/engines_studios/simulators/simulator_utils.py,sha256=HA08pIMJWV3OFrWj3Ca8GldvgJZfFoTOloyLK0UWMgA,6729
25
- dayhoff_tools/cli/engines_studios/simulators/studio_list_simulator.py,sha256=CHqxeTApdYneOngfGCAZiNxi9al_yAP1hNhVsQJ57V4,11197
25
+ dayhoff_tools/cli/engines_studios/simulators/studio_list_simulator.py,sha256=ntizeR0BJLdJOwCRBKPajc2xT-BL7SNnONxfgxXDgr8,11609
26
26
  dayhoff_tools/cli/engines_studios/simulators/studio_status_simulator.py,sha256=6WvpnRawJVaQf_H81zuR1_66igRRVxPxjAt8e69xjp4,5394
27
- dayhoff_tools/cli/engines_studios/studio_commands.py,sha256=0x0x2DZ7LWXQZOq0rKWdguVp2QQCrkSUjK43O6jnLOM,24391
27
+ dayhoff_tools/cli/engines_studios/studio_commands.py,sha256=ThA5LN4E15ADkKRxN2bnvm70FCgqtpZTa8mRCgU8Rx8,25443
28
28
  dayhoff_tools/cli/main.py,sha256=Nz_jtbppmvWKHZydQ0nkt_eejccJE90ces8xCGrerdY,7086
29
29
  dayhoff_tools/cli/swarm_commands.py,sha256=5EyKj8yietvT5lfoz8Zx0iQvVaNgc3SJX1z2zQR6o6M,5614
30
30
  dayhoff_tools/cli/utility_commands.py,sha256=e2P4dCCtoqMUGNyb0lFBZ6GZpl5Zslm1qqE5qIvsy38,50765
@@ -48,7 +48,7 @@ dayhoff_tools/intake/uniprot.py,sha256=BZYJQF63OtPcBBnQ7_P9gulxzJtqyorgyuDiPeOJq
48
48
  dayhoff_tools/logs.py,sha256=DKdeP0k0kliRcilwvX0mUB2eipO5BdWUeHwh-VnsICs,838
49
49
  dayhoff_tools/sqlite.py,sha256=jV55ikF8VpTfeQqqlHSbY8OgfyfHj8zgHNpZjBLos_E,18672
50
50
  dayhoff_tools/warehouse.py,sha256=UETBtZD3r7WgvURqfGbyHlT7cxoiVq8isjzMuerKw8I,24475
51
- dayhoff_tools-1.13.7.dist-info/METADATA,sha256=ylXgCRVZUklhlePHVBhWKq50uBdex_XfCTG4hXx8g7A,2980
52
- dayhoff_tools-1.13.7.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
53
- dayhoff_tools-1.13.7.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
54
- dayhoff_tools-1.13.7.dist-info/RECORD,,
51
+ dayhoff_tools-1.13.9.dist-info/METADATA,sha256=gKTsv3bywMV_JtQHzZAdQmgPo2ZRr-JHHR2oq7FzEe4,2980
52
+ dayhoff_tools-1.13.9.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
53
+ dayhoff_tools-1.13.9.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
54
+ dayhoff_tools-1.13.9.dist-info/RECORD,,