dayhoff-tools 1.1.10__py3-none-any.whl → 1.13.12__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.
Files changed (41) hide show
  1. dayhoff_tools/__init__.py +10 -0
  2. dayhoff_tools/cli/cloud_commands.py +179 -43
  3. dayhoff_tools/cli/engine1/__init__.py +323 -0
  4. dayhoff_tools/cli/engine1/engine_core.py +703 -0
  5. dayhoff_tools/cli/engine1/engine_lifecycle.py +136 -0
  6. dayhoff_tools/cli/engine1/engine_maintenance.py +431 -0
  7. dayhoff_tools/cli/engine1/engine_management.py +505 -0
  8. dayhoff_tools/cli/engine1/shared.py +501 -0
  9. dayhoff_tools/cli/engine1/studio_commands.py +825 -0
  10. dayhoff_tools/cli/engines_studios/__init__.py +6 -0
  11. dayhoff_tools/cli/engines_studios/api_client.py +351 -0
  12. dayhoff_tools/cli/engines_studios/auth.py +144 -0
  13. dayhoff_tools/cli/engines_studios/engine-studio-cli.md +1230 -0
  14. dayhoff_tools/cli/engines_studios/engine_commands.py +1151 -0
  15. dayhoff_tools/cli/engines_studios/progress.py +260 -0
  16. dayhoff_tools/cli/engines_studios/simulators/cli-simulators.md +151 -0
  17. dayhoff_tools/cli/engines_studios/simulators/demo.sh +75 -0
  18. dayhoff_tools/cli/engines_studios/simulators/engine_list_simulator.py +319 -0
  19. dayhoff_tools/cli/engines_studios/simulators/engine_status_simulator.py +369 -0
  20. dayhoff_tools/cli/engines_studios/simulators/idle_status_simulator.py +476 -0
  21. dayhoff_tools/cli/engines_studios/simulators/simulator_utils.py +180 -0
  22. dayhoff_tools/cli/engines_studios/simulators/studio_list_simulator.py +374 -0
  23. dayhoff_tools/cli/engines_studios/simulators/studio_status_simulator.py +164 -0
  24. dayhoff_tools/cli/engines_studios/studio_commands.py +755 -0
  25. dayhoff_tools/cli/main.py +106 -7
  26. dayhoff_tools/cli/utility_commands.py +896 -179
  27. dayhoff_tools/deployment/base.py +70 -6
  28. dayhoff_tools/deployment/deploy_aws.py +165 -25
  29. dayhoff_tools/deployment/deploy_gcp.py +78 -5
  30. dayhoff_tools/deployment/deploy_utils.py +20 -7
  31. dayhoff_tools/deployment/job_runner.py +9 -4
  32. dayhoff_tools/deployment/processors.py +230 -418
  33. dayhoff_tools/deployment/swarm.py +47 -12
  34. dayhoff_tools/embedders.py +28 -26
  35. dayhoff_tools/fasta.py +181 -64
  36. dayhoff_tools/warehouse.py +268 -1
  37. {dayhoff_tools-1.1.10.dist-info → dayhoff_tools-1.13.12.dist-info}/METADATA +20 -5
  38. dayhoff_tools-1.13.12.dist-info/RECORD +54 -0
  39. {dayhoff_tools-1.1.10.dist-info → dayhoff_tools-1.13.12.dist-info}/WHEEL +1 -1
  40. dayhoff_tools-1.1.10.dist-info/RECORD +0 -32
  41. {dayhoff_tools-1.1.10.dist-info → dayhoff_tools-1.13.12.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,319 @@
1
+ #!/usr/bin/env python3
2
+ """Simulator for engine list output - iterate on design locally without AWS.
3
+
4
+ This lets you quickly see how the list command output looks with different
5
+ engine states and configurations.
6
+
7
+ Usage:
8
+ python dayhoff_tools/cli/engines_studios/simulators/engine_list_simulator.py # Show all scenarios
9
+ python dayhoff_tools/cli/engines_studios/simulators/engine_list_simulator.py --scenario few # Show specific scenario
10
+ python dayhoff_tools/cli/engines_studios/simulators/engine_list_simulator.py --env prod # Simulate different environment
11
+ """
12
+
13
+ import argparse
14
+ import sys
15
+ from typing import Any
16
+
17
+
18
+ def colorize(text: str, color_code: str) -> str:
19
+ """Apply ANSI color code to text."""
20
+ return f"\033[{color_code}m{text}\033[0m"
21
+
22
+
23
+ def format_list_output(engines: list[dict[str, Any]], env: str = "dev") -> None:
24
+ """Format and print engine list output matching the actual CLI."""
25
+
26
+ # Header with blue account name
27
+ print(f"\nEngines for AWS Account {colorize(env, '34')}")
28
+
29
+ if not engines:
30
+ print("No engines found\n")
31
+ return
32
+
33
+ # Calculate dynamic width for Name column (longest name + 2 for padding)
34
+ max_name_len = max(
35
+ (len(engine.get("name", "unknown")) for engine in engines), default=4
36
+ )
37
+ name_width = max(max_name_len + 2, len("Name") + 2)
38
+
39
+ # Fixed widths for other columns
40
+ state_width = 12
41
+ user_width = 12
42
+ type_width = 12
43
+ id_width = 20
44
+
45
+ # Table top border
46
+ print(
47
+ "╭"
48
+ + "─" * (name_width + 1)
49
+ + "┬"
50
+ + "─" * (state_width + 1)
51
+ + "┬"
52
+ + "─" * (user_width + 1)
53
+ + "┬"
54
+ + "─" * (type_width + 1)
55
+ + "┬"
56
+ + "─" * (id_width + 1)
57
+ + "╮"
58
+ )
59
+
60
+ # Table header
61
+ print(
62
+ f"│ {'Name':<{name_width}}│ {'State':<{state_width}}│ {'User':<{user_width}}│ {'Type':<{type_width}}│ {'Instance ID':<{id_width}}│"
63
+ )
64
+
65
+ # Header separator
66
+ print(
67
+ "├"
68
+ + "─" * (name_width + 1)
69
+ + "┼"
70
+ + "─" * (state_width + 1)
71
+ + "┼"
72
+ + "─" * (user_width + 1)
73
+ + "┼"
74
+ + "─" * (type_width + 1)
75
+ + "┼"
76
+ + "─" * (id_width + 1)
77
+ + "┤"
78
+ )
79
+
80
+ # Table rows
81
+ for engine in engines:
82
+ name = engine.get("name", "unknown")
83
+ state = engine.get("state", "unknown")
84
+ user = engine.get("user", "unknown")
85
+ engine_type = engine.get("engine_type", "unknown")
86
+ instance_id = engine.get("instance_id", "unknown")
87
+
88
+ # Truncate if needed
89
+ if len(name) > name_width - 1:
90
+ name = name[: name_width - 1]
91
+ if len(user) > user_width - 1:
92
+ user = user[: user_width - 1]
93
+ if len(engine_type) > type_width - 1:
94
+ engine_type = engine_type[: type_width - 1]
95
+
96
+ # Color the name (blue)
97
+ name_display = colorize(f"{name:<{name_width}}", "34")
98
+
99
+ # Color the state
100
+ if state == "running":
101
+ state_display = colorize(f"{state:<{state_width}}", "32") # Green
102
+ elif state in ["starting", "stopping", "pending"]:
103
+ state_display = colorize(f"{state:<{state_width}}", "33") # Yellow
104
+ elif state == "stopped":
105
+ state_display = colorize(f"{state:<{state_width}}", "90") # Grey (dim)
106
+ else:
107
+ state_display = f"{state:<{state_width}}" # No color for other states
108
+
109
+ # Color the instance ID (grey)
110
+ instance_id_display = colorize(f"{instance_id:<{id_width}}", "90")
111
+
112
+ print(
113
+ f"│ {name_display}│ {state_display}│ {user:<{user_width}}│ {engine_type:<{type_width}}│ {instance_id_display}│"
114
+ )
115
+
116
+ # Table bottom border
117
+ print(
118
+ "╰"
119
+ + "─" * (name_width + 1)
120
+ + "┴"
121
+ + "─" * (state_width + 1)
122
+ + "┴"
123
+ + "─" * (user_width + 1)
124
+ + "┴"
125
+ + "─" * (type_width + 1)
126
+ + "┴"
127
+ + "─" * (id_width + 1)
128
+ + "╯"
129
+ )
130
+
131
+ print(f"Total: {len(engines)}\n")
132
+
133
+
134
+ def generate_scenarios() -> dict[str, dict[str, Any]]:
135
+ """Generate various test scenarios for list output."""
136
+
137
+ scenarios = {}
138
+
139
+ # Scenario 1: Single running engine
140
+ scenarios["single"] = {
141
+ "name": "Single Running Engine",
142
+ "engines": [
143
+ {
144
+ "name": "alice-gpu",
145
+ "state": "running",
146
+ "user": "alice",
147
+ "engine_type": "gpu",
148
+ "instance_id": "i-0123456789abcdef0",
149
+ }
150
+ ],
151
+ "env": "dev",
152
+ }
153
+
154
+ # Scenario 2: Few engines with various states
155
+ scenarios["few"] = {
156
+ "name": "Few Engines - Mixed States",
157
+ "engines": [
158
+ {
159
+ "name": "alice-gpu",
160
+ "state": "running",
161
+ "user": "alice",
162
+ "engine_type": "gpu",
163
+ "instance_id": "i-0123456789abcdef0",
164
+ },
165
+ {
166
+ "name": "bob-cpu",
167
+ "state": "stopped",
168
+ "user": "bob",
169
+ "engine_type": "cpu",
170
+ "instance_id": "i-1234567890abcdef1",
171
+ },
172
+ {
173
+ "name": "charlie",
174
+ "state": "starting",
175
+ "user": "charlie",
176
+ "engine_type": "cpu",
177
+ "instance_id": "i-2345678901abcdef2",
178
+ },
179
+ ],
180
+ "env": "sand",
181
+ }
182
+
183
+ # Scenario 3: Many engines (production-like)
184
+ scenarios["many"] = {
185
+ "name": "Many Engines - Production",
186
+ "engines": [
187
+ {
188
+ "name": "alice-main",
189
+ "state": "running",
190
+ "user": "alice",
191
+ "engine_type": "gpu",
192
+ "instance_id": "i-0123456789abcdef0",
193
+ },
194
+ {
195
+ "name": "bob-exp1",
196
+ "state": "running",
197
+ "user": "bob",
198
+ "engine_type": "cpu",
199
+ "instance_id": "i-1234567890abcdef1",
200
+ },
201
+ {
202
+ "name": "bob-exp2",
203
+ "state": "stopped",
204
+ "user": "bob",
205
+ "engine_type": "cpu",
206
+ "instance_id": "i-2345678901abcdef2",
207
+ },
208
+ {
209
+ "name": "charlie-gpu",
210
+ "state": "running",
211
+ "user": "charlie",
212
+ "engine_type": "gpu",
213
+ "instance_id": "i-3456789012abcdef3",
214
+ },
215
+ {
216
+ "name": "diana-dev",
217
+ "state": "running",
218
+ "user": "diana",
219
+ "engine_type": "cpu",
220
+ "instance_id": "i-4567890123abcdef4",
221
+ },
222
+ {
223
+ "name": "eve-test",
224
+ "state": "stopping",
225
+ "user": "eve",
226
+ "engine_type": "cpu",
227
+ "instance_id": "i-5678901234abcdef5",
228
+ },
229
+ {
230
+ "name": "frank-prod",
231
+ "state": "running",
232
+ "user": "frank",
233
+ "engine_type": "gpu",
234
+ "instance_id": "i-6789012345abcdef6",
235
+ },
236
+ ],
237
+ "env": "prod",
238
+ }
239
+
240
+ # Scenario 4: Empty list
241
+ scenarios["empty"] = {
242
+ "name": "No Engines",
243
+ "engines": [],
244
+ "env": "dev",
245
+ }
246
+
247
+ # Scenario 5: All transitional states
248
+ scenarios["transitions"] = {
249
+ "name": "Transitional States",
250
+ "engines": [
251
+ {
252
+ "name": "engine1",
253
+ "state": "starting",
254
+ "user": "alice",
255
+ "engine_type": "cpu",
256
+ "instance_id": "i-0123456789abcdef0",
257
+ },
258
+ {
259
+ "name": "engine2",
260
+ "state": "stopping",
261
+ "user": "bob",
262
+ "engine_type": "cpu",
263
+ "instance_id": "i-1234567890abcdef1",
264
+ },
265
+ {
266
+ "name": "engine3",
267
+ "state": "pending",
268
+ "user": "charlie",
269
+ "engine_type": "gpu",
270
+ "instance_id": "i-2345678901abcdef2",
271
+ },
272
+ ],
273
+ "env": "sand",
274
+ }
275
+
276
+ return scenarios
277
+
278
+
279
+ def main():
280
+ parser = argparse.ArgumentParser(
281
+ description="Simulate engine list output for design iteration"
282
+ )
283
+ parser.add_argument(
284
+ "--scenario",
285
+ choices=["single", "few", "many", "empty", "transitions", "all"],
286
+ default="all",
287
+ help="Which scenario to display (default: all)",
288
+ )
289
+ parser.add_argument(
290
+ "--env",
291
+ choices=["dev", "sand", "prod"],
292
+ help="Override environment for display",
293
+ )
294
+
295
+ args = parser.parse_args()
296
+
297
+ scenarios = generate_scenarios()
298
+
299
+ if args.scenario == "all":
300
+ # Show all scenarios
301
+ for _, scenario_data in scenarios.items():
302
+ print("\n" + "=" * 80)
303
+ print(f"SCENARIO: {scenario_data['name']}")
304
+ print("=" * 80 + "\n")
305
+
306
+ env = args.env if args.env else scenario_data["env"]
307
+ format_list_output(scenario_data["engines"], env)
308
+ print() # Extra newline between scenarios
309
+ else:
310
+ # Show specific scenario
311
+ scenario_data = scenarios[args.scenario]
312
+ print(f"\nSCENARIO: {scenario_data['name']}\n")
313
+
314
+ env = args.env if args.env else scenario_data["env"]
315
+ format_list_output(scenario_data["engines"], env)
316
+
317
+
318
+ if __name__ == "__main__":
319
+ main()
@@ -0,0 +1,369 @@
1
+ #!/usr/bin/env python3
2
+ """Simulator for engine status output - iterate on design locally without AWS.
3
+
4
+ This lets you quickly see how the status command output looks for different
5
+ engine states and configurations.
6
+
7
+ Usage:
8
+ python dayhoff_tools/cli/engines_studios/simulators/engine_status_simulator.py # Show all scenarios
9
+ python dayhoff_tools/cli/engines_studios/simulators/engine_status_simulator.py --scenario running # Show specific scenario
10
+ python dayhoff_tools/cli/engines_studios/simulators/engine_status_simulator.py --env sand # Override environment
11
+ """
12
+
13
+ import argparse
14
+ import sys
15
+ from datetime import datetime, timedelta, timezone
16
+ from pathlib import Path
17
+
18
+ # Import standalone utilities
19
+ simulator_dir = Path(__file__).parent
20
+ sys.path.insert(0, str(simulator_dir))
21
+ from simulator_utils import format_idle_state, format_time_ago
22
+
23
+
24
+ def colorize(text: str, color_code: str) -> str:
25
+ """Apply ANSI color code to text."""
26
+ return f"\033[{color_code}m{text}\033[0m"
27
+
28
+
29
+ def format_status_output(status_data: dict, env: str = "dev") -> None:
30
+ """Format and print engine status output matching the actual CLI."""
31
+
32
+ # Display basic info - matching engine_commands.py order
33
+ engine_name = status_data.get("name", status_data.get("instance_id", "unknown"))
34
+ print(f"Name: {colorize(engine_name, '34')}") # Blue
35
+
36
+ # Show state with color coding
37
+ engine_state = status_data.get("state", "unknown")
38
+ state_lower = engine_state.lower()
39
+ if state_lower == "running":
40
+ print(f"State: {colorize(engine_state, '32')}") # Green
41
+ elif state_lower in ["stopped", "terminated"]:
42
+ print(f"State: {colorize(engine_state, '31')}") # Red
43
+ elif state_lower in ["stopping", "starting", "pending"]:
44
+ print(f"State: {colorize(engine_state, '33')}") # Yellow
45
+ else:
46
+ print(f"State: {engine_state}")
47
+
48
+ # Show account
49
+ print(f"Account: {env}")
50
+
51
+ if status_data.get("launch_time"):
52
+ print(f"Launched: {format_time_ago(status_data['launch_time'])}")
53
+
54
+ print(f"Type: {status_data.get('instance_type', 'unknown')}")
55
+ print(f"Instance ID: {status_data.get('instance_id', 'unknown')}")
56
+
57
+ if status_data.get("public_ip"):
58
+ print(f"Public IP: {status_data['public_ip']}")
59
+
60
+ # Only show idle state and sensors for running engines
61
+ if state_lower != "running":
62
+ if state_lower in ["stopped", "terminated"]:
63
+ print(
64
+ f"\n⚠️ Engine is {engine_state.lower()} - idle detection not available"
65
+ )
66
+ return
67
+
68
+ # Display idle state
69
+ idle_state = status_data.get("idle_state", {})
70
+ if idle_state:
71
+ idle_display = format_idle_state(idle_state)
72
+ print(idle_display)
73
+
74
+ # Display attached studios
75
+ print(f"\nAttached Studios: {status_data.get('attached_studios', 'None')}")
76
+
77
+
78
+ def generate_scenarios() -> dict:
79
+ """Generate various test scenarios for status output."""
80
+
81
+ scenarios = {}
82
+
83
+ # Scenario 1: Running engine with idle state
84
+ scenarios["running_idle"] = {
85
+ "name": "Running Engine - Idle",
86
+ "status_data": {
87
+ "name": "alice-gpu",
88
+ "instance_id": "i-0123456789abcdef0",
89
+ "instance_type": "g4dn.xlarge",
90
+ "state": "running",
91
+ "public_ip": "54.123.45.67",
92
+ "launch_time": (
93
+ datetime.now(timezone.utc) - timedelta(hours=3)
94
+ ).isoformat(),
95
+ "idle_state": {
96
+ "is_idle": True,
97
+ "reason": "All sensors report idle",
98
+ "idle_seconds": 450,
99
+ "timeout_seconds": "1800",
100
+ "sensors": {
101
+ "coffee": {
102
+ "active": False,
103
+ "confidence": "HIGH",
104
+ "details": {},
105
+ },
106
+ "ssh": {
107
+ "active": False,
108
+ "confidence": "HIGH",
109
+ "details": {},
110
+ },
111
+ "ide": {
112
+ "active": False,
113
+ "confidence": "HIGH",
114
+ "details": {},
115
+ },
116
+ "docker": {
117
+ "active": False,
118
+ "confidence": "MEDIUM",
119
+ "details": {},
120
+ },
121
+ },
122
+ },
123
+ "attached_studios": "None",
124
+ },
125
+ "env": "dev",
126
+ }
127
+
128
+ # Scenario 2: Running engine with active SSH
129
+ scenarios["running_active"] = {
130
+ "name": "Running Engine - Active (SSH + IDE + Coffee)",
131
+ "status_data": {
132
+ "name": "bob-cpu",
133
+ "instance_id": "i-1234567890abcdef1",
134
+ "instance_type": "t3a.xlarge",
135
+ "state": "running",
136
+ "public_ip": "54.234.56.78",
137
+ "launch_time": (
138
+ datetime.now(timezone.utc) - timedelta(minutes=45)
139
+ ).isoformat(),
140
+ "idle_state": {
141
+ "is_idle": False,
142
+ "reason": "Active connections detected",
143
+ "idle_seconds": 0,
144
+ "timeout_seconds": "1800",
145
+ "sensors": {
146
+ "coffee": {
147
+ "active": True,
148
+ "confidence": "HIGH",
149
+ "details": {
150
+ "remaining_seconds": "13200", # 220 minutes
151
+ "timeout_seconds": "14400",
152
+ },
153
+ },
154
+ "ssh": {
155
+ "active": True,
156
+ "confidence": "HIGH",
157
+ "details": {
158
+ "session_count": 2,
159
+ "sessions": [
160
+ {
161
+ "user": "bob",
162
+ "tty": "pts/0",
163
+ "from": "127.0.0.1",
164
+ "login": "2025-11-18 09:30",
165
+ "idle": "00:05",
166
+ "pid": "12345",
167
+ },
168
+ {
169
+ "user": "bob",
170
+ "tty": "pts/1",
171
+ "from": "127.0.0.1",
172
+ "login": "2025-11-18 09:45",
173
+ "idle": "00:02",
174
+ "pid": "12567",
175
+ },
176
+ ],
177
+ },
178
+ },
179
+ "ide": {
180
+ "active": True,
181
+ "confidence": "HIGH",
182
+ "details": {
183
+ "connection_count": 1,
184
+ "ide_type": "Cursor",
185
+ "connections": [
186
+ {
187
+ "local": "127.0.0.1:32777",
188
+ "remote": "127.0.0.1:33616",
189
+ "process": "cursor-ba90f2f8",
190
+ "pid": "24967",
191
+ }
192
+ ],
193
+ },
194
+ },
195
+ "docker": {
196
+ "active": False,
197
+ "confidence": "MEDIUM",
198
+ "details": {},
199
+ },
200
+ },
201
+ },
202
+ "attached_studios": "vol-047e03b0647fa9d87",
203
+ },
204
+ "env": "sand",
205
+ }
206
+
207
+ # Scenario 3: Stopped engine
208
+ scenarios["stopped"] = {
209
+ "name": "Stopped Engine",
210
+ "status_data": {
211
+ "name": "charlie-test",
212
+ "instance_id": "i-2345678901abcdef2",
213
+ "instance_type": "t3a.medium",
214
+ "state": "stopped",
215
+ "launch_time": (datetime.now(timezone.utc) - timedelta(days=2)).isoformat(),
216
+ "attached_studios": "None",
217
+ },
218
+ "env": "dev",
219
+ }
220
+
221
+ # Scenario 4: Starting engine
222
+ scenarios["starting"] = {
223
+ "name": "Starting Engine",
224
+ "status_data": {
225
+ "name": "diana-gpu",
226
+ "instance_id": "i-3456789012abcdef3",
227
+ "instance_type": "g5.xlarge",
228
+ "state": "starting",
229
+ "launch_time": (
230
+ datetime.now(timezone.utc) - timedelta(minutes=2)
231
+ ).isoformat(),
232
+ "attached_studios": "None",
233
+ },
234
+ "env": "prod",
235
+ }
236
+
237
+ # Scenario 5: Stopping engine
238
+ scenarios["stopping"] = {
239
+ "name": "Stopping Engine",
240
+ "status_data": {
241
+ "name": "eve-dev",
242
+ "instance_id": "i-4567890123abcdef4",
243
+ "instance_type": "t3a.large",
244
+ "state": "stopping",
245
+ "public_ip": "54.123.45.89",
246
+ "launch_time": (
247
+ datetime.now(timezone.utc) - timedelta(hours=8)
248
+ ).isoformat(),
249
+ "attached_studios": "None",
250
+ },
251
+ "env": "sand",
252
+ }
253
+
254
+ # Scenario 6: Running with Docker activity
255
+ scenarios["running_docker"] = {
256
+ "name": "Running Engine - Docker Active",
257
+ "status_data": {
258
+ "name": "frank-ml",
259
+ "instance_id": "i-5678901234abcdef5",
260
+ "instance_type": "g4dn.2xlarge",
261
+ "state": "running",
262
+ "public_ip": "54.234.67.90",
263
+ "launch_time": (
264
+ datetime.now(timezone.utc) - timedelta(hours=12)
265
+ ).isoformat(),
266
+ "idle_state": {
267
+ "is_idle": False,
268
+ "reason": "Docker containers running",
269
+ "idle_seconds": 0,
270
+ "timeout_seconds": "1800",
271
+ "sensors": {
272
+ "coffee": {
273
+ "active": False,
274
+ "confidence": "HIGH",
275
+ "details": {},
276
+ },
277
+ "ssh": {
278
+ "active": False,
279
+ "confidence": "HIGH",
280
+ "details": {},
281
+ },
282
+ "ide": {
283
+ "active": False,
284
+ "confidence": "HIGH",
285
+ "details": {},
286
+ },
287
+ "docker": {
288
+ "active": True,
289
+ "confidence": "HIGH",
290
+ "details": {
291
+ "container_count": 3,
292
+ "containers": [
293
+ {
294
+ "name": "training-job-1",
295
+ "status": "running",
296
+ "uptime": "3 hours",
297
+ },
298
+ {
299
+ "name": "redis-cache",
300
+ "status": "running",
301
+ "uptime": "12 hours",
302
+ },
303
+ {
304
+ "name": "monitoring",
305
+ "status": "running",
306
+ "uptime": "12 hours",
307
+ },
308
+ ],
309
+ },
310
+ },
311
+ },
312
+ },
313
+ "attached_studios": "vol-01234567890abcdef",
314
+ },
315
+ "env": "prod",
316
+ }
317
+
318
+ return scenarios
319
+
320
+
321
+ def main():
322
+ parser = argparse.ArgumentParser(
323
+ description="Simulate engine status output for design iteration"
324
+ )
325
+ parser.add_argument(
326
+ "--scenario",
327
+ choices=[
328
+ "running_idle",
329
+ "running_active",
330
+ "stopped",
331
+ "starting",
332
+ "stopping",
333
+ "running_docker",
334
+ "all",
335
+ ],
336
+ default="all",
337
+ help="Which scenario to display (default: all)",
338
+ )
339
+ parser.add_argument(
340
+ "--env",
341
+ choices=["dev", "sand", "prod"],
342
+ help="Override environment for display",
343
+ )
344
+
345
+ args = parser.parse_args()
346
+
347
+ scenarios = generate_scenarios()
348
+
349
+ if args.scenario == "all":
350
+ # Show all scenarios
351
+ for scenario_key, scenario_data in scenarios.items():
352
+ print("\n" + "=" * 80)
353
+ print(f"SCENARIO: {scenario_data['name']}")
354
+ print("=" * 80 + "\n")
355
+
356
+ env = args.env if args.env else scenario_data["env"]
357
+ format_status_output(scenario_data["status_data"], env)
358
+ print() # Extra newline between scenarios
359
+ else:
360
+ # Show specific scenario
361
+ scenario_data = scenarios[args.scenario]
362
+ print(f"\nSCENARIO: {scenario_data['name']}\n")
363
+
364
+ env = args.env if args.env else scenario_data["env"]
365
+ format_status_output(scenario_data["status_data"], env)
366
+
367
+
368
+ if __name__ == "__main__":
369
+ main()