hte-cli 0.2.24__py3-none-any.whl → 0.2.26__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.
hte_cli/cli.py CHANGED
@@ -172,6 +172,18 @@ def session_join(ctx, session_id: str, force_setup: bool):
172
172
  console.print("[red]Not logged in. Run: hte-cli auth login[/red]")
173
173
  sys.exit(1)
174
174
 
175
+ # Check Docker is running before we start (with retry prompt)
176
+ while True:
177
+ docker_ok, docker_error = _check_docker()
178
+ if docker_ok:
179
+ console.print("[dim]✓ Docker running[/dim]")
180
+ break
181
+ console.print(f"[red]{docker_error}[/red]")
182
+ console.print()
183
+ if not click.confirm("Start Docker and retry?", default=True):
184
+ sys.exit(1)
185
+ console.print("[dim]Checking Docker again...[/dim]")
186
+
175
187
  api = APIClient(config)
176
188
 
177
189
  # Step 1: Join session
@@ -201,8 +213,11 @@ def session_join(ctx, session_id: str, force_setup: bool):
201
213
  # Check if reconnecting (session already in_progress)
202
214
  is_reconnect = session_info.get("status") == "in_progress"
203
215
 
204
- if is_reconnect and not force_setup:
205
- console.print("[yellow]Reconnecting to existing session...[/yellow]")
216
+ # Always run setup on reconnect - previous attempt may have failed
217
+ # (e.g., image pull failed, Docker wasn't running, etc.)
218
+ if is_reconnect:
219
+ force_setup = True
220
+ console.print("[yellow]Reconnecting to existing session (re-running setup)...[/yellow]")
206
221
  console.print()
207
222
 
208
223
  console.print(
@@ -219,7 +234,11 @@ def session_join(ctx, session_id: str, force_setup: bool):
219
234
  import time
220
235
  from hte_cli.events import EventStreamer
221
236
  from hte_cli.runner import TaskRunner
222
- from hte_cli.image_utils import extract_images_from_compose, pull_image_with_progress
237
+ from hte_cli.image_utils import (
238
+ extract_images_from_compose,
239
+ extract_image_platforms_from_compose,
240
+ pull_image_with_progress,
241
+ )
223
242
 
224
243
  # Create event streamer
225
244
  events = EventStreamer(api, session_id)
@@ -285,9 +304,11 @@ def session_join(ctx, session_id: str, force_setup: bool):
285
304
  failed_images = []
286
305
 
287
306
  if not is_reconnect or force_setup:
288
- # Extract images from compose
307
+ # Extract images and their platforms from compose
308
+ image_platforms = {}
289
309
  if compose_yaml:
290
310
  images = extract_images_from_compose(compose_yaml)
311
+ image_platforms = extract_image_platforms_from_compose(compose_yaml)
291
312
 
292
313
  # Send setup_started event (includes CLI version for debugging)
293
314
  events.setup_started(images=images, cli_version=__version__)
@@ -298,9 +319,11 @@ def session_join(ctx, session_id: str, force_setup: bool):
298
319
 
299
320
  console.print(f"[bold]Step 2:[/bold] Pulling {len(images)} Docker image(s)...")
300
321
  pull_start = time.monotonic()
322
+ pull_errors = {}
301
323
 
302
324
  for img in images:
303
325
  short_name = img.split("/")[-1][:40]
326
+ platform = image_platforms.get(img)
304
327
 
305
328
  # Check if already cached
306
329
  if check_image_exists_locally(img):
@@ -310,6 +333,7 @@ def session_join(ctx, session_id: str, force_setup: bool):
310
333
 
311
334
  # Need to pull - show progress
312
335
  last_status = ["connecting..."]
336
+ last_error = [""]
313
337
  with console.status(
314
338
  f"[yellow]↓[/yellow] {short_name} [dim]connecting...[/dim]"
315
339
  ) as status:
@@ -328,14 +352,23 @@ def session_join(ctx, session_id: str, force_setup: bool):
328
352
  status.update(
329
353
  f"[yellow]↓[/yellow] {short_name} [dim]{display}[/dim]"
330
354
  )
355
+ # Capture error messages
356
+ if "error" in line.lower() or "denied" in line.lower():
357
+ last_error[0] = line
331
358
 
332
- success = pull_image_with_progress(img, on_progress=show_progress)
359
+ success = pull_image_with_progress(
360
+ img, platform=platform, on_progress=show_progress
361
+ )
333
362
 
334
363
  if success:
335
364
  console.print(f" [green]✓[/green] {short_name} [dim](downloaded)[/dim]")
336
365
  pulled_images.append(img)
337
366
  else:
338
- console.print(f" [red]✗[/red] {short_name} [dim](failed)[/dim]")
367
+ platform_note = f" (platform: {platform})" if platform else ""
368
+ console.print(f" [red]✗[/red] {short_name}{platform_note} [dim](failed)[/dim]")
369
+ if last_error[0]:
370
+ console.print(f" [dim]{last_error[0][:60]}[/dim]")
371
+ pull_errors[img] = last_error[0]
339
372
  failed_images.append(img)
340
373
 
341
374
  pull_duration = time.monotonic() - pull_start
@@ -347,6 +380,20 @@ def session_join(ctx, session_id: str, force_setup: bool):
347
380
  )
348
381
  console.print()
349
382
 
383
+ # Fail fast if any required image couldn't be pulled
384
+ if failed_images:
385
+ console.print(
386
+ f"[red]Error: Failed to pull {len(failed_images)} required Docker image(s).[/red]"
387
+ )
388
+ console.print()
389
+ console.print("[yellow]Troubleshooting:[/yellow]")
390
+ console.print(" 1. Check Docker is running: docker info")
391
+ console.print(" 2. Try manual pull: docker pull python:3.12-slim --platform linux/amd64")
392
+ console.print(" 3. Check network connectivity")
393
+ console.print()
394
+ console.print("Session remains active - you can retry with: hte-cli session join " + session_id)
395
+ sys.exit(1)
396
+
350
397
  # Send setup_completed - THIS STARTS THE TIMER ON SERVER
351
398
  total_setup = time.monotonic() - setup_start_time
352
399
  events.setup_completed(total_seconds=total_setup)
@@ -644,7 +691,7 @@ def _check_docker() -> tuple[bool, str | None]:
644
691
  timeout=10,
645
692
  )
646
693
  if result.returncode != 0:
647
- return False, "Docker is not running. Start Docker Desktop or the Docker daemon."
694
+ return False, "Docker is not running. Start Docker (Docker Desktop, colima, or dockerd)."
648
695
  except FileNotFoundError:
649
696
  return False, "Docker is not installed. Install from https://docs.docker.com/get-docker/"
650
697
  except Exception as e:
hte_cli/image_utils.py CHANGED
@@ -38,6 +38,33 @@ def extract_images_from_compose(compose_yaml: str) -> list[str]:
38
38
  return []
39
39
 
40
40
 
41
+ def extract_image_platforms_from_compose(compose_yaml: str) -> dict[str, str | None]:
42
+ """
43
+ Extract Docker image names and their platforms from a compose.yaml string.
44
+
45
+ Args:
46
+ compose_yaml: Docker Compose YAML content
47
+
48
+ Returns:
49
+ Dict mapping image names to their platform (or None if no platform specified)
50
+ """
51
+ try:
52
+ compose_data = yaml.safe_load(compose_yaml)
53
+ if not compose_data or "services" not in compose_data:
54
+ return {}
55
+
56
+ image_platforms = {}
57
+ for service_name, service_config in compose_data.get("services", {}).items():
58
+ if isinstance(service_config, dict) and "image" in service_config:
59
+ image = service_config["image"]
60
+ platform = service_config.get("platform")
61
+ image_platforms[image] = platform
62
+ return image_platforms
63
+ except yaml.YAMLError as e:
64
+ logger.warning(f"Failed to parse compose.yaml: {e}")
65
+ return {}
66
+
67
+
41
68
  def check_image_exists_locally(image: str) -> bool:
42
69
  """
43
70
  Check if a Docker image exists locally.
@@ -61,16 +88,20 @@ def check_image_exists_locally(image: str) -> bool:
61
88
 
62
89
  def pull_image_with_progress(
63
90
  image: str,
91
+ platform: str | None = None,
64
92
  on_progress: Callable[[str, str], None] | None = None,
65
93
  on_complete: Callable[[str, bool], None] | None = None,
94
+ on_error: Callable[[str, str], None] | None = None,
66
95
  ) -> bool:
67
96
  """
68
97
  Pull a Docker image with progress callbacks using PTY for real progress output.
69
98
 
70
99
  Args:
71
100
  image: Image name to pull
101
+ platform: Optional platform to pull (e.g., "linux/amd64")
72
102
  on_progress: Callback(image, status_line) called for each progress update
73
103
  on_complete: Callback(image, success) called when pull completes
104
+ on_error: Callback(image, error_message) called when pull fails
74
105
 
75
106
  Returns:
76
107
  True if pull succeeded, False otherwise
@@ -79,8 +110,12 @@ def pull_image_with_progress(
79
110
  # Use PTY to get real progress output from docker
80
111
  master_fd, slave_fd = pty.openpty()
81
112
 
113
+ cmd = ["docker", "pull", image]
114
+ if platform:
115
+ cmd.extend(["--platform", platform])
116
+
82
117
  process = subprocess.Popen(
83
- ["docker", "pull", image],
118
+ cmd,
84
119
  stdout=slave_fd,
85
120
  stderr=slave_fd,
86
121
  stdin=slave_fd,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hte-cli
3
- Version: 0.2.24
3
+ Version: 0.2.26
4
4
  Summary: Human Time-to-Completion Evaluation CLI
5
5
  Project-URL: Homepage, https://github.com/sean-peters-au/lyptus-mono
6
6
  Author: Lyptus Research
@@ -1,15 +1,15 @@
1
1
  hte_cli/__init__.py,sha256=fDGXp-r8bIoLtlQnn5xJ_CpwMhonvk9bGjZQsjA2mDI,914
2
2
  hte_cli/__main__.py,sha256=63n0gNGfskidWDU0aAIF2N8lylVCLYKVIkrN9QiORoo,107
3
3
  hte_cli/api_client.py,sha256=m42kfFZS72Nu_VuDwxRsLNy4ziCcvgk7KNWBh9gwqy0,9257
4
- hte_cli/cli.py,sha256=ZZSEbvFKJBeWPwlfABdXMvAcz5kfsywH033leFFkO7M,24184
4
+ hte_cli/cli.py,sha256=ywYfUmxuPt693c24vhK5bwL9KCe3IkR_lcca6cQHa9E,26413
5
5
  hte_cli/config.py,sha256=42Xv__YMSeRLs2zhGukJkIXFKtnBtYCHnONfViGyt2g,3387
6
6
  hte_cli/errors.py,sha256=1J5PpxcUKBu6XjigMMCPOq4Zc12tnv8LhAsiaVFWLQM,2762
7
7
  hte_cli/events.py,sha256=oDKCS-a0IZ7bz7xkwQj5eM4DoDCYvnclAGohrMTWf8s,5644
8
- hte_cli/image_utils.py,sha256=TLwJdswUQrSD2bQcAXW03R8j8WG2pbHzd12TWcE7zy4,6418
8
+ hte_cli/image_utils.py,sha256=nVHhUY-QZ4uPpGSx3ByOiVGOnm9T11p_cVlb39FQb_Y,7717
9
9
  hte_cli/runner.py,sha256=SWl9FF4X3e9eBbZyL0ujhmmSL5OK8J6st-Ty0jD5AWM,14550
10
10
  hte_cli/scorers.py,sha256=B0ZjQ3Fh-VDkc_8CDc86yW7vpdimbV3RSqs7l-VeUIg,6629
11
11
  hte_cli/version_check.py,sha256=WVZyGy2XfAghQYdd2N9-0Qfg-7pgp9gt4761-PnmacI,1708
12
- hte_cli-0.2.24.dist-info/METADATA,sha256=tnFgGGfZ15wjb6fz_Bgzuo9ApfgjRpwv6HSxVIVu7Os,3820
13
- hte_cli-0.2.24.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
- hte_cli-0.2.24.dist-info/entry_points.txt,sha256=XbyEEi1H14DFAt0Kdl22e_IRVEGzimSzYSh5HlhKlFA,41
15
- hte_cli-0.2.24.dist-info/RECORD,,
12
+ hte_cli-0.2.26.dist-info/METADATA,sha256=8KvyBSr89IAjdcx9UMs2Obl2HMoMJFjeS2r5aH7XgLM,3820
13
+ hte_cli-0.2.26.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ hte_cli-0.2.26.dist-info/entry_points.txt,sha256=XbyEEi1H14DFAt0Kdl22e_IRVEGzimSzYSh5HlhKlFA,41
15
+ hte_cli-0.2.26.dist-info/RECORD,,