wafer-cli 0.2.15__py3-none-any.whl → 0.2.17__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.
wafer/auth.py CHANGED
@@ -191,6 +191,10 @@ def get_valid_token() -> str | None:
191
191
  if e.response.status_code != 401:
192
192
  # Not an auth error, re-raise
193
193
  raise
194
+ except httpx.RequestError:
195
+ # Network error (timeout, connection refused, DNS failure, etc.)
196
+ # Cannot verify token - return None to trigger re-login prompt
197
+ return None
194
198
 
195
199
  # Token expired, try refresh
196
200
  if not creds.refresh_token:
@@ -203,6 +207,9 @@ def get_valid_token() -> str | None:
203
207
  except httpx.HTTPStatusError:
204
208
  # Refresh failed, need to re-login
205
209
  return None
210
+ except httpx.RequestError:
211
+ # Network error during refresh - return None to trigger re-login prompt
212
+ return None
206
213
 
207
214
 
208
215
  def _find_free_port() -> int:
wafer/evaluate.py CHANGED
@@ -354,7 +354,8 @@ def _build_docker_pip_install_cmd(target: BaremetalTarget | VMTarget) -> str:
354
354
  )
355
355
 
356
356
  # Install uv (fast, reliable) - use pip3 for compatibility
357
- commands.append("pip3 install uv")
357
+ # Use --break-system-packages for Python 3.12+ with PEP 668 externally managed environments
358
+ commands.append("pip3 install --break-system-packages uv")
358
359
 
359
360
  # Install torch with custom index if specified (like Modal's two-phase install)
360
361
  # Use --system --break-system-packages to install to container's Python
wafer/workspaces.py CHANGED
@@ -13,7 +13,7 @@ import httpx
13
13
  from .api_client import get_api_url
14
14
  from .auth import get_auth_headers
15
15
 
16
- VALID_STATUSES = {"creating", "running"}
16
+ VALID_STATUSES = {"creating", "running", "error"}
17
17
 
18
18
 
19
19
  def _get_client() -> tuple[str, dict[str, str]]:
@@ -211,17 +211,40 @@ def list_workspaces(json_output: bool = False) -> str:
211
211
  lines = ["Workspaces:", ""]
212
212
  for ws in workspaces:
213
213
  status = ws.get("status", "unknown")
214
- status_icon = {"running": "●", "creating": "◐"}.get(status, "?")
214
+ status_icon = {"running": "●", "creating": "◐", "error": "✗"}.get(status, "?")
215
215
  lines.append(f" {status_icon} {ws['name']} ({ws['id']})")
216
216
  lines.append(f" GPU: {ws.get('gpu_type', 'N/A')} | Image: {ws.get('image', 'N/A')}")
217
- if ws.get("ssh_host") and ws.get("ssh_port") and ws.get("ssh_user"):
217
+
218
+ # Show appropriate guidance based on status
219
+ if status == "error":
220
+ lines.append(
221
+ f" Status: Provisioning failed. Delete and recreate: wafer workspaces delete {ws['name']}"
222
+ )
223
+ elif ws.get("ssh_host") and ws.get("ssh_port") and ws.get("ssh_user"):
218
224
  lines.append(
219
225
  f" SSH: ssh -p {ws['ssh_port']} {ws['ssh_user']}@{ws['ssh_host']}"
220
226
  )
227
+ elif status == "running":
228
+ # Running but missing SSH credentials - unusual state
229
+ lines.append(
230
+ f" Status: Running but SSH not ready. Try: wafer workspaces delete {ws['name']} && wafer workspaces create {ws['name']} --wait"
231
+ )
221
232
  else:
222
- lines.append(" SSH: Not ready (run: wafer workspaces ssh <name>)")
233
+ lines.append(" SSH: Not ready (workspace is still creating)")
223
234
  lines.append("")
224
235
 
236
+ # Add SSH tip for users with running workspaces
237
+ has_running_with_ssh = any(
238
+ ws.get("status") == "running" and ws.get("ssh_host")
239
+ for ws in workspaces
240
+ )
241
+ if has_running_with_ssh:
242
+ lines.append("Tip: SSH directly for interactive work. 'exec' is for quick commands only.")
243
+
244
+ has_error = any(ws.get("status") == "error" for ws in workspaces)
245
+ if has_error:
246
+ lines.append("Note: Error workspaces are auto-cleaned after 12 hours.")
247
+
225
248
  return "\n".join(lines)
226
249
 
227
250
 
@@ -441,6 +464,12 @@ def sync_files(
441
464
  f"Workspace {workspace_id} has invalid status '{workspace_status}'. "
442
465
  f"Valid statuses: {VALID_STATUSES}"
443
466
  )
467
+ if workspace_status == "error":
468
+ raise RuntimeError(
469
+ f"Workspace provisioning failed. Delete and recreate:\n"
470
+ f" wafer workspaces delete {workspace_id}\n"
471
+ f" wafer workspaces create {ws.get('name', workspace_id)} --wait"
472
+ )
444
473
  if workspace_status != "running":
445
474
  raise RuntimeError(
446
475
  f"Workspace is {workspace_status}. Wait for it to be running before syncing."
@@ -448,9 +477,14 @@ def sync_files(
448
477
  ssh_host = ws.get("ssh_host")
449
478
  ssh_port = ws.get("ssh_port")
450
479
  ssh_user = ws.get("ssh_user")
451
- assert ssh_host, "Workspace missing ssh_host"
480
+ if not ssh_host or not ssh_port or not ssh_user:
481
+ # Workspace is running but SSH credentials are missing - unusual state
482
+ raise RuntimeError(
483
+ f"Workspace is running but SSH not ready.\n"
484
+ f" Delete and recreate: wafer workspaces delete {workspace_id}\n"
485
+ f" Then: wafer workspaces create {ws.get('name', workspace_id)} --wait"
486
+ )
452
487
  assert isinstance(ssh_port, int) and ssh_port > 0, "Workspace missing valid ssh_port"
453
- assert ssh_user, "Workspace missing ssh_user"
454
488
 
455
489
  # Build rsync command
456
490
  # -a: archive mode (preserves permissions, etc.)
@@ -607,16 +641,34 @@ def get_workspace(workspace_id: str, json_output: bool = False) -> str:
607
641
  f" Last Used: {workspace.get('last_used_at', 'N/A')}",
608
642
  ]
609
643
 
610
- if workspace.get("ssh_host"):
644
+ if status == "error":
645
+ lines.extend([
646
+ "",
647
+ "Provisioning failed. Delete and recreate:",
648
+ f" wafer workspaces delete {workspace['name']}",
649
+ f" wafer workspaces create {workspace['name']} --wait",
650
+ "",
651
+ "Note: Error workspaces are auto-cleaned after 12 hours.",
652
+ ])
653
+ elif workspace.get("ssh_host"):
611
654
  lines.extend([
612
655
  "",
613
656
  "SSH Info:",
614
657
  f" Host: {workspace['ssh_host']}",
615
658
  f" Port: {workspace.get('ssh_port', 22)}",
616
659
  f" User: {workspace.get('ssh_user', 'root')}",
660
+ "",
661
+ "Tip: SSH directly for interactive work. 'exec' is for quick commands only.",
617
662
  ])
618
663
  elif status == "creating":
619
664
  lines.extend(["", "SSH: available once workspace is running"])
665
+ elif status == "running":
666
+ # Running but no SSH credentials - unusual state
667
+ lines.extend([
668
+ "",
669
+ "Status: Running but SSH not ready.",
670
+ f" Delete and recreate: wafer workspaces delete {workspace['name']}",
671
+ ])
620
672
 
621
673
  return "\n".join(lines)
622
674
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wafer-cli
3
- Version: 0.2.15
3
+ Version: 0.2.17
4
4
  Summary: CLI tool for running commands on remote GPUs and GPU kernel optimization agent
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: typer>=0.12.0
@@ -2,13 +2,13 @@ wafer/GUIDE.md,sha256=G6P4aFZslEXiHmVjtTB3_OIpGK5d1tSiqxtawASVUZg,3588
2
2
  wafer/__init__.py,sha256=kBM_ONCpU6UUMBOH8Tmg4A88sNFnbaD59o61cJs-uYM,90
3
3
  wafer/analytics.py,sha256=qLY6Z16usVHFD8TCv7XBuz7l47vXVdXk-qhOzA-hW_8,8179
4
4
  wafer/api_client.py,sha256=i_Az2b2llC3DSW8yOL-BKqa7LSKuxOr8hSN40s-oQXY,6313
5
- wafer/auth.py,sha256=nneKUjGwb5ggJEHRdF_GlFkT1ZozHP4kGyuXjhZjtgM,13677
5
+ wafer/auth.py,sha256=dwss_se5P-FFc9IN38q4kh_dBrA6k-CguDBkivgcdj0,14003
6
6
  wafer/autotuner.py,sha256=41WYP41pTDvMijv2h42vm89bcHtDMJXObDlWmn6xpFU,44416
7
7
  wafer/billing.py,sha256=jbLB2lI4_9f2KD8uEFDi_ixLlowe5hasC0TIZJyIXRg,7163
8
8
  wafer/cli.py,sha256=lBBTQCcmKREqZDOQh27qSq8i6NedjHW5oh1JiuT9aho,254241
9
9
  wafer/config.py,sha256=h5Eo9_yfWqWGoPNdVQikI9GoZVUeysunSYiixf1mKcw,3411
10
10
  wafer/corpus.py,sha256=x5aFhCsTSAtgzFG9AMFpqq92Ej63mXofL-vvvpjj1sM,12913
11
- wafer/evaluate.py,sha256=bLTfL7jAGQlfqLL39hSGSB7bnBp5THTCY7nl6giVMkQ,176005
11
+ wafer/evaluate.py,sha256=s1NszUBtxdWRonbi8YR3XWfCiCjNm14g2Pp1lu4kmtY,176125
12
12
  wafer/global_config.py,sha256=fhaR_RU3ufMksDmOohH1OLeQ0JT0SDW1hEip_zaP75k,11345
13
13
  wafer/gpu_run.py,sha256=TwqXy72T7f2I7e6n5WWod3xgxCPnDhU0BgLsB4CUoQY,9716
14
14
  wafer/inference.py,sha256=tZCO5i05FKY27ewis3CSBHFBeFbXY3xwj0DSjdoMY9s,4314
@@ -27,15 +27,15 @@ wafer/targets.py,sha256=9r-iRWoKSH5cQl1LcamaX-T7cNVOg99ngIm_hlRk-qU,26922
27
27
  wafer/targets_ops.py,sha256=jN1oIBx0mutxRNE9xpIc7SaBxPkVmOyus2eqn0kEKNI,21475
28
28
  wafer/tracelens.py,sha256=g9ZIeFyNojZn4uTd3skPqIrRiL7aMJOz_-GOd3aiyy4,7998
29
29
  wafer/wevin_cli.py,sha256=vF3GNH-qWXO4hAlXaDg98VZpS4uFexVUp94BHsJjjMU,22179
30
- wafer/workspaces.py,sha256=XZvN-13oq40fkpoJTB2UWTG9KkD-eO47ptXK0FY6360,30083
30
+ wafer/workspaces.py,sha256=k_cQKQwFXDdD3818JpOyerwqQRkbnvPaMi-qo6xq2KQ,32407
31
31
  wafer/skills/wafer-guide/SKILL.md,sha256=KWetJw2TVTbz11_nzqazqOJWWRlbHRFShs4sOoreiWo,3255
32
32
  wafer/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  wafer/templates/ask_docs.py,sha256=Lxs-faz9v5m4Qa4NjF2X_lE8KwM9ES9MNJkxo7ep56o,2256
34
34
  wafer/templates/optimize_kernel.py,sha256=u6AL7Q3uttqlnBLzcoFdsiPq5lV2TV3bgqwCYYlK9gk,2357
35
35
  wafer/templates/optimize_kernelbench.py,sha256=aoOA13zWEl89r6QW03xF9NKxQ7j4mWe9rwua6-mlr4Y,4780
36
36
  wafer/templates/trace_analyze.py,sha256=XE1VqzVkIUsZbXF8EzQdDYgg-AZEYAOFpr6B_vnRELc,2880
37
- wafer_cli-0.2.15.dist-info/METADATA,sha256=z1TLYbZzeOJpMMaG3TqJd_M5WbeLRVEtSmoTO0qhPc4,560
38
- wafer_cli-0.2.15.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
39
- wafer_cli-0.2.15.dist-info/entry_points.txt,sha256=WqB7hB__WhtPY8y1cO2sZiUz7fCq6Ik-usAigpeFvWE,41
40
- wafer_cli-0.2.15.dist-info/top_level.txt,sha256=2MK1IVMWfpLL8BZCQ3E9aG6L6L666gSA_teYlwan4fs,6
41
- wafer_cli-0.2.15.dist-info/RECORD,,
37
+ wafer_cli-0.2.17.dist-info/METADATA,sha256=mqpBLr09_JrI0oGifxo9vVCy8PUg8x2a-LuWN5zMUKI,560
38
+ wafer_cli-0.2.17.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
39
+ wafer_cli-0.2.17.dist-info/entry_points.txt,sha256=WqB7hB__WhtPY8y1cO2sZiUz7fCq6Ik-usAigpeFvWE,41
40
+ wafer_cli-0.2.17.dist-info/top_level.txt,sha256=2MK1IVMWfpLL8BZCQ3E9aG6L6L666gSA_teYlwan4fs,6
41
+ wafer_cli-0.2.17.dist-info/RECORD,,