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.
- dayhoff_tools/__init__.py +10 -0
- dayhoff_tools/cli/cloud_commands.py +179 -43
- dayhoff_tools/cli/engine1/__init__.py +323 -0
- dayhoff_tools/cli/engine1/engine_core.py +703 -0
- dayhoff_tools/cli/engine1/engine_lifecycle.py +136 -0
- dayhoff_tools/cli/engine1/engine_maintenance.py +431 -0
- dayhoff_tools/cli/engine1/engine_management.py +505 -0
- dayhoff_tools/cli/engine1/shared.py +501 -0
- dayhoff_tools/cli/engine1/studio_commands.py +825 -0
- dayhoff_tools/cli/engines_studios/__init__.py +6 -0
- dayhoff_tools/cli/engines_studios/api_client.py +351 -0
- dayhoff_tools/cli/engines_studios/auth.py +144 -0
- dayhoff_tools/cli/engines_studios/engine-studio-cli.md +1230 -0
- dayhoff_tools/cli/engines_studios/engine_commands.py +1151 -0
- dayhoff_tools/cli/engines_studios/progress.py +260 -0
- dayhoff_tools/cli/engines_studios/simulators/cli-simulators.md +151 -0
- dayhoff_tools/cli/engines_studios/simulators/demo.sh +75 -0
- dayhoff_tools/cli/engines_studios/simulators/engine_list_simulator.py +319 -0
- dayhoff_tools/cli/engines_studios/simulators/engine_status_simulator.py +369 -0
- dayhoff_tools/cli/engines_studios/simulators/idle_status_simulator.py +476 -0
- dayhoff_tools/cli/engines_studios/simulators/simulator_utils.py +180 -0
- dayhoff_tools/cli/engines_studios/simulators/studio_list_simulator.py +374 -0
- dayhoff_tools/cli/engines_studios/simulators/studio_status_simulator.py +164 -0
- dayhoff_tools/cli/engines_studios/studio_commands.py +755 -0
- dayhoff_tools/cli/main.py +106 -7
- dayhoff_tools/cli/utility_commands.py +896 -179
- dayhoff_tools/deployment/base.py +70 -6
- dayhoff_tools/deployment/deploy_aws.py +165 -25
- dayhoff_tools/deployment/deploy_gcp.py +78 -5
- dayhoff_tools/deployment/deploy_utils.py +20 -7
- dayhoff_tools/deployment/job_runner.py +9 -4
- dayhoff_tools/deployment/processors.py +230 -418
- dayhoff_tools/deployment/swarm.py +47 -12
- dayhoff_tools/embedders.py +28 -26
- dayhoff_tools/fasta.py +181 -64
- dayhoff_tools/warehouse.py +268 -1
- {dayhoff_tools-1.1.10.dist-info → dayhoff_tools-1.13.12.dist-info}/METADATA +20 -5
- dayhoff_tools-1.13.12.dist-info/RECORD +54 -0
- {dayhoff_tools-1.1.10.dist-info → dayhoff_tools-1.13.12.dist-info}/WHEEL +1 -1
- dayhoff_tools-1.1.10.dist-info/RECORD +0 -32
- {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()
|