wafer-cli 0.2.24__tar.gz → 0.2.26__tar.gz
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_cli-0.2.26/PKG-INFO +107 -0
- wafer_cli-0.2.26/README.md +89 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/pyproject.toml +3 -2
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_analytics.py +2 -2
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_billing.py +15 -15
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/GUIDE.md +1 -1
- wafer_cli-0.2.26/wafer/agent_defaults.py +42 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/billing.py +6 -6
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/cli.py +513 -86
- wafer_cli-0.2.26/wafer/cli_instructions.py +143 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/corpus.py +72 -6
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/evaluate.py +13 -6
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/kernel_scope.py +1 -1
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/ncu_analyze.py +1 -1
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/nsys_analyze.py +1 -1
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/skills/wafer-guide/SKILL.md +22 -6
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/ssh_keys.py +6 -6
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/templates/ask_docs.py +1 -1
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/templates/optimize_kernel.py +1 -1
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/templates/optimize_kernelbench.py +17 -62
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/templates/trace_analyze.py +1 -1
- wafer_cli-0.2.26/wafer/tests/test_eval_cli_parity.py +199 -0
- wafer_cli-0.2.26/wafer/trace_compare.py +274 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/wevin_cli.py +68 -9
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/workspaces.py +8 -8
- wafer_cli-0.2.26/wafer_cli.egg-info/PKG-INFO +107 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer_cli.egg-info/SOURCES.txt +4 -0
- wafer_cli-0.2.24/PKG-INFO +0 -16
- wafer_cli-0.2.24/README.md +0 -242
- wafer_cli-0.2.24/wafer_cli.egg-info/PKG-INFO +0 -16
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/setup.cfg +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_auth.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_cli_coverage.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_cli_parity_integration.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_config_integration.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_file_operations_integration.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_kernel_scope_cli.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_nsys_analyze.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_nsys_profile.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_output.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_rocprof_compute_integration.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_skill_commands.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_ssh_integration.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_targets_ops.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_wevin_cli.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/tests/test_workflow_integration.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/__init__.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/analytics.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/api_client.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/auth.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/autotuner.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/config.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/global_config.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/gpu_run.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/inference.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/nsys_profile.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/output.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/problems.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/rocprof_compute.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/rocprof_sdk.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/rocprof_systems.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/target_lock.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/targets.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/targets_ops.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/templates/__init__.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer/tracelens.py +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer_cli.egg-info/dependency_links.txt +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer_cli.egg-info/entry_points.txt +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer_cli.egg-info/requires.txt +0 -0
- {wafer_cli-0.2.24 → wafer_cli-0.2.26}/wafer_cli.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wafer-cli
|
|
3
|
+
Version: 0.2.26
|
|
4
|
+
Summary: CLI for running GPU workloads, managing remote workspaces, and evaluating/optimizing kernels
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: typer>=0.12.0
|
|
8
|
+
Requires-Dist: trio>=0.24.0
|
|
9
|
+
Requires-Dist: trio-asyncio>=0.15.0
|
|
10
|
+
Requires-Dist: wafer-core>=0.1.0
|
|
11
|
+
Requires-Dist: perfetto>=0.16.0
|
|
12
|
+
Requires-Dist: posthog>=3.0.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
16
|
+
Requires-Dist: diff-cover>=8.0.0; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
18
|
+
|
|
19
|
+
# Wafer CLI
|
|
20
|
+
|
|
21
|
+
Wafer CLI gives coding agents direct access to GPU docs, trace analysis, and remote kernel evaluation.
|
|
22
|
+
It helps you develop and optimize GPU kernels even when you are not working on a machine with a GPU.
|
|
23
|
+
|
|
24
|
+
## Key features
|
|
25
|
+
|
|
26
|
+
- Query GPU documentation with citations
|
|
27
|
+
- Analyze GPU traces and profiles
|
|
28
|
+
- Evaluate kernels on remote GPUs for correctness and performance
|
|
29
|
+
- Run commands on GPU targets (remote or local)
|
|
30
|
+
- Manage persistent workspaces
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv tool install wafer-cli
|
|
36
|
+
wafer login
|
|
37
|
+
wafer remote-run -- nvidia-smi
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Common commands
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
wafer workspaces list
|
|
44
|
+
wafer workspaces create my-workspace --wait
|
|
45
|
+
wafer agent -t ask-docs --corpus cuda "What causes shared memory bank conflicts?"
|
|
46
|
+
wafer agent -t trace-analyze --args trace=./profile.ncu-rep "Why is this kernel slow?"
|
|
47
|
+
wafer evaluate --impl kernel.py --reference ref.py --test-cases tests.json --benchmark
|
|
48
|
+
wafer nvidia ncu analyze profile.ncu-rep
|
|
49
|
+
wafer corpus list
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Typical workflows
|
|
53
|
+
|
|
54
|
+
### Query GPU documentation
|
|
55
|
+
|
|
56
|
+
Download a documentation corpus and ask questions with citations.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
wafer corpus download cuda
|
|
60
|
+
wafer agent -t ask-docs --corpus cuda "What causes shared memory bank conflicts?"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Analyze performance traces
|
|
64
|
+
|
|
65
|
+
Use the trace analysis template or query trace data directly.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
wafer agent -t trace-analyze --args trace=./profile.ncu-rep "Why is this kernel slow?"
|
|
69
|
+
wafer nvidia perfetto query trace.json \
|
|
70
|
+
"SELECT name, dur/1e6 as ms FROM slice WHERE cat='kernel' ORDER BY dur DESC LIMIT 10"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Evaluate kernels on remote GPUs
|
|
74
|
+
|
|
75
|
+
Run correctness and performance checks on a remote target.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
wafer evaluate \
|
|
79
|
+
--impl ./kernel.py \
|
|
80
|
+
--reference ./reference.py \
|
|
81
|
+
--test-cases ./tests.json \
|
|
82
|
+
--benchmark
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Run commands on a remote GPU
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
wafer remote-run -- nvidia-smi
|
|
89
|
+
wafer remote-run --upload-dir ./my_code -- python3 train.py
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Manage workspaces
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
wafer workspaces list
|
|
96
|
+
wafer workspaces create my-workspace --wait
|
|
97
|
+
wafer workspaces ssh <workspace-id>
|
|
98
|
+
wafer workspaces delete <workspace-id>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Install the CLI skill (optional)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
wafer skill install
|
|
105
|
+
# or
|
|
106
|
+
wafer skill install -t <claude/codex>
|
|
107
|
+
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Wafer CLI
|
|
2
|
+
|
|
3
|
+
Wafer CLI gives coding agents direct access to GPU docs, trace analysis, and remote kernel evaluation.
|
|
4
|
+
It helps you develop and optimize GPU kernels even when you are not working on a machine with a GPU.
|
|
5
|
+
|
|
6
|
+
## Key features
|
|
7
|
+
|
|
8
|
+
- Query GPU documentation with citations
|
|
9
|
+
- Analyze GPU traces and profiles
|
|
10
|
+
- Evaluate kernels on remote GPUs for correctness and performance
|
|
11
|
+
- Run commands on GPU targets (remote or local)
|
|
12
|
+
- Manage persistent workspaces
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv tool install wafer-cli
|
|
18
|
+
wafer login
|
|
19
|
+
wafer remote-run -- nvidia-smi
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Common commands
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
wafer workspaces list
|
|
26
|
+
wafer workspaces create my-workspace --wait
|
|
27
|
+
wafer agent -t ask-docs --corpus cuda "What causes shared memory bank conflicts?"
|
|
28
|
+
wafer agent -t trace-analyze --args trace=./profile.ncu-rep "Why is this kernel slow?"
|
|
29
|
+
wafer evaluate --impl kernel.py --reference ref.py --test-cases tests.json --benchmark
|
|
30
|
+
wafer nvidia ncu analyze profile.ncu-rep
|
|
31
|
+
wafer corpus list
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Typical workflows
|
|
35
|
+
|
|
36
|
+
### Query GPU documentation
|
|
37
|
+
|
|
38
|
+
Download a documentation corpus and ask questions with citations.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
wafer corpus download cuda
|
|
42
|
+
wafer agent -t ask-docs --corpus cuda "What causes shared memory bank conflicts?"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Analyze performance traces
|
|
46
|
+
|
|
47
|
+
Use the trace analysis template or query trace data directly.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
wafer agent -t trace-analyze --args trace=./profile.ncu-rep "Why is this kernel slow?"
|
|
51
|
+
wafer nvidia perfetto query trace.json \
|
|
52
|
+
"SELECT name, dur/1e6 as ms FROM slice WHERE cat='kernel' ORDER BY dur DESC LIMIT 10"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Evaluate kernels on remote GPUs
|
|
56
|
+
|
|
57
|
+
Run correctness and performance checks on a remote target.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
wafer evaluate \
|
|
61
|
+
--impl ./kernel.py \
|
|
62
|
+
--reference ./reference.py \
|
|
63
|
+
--test-cases ./tests.json \
|
|
64
|
+
--benchmark
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Run commands on a remote GPU
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
wafer remote-run -- nvidia-smi
|
|
71
|
+
wafer remote-run --upload-dir ./my_code -- python3 train.py
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Manage workspaces
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
wafer workspaces list
|
|
78
|
+
wafer workspaces create my-workspace --wait
|
|
79
|
+
wafer workspaces ssh <workspace-id>
|
|
80
|
+
wafer workspaces delete <workspace-id>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Install the CLI skill (optional)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
wafer skill install
|
|
87
|
+
# or
|
|
88
|
+
wafer skill install -t <claude/codex>
|
|
89
|
+
```
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "wafer-cli"
|
|
3
|
-
version = "0.2.
|
|
4
|
-
description = "CLI
|
|
3
|
+
version = "0.2.26"
|
|
4
|
+
description = "CLI for running GPU workloads, managing remote workspaces, and evaluating/optimizing kernels"
|
|
5
|
+
readme = "README.md"
|
|
5
6
|
requires-python = ">=3.11"
|
|
6
7
|
dependencies = [
|
|
7
8
|
"typer>=0.12.0",
|
|
@@ -467,7 +467,7 @@ class TestLoginLogoutAnalytics:
|
|
|
467
467
|
patch("wafer.analytics.track_login") as mock_track_login, \
|
|
468
468
|
patch("wafer.analytics.init_analytics", return_value=True):
|
|
469
469
|
|
|
470
|
-
runner.invoke(app, ["login", "--token", "test-token"])
|
|
470
|
+
runner.invoke(app, ["auth", "login", "--token", "test-token"])
|
|
471
471
|
|
|
472
472
|
# track_login should be called
|
|
473
473
|
mock_track_login.assert_called_once_with("test-user-id", "test@example.com")
|
|
@@ -484,7 +484,7 @@ class TestLoginLogoutAnalytics:
|
|
|
484
484
|
patch("wafer.analytics.track_logout") as mock_track_logout, \
|
|
485
485
|
patch("wafer.analytics.init_analytics", return_value=True):
|
|
486
486
|
|
|
487
|
-
result = runner.invoke(app, ["logout"])
|
|
487
|
+
result = runner.invoke(app, ["auth", "logout"])
|
|
488
488
|
|
|
489
489
|
assert result.exit_code == 0
|
|
490
490
|
mock_track_logout.assert_called_once()
|
|
@@ -210,7 +210,7 @@ class TestBillingUsageCommand:
|
|
|
210
210
|
)
|
|
211
211
|
mock_client.return_value.__enter__.return_value.get.return_value = mock_response
|
|
212
212
|
|
|
213
|
-
result = runner.invoke(app, ["billing"])
|
|
213
|
+
result = runner.invoke(app, ["config", "billing"])
|
|
214
214
|
|
|
215
215
|
assert result.exit_code != 0
|
|
216
216
|
assert "login" in result.output.lower()
|
|
@@ -242,7 +242,7 @@ class TestBillingUsageCommand:
|
|
|
242
242
|
mock_response.raise_for_status.return_value = None
|
|
243
243
|
mock_client.return_value.__enter__.return_value.get.return_value = mock_response
|
|
244
244
|
|
|
245
|
-
result = runner.invoke(app, ["billing", "--json"])
|
|
245
|
+
result = runner.invoke(app, ["config", "billing", "--json"])
|
|
246
246
|
|
|
247
247
|
assert result.exit_code == 0
|
|
248
248
|
data = json.loads(result.stdout)
|
|
@@ -275,7 +275,7 @@ class TestBillingUsageCommand:
|
|
|
275
275
|
mock_response.raise_for_status.return_value = None
|
|
276
276
|
mock_client.return_value.__enter__.return_value.get.return_value = mock_response
|
|
277
277
|
|
|
278
|
-
result = runner.invoke(app, ["billing"])
|
|
278
|
+
result = runner.invoke(app, ["config", "billing"])
|
|
279
279
|
|
|
280
280
|
assert result.exit_code == 0
|
|
281
281
|
assert "Pro" in result.output
|
|
@@ -294,7 +294,7 @@ class TestBillingUsageCommand:
|
|
|
294
294
|
httpx.RequestError("Connection failed")
|
|
295
295
|
)
|
|
296
296
|
|
|
297
|
-
result = runner.invoke(app, ["billing"])
|
|
297
|
+
result = runner.invoke(app, ["config", "billing"])
|
|
298
298
|
|
|
299
299
|
assert result.exit_code != 0
|
|
300
300
|
assert "error" in result.output.lower() or "reach" in result.output.lower()
|
|
@@ -317,7 +317,7 @@ class TestBillingTopupCommand:
|
|
|
317
317
|
)
|
|
318
318
|
mock_client.return_value.__enter__.return_value.post.return_value = mock_response
|
|
319
319
|
|
|
320
|
-
result = runner.invoke(app, ["billing", "topup"])
|
|
320
|
+
result = runner.invoke(app, ["config", "billing", "topup"])
|
|
321
321
|
|
|
322
322
|
assert result.exit_code != 0
|
|
323
323
|
assert "login" in result.output.lower()
|
|
@@ -343,7 +343,7 @@ class TestBillingTopupCommand:
|
|
|
343
343
|
mock_client.return_value.__enter__.return_value.post.return_value = mock_response
|
|
344
344
|
|
|
345
345
|
with patch("webbrowser.open") as mock_browser:
|
|
346
|
-
result = runner.invoke(app, ["billing", "topup"])
|
|
346
|
+
result = runner.invoke(app, ["config", "billing", "topup"])
|
|
347
347
|
|
|
348
348
|
assert result.exit_code == 0
|
|
349
349
|
# Verify $25 = 2500 cents was sent
|
|
@@ -372,7 +372,7 @@ class TestBillingTopupCommand:
|
|
|
372
372
|
mock_client.return_value.__enter__.return_value.post.return_value = mock_response
|
|
373
373
|
|
|
374
374
|
with patch("webbrowser.open") as mock_browser:
|
|
375
|
-
result = runner.invoke(app, ["billing", "topup", "100"])
|
|
375
|
+
result = runner.invoke(app, ["config", "billing", "topup", "100"])
|
|
376
376
|
|
|
377
377
|
assert result.exit_code == 0
|
|
378
378
|
call_args = mock_client.return_value.__enter__.return_value.post.call_args
|
|
@@ -381,14 +381,14 @@ class TestBillingTopupCommand:
|
|
|
381
381
|
|
|
382
382
|
def test_amount_below_minimum(self) -> None:
|
|
383
383
|
"""Amount below $10 should error."""
|
|
384
|
-
result = runner.invoke(app, ["billing", "topup", "5"])
|
|
384
|
+
result = runner.invoke(app, ["config", "billing", "topup", "5"])
|
|
385
385
|
|
|
386
386
|
assert result.exit_code != 0
|
|
387
387
|
assert "10" in result.output # Should mention minimum
|
|
388
388
|
|
|
389
389
|
def test_amount_above_maximum(self) -> None:
|
|
390
390
|
"""Amount above $500 should error."""
|
|
391
|
-
result = runner.invoke(app, ["billing", "topup", "600"])
|
|
391
|
+
result = runner.invoke(app, ["config", "billing", "topup", "600"])
|
|
392
392
|
|
|
393
393
|
assert result.exit_code != 0
|
|
394
394
|
assert "500" in result.output # Should mention maximum
|
|
@@ -410,7 +410,7 @@ class TestBillingTopupCommand:
|
|
|
410
410
|
)
|
|
411
411
|
mock_client.return_value.__enter__.return_value.post.return_value = mock_response
|
|
412
412
|
|
|
413
|
-
result = runner.invoke(app, ["billing", "topup"])
|
|
413
|
+
result = runner.invoke(app, ["config", "billing", "topup"])
|
|
414
414
|
|
|
415
415
|
assert result.exit_code != 0
|
|
416
416
|
assert "upgrade" in result.output.lower() or "portal" in result.output.lower()
|
|
@@ -436,7 +436,7 @@ class TestBillingTopupCommand:
|
|
|
436
436
|
mock_client.return_value.__enter__.return_value.post.return_value = mock_response
|
|
437
437
|
|
|
438
438
|
with patch("webbrowser.open") as mock_browser:
|
|
439
|
-
result = runner.invoke(app, ["billing", "topup", "--no-browser"])
|
|
439
|
+
result = runner.invoke(app, ["config", "billing", "topup", "--no-browser"])
|
|
440
440
|
|
|
441
441
|
assert result.exit_code == 0
|
|
442
442
|
assert "https://checkout.stripe.com/test" in result.output
|
|
@@ -460,7 +460,7 @@ class TestBillingPortalCommand:
|
|
|
460
460
|
)
|
|
461
461
|
mock_client.return_value.__enter__.return_value.post.return_value = mock_response
|
|
462
462
|
|
|
463
|
-
result = runner.invoke(app, ["billing", "portal"])
|
|
463
|
+
result = runner.invoke(app, ["config", "billing", "portal"])
|
|
464
464
|
|
|
465
465
|
assert result.exit_code != 0
|
|
466
466
|
assert "login" in result.output.lower()
|
|
@@ -483,7 +483,7 @@ class TestBillingPortalCommand:
|
|
|
483
483
|
mock_client.return_value.__enter__.return_value.post.return_value = mock_response
|
|
484
484
|
|
|
485
485
|
with patch("webbrowser.open") as mock_browser:
|
|
486
|
-
result = runner.invoke(app, ["billing", "portal"])
|
|
486
|
+
result = runner.invoke(app, ["config", "billing", "portal"])
|
|
487
487
|
|
|
488
488
|
assert result.exit_code == 0
|
|
489
489
|
mock_browser.assert_called_once_with("https://billing.stripe.com/test")
|
|
@@ -506,7 +506,7 @@ class TestBillingPortalCommand:
|
|
|
506
506
|
mock_client.return_value.__enter__.return_value.post.return_value = mock_response
|
|
507
507
|
|
|
508
508
|
with patch("webbrowser.open") as mock_browser:
|
|
509
|
-
result = runner.invoke(app, ["billing", "portal", "--no-browser"])
|
|
509
|
+
result = runner.invoke(app, ["config", "billing", "portal", "--no-browser"])
|
|
510
510
|
|
|
511
511
|
assert result.exit_code == 0
|
|
512
512
|
assert "https://billing.stripe.com/test" in result.output
|
|
@@ -528,4 +528,4 @@ class TestInsufficientCreditsError:
|
|
|
528
528
|
message = _friendly_error(402, '{"detail": "Insufficient credits"}', "test-workspace")
|
|
529
529
|
|
|
530
530
|
assert "credit" in message.lower()
|
|
531
|
-
assert "wafer billing" in message.lower()
|
|
531
|
+
assert "wafer config billing" in message.lower()
|
|
@@ -7,7 +7,7 @@ GPU development primitives for LLM agents.
|
|
|
7
7
|
Run code on cloud GPUs instantly with workspaces:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
wafer login
|
|
10
|
+
wafer auth login # One-time auth
|
|
11
11
|
wafer workspaces create dev --gpu B200 # Create workspace (NVIDIA B200)
|
|
12
12
|
wafer workspaces exec dev -- python -c "import torch; print(torch.cuda.get_device_name(0))"
|
|
13
13
|
wafer workspaces sync dev ./my-project # Sync files
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Shared agent defaults for kernel optimization tasks.
|
|
2
|
+
|
|
3
|
+
Single source of truth for bash allowlists and enabled tools used by both:
|
|
4
|
+
- CLI templates (apps/wafer-cli/wafer/templates/optimize_kernelbench.py)
|
|
5
|
+
- Eval configs (research/evals/optimize_kernelbench_eval/.../base_config.py)
|
|
6
|
+
|
|
7
|
+
Import from here instead of defining your own copy.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
# Tools available to the agent (coding environment tools)
|
|
13
|
+
ENABLED_TOOLS: list[str] = ["read", "write", "edit", "glob", "grep", "bash"]
|
|
14
|
+
|
|
15
|
+
# Bash commands allowed for kernel optimization agents.
|
|
16
|
+
# Uses prefix matching — "wafer evaluate" also allows "wafer evaluate kernelbench".
|
|
17
|
+
KERNELBENCH_BASH_ALLOWLIST: list[str] = [
|
|
18
|
+
# Kernel evaluation
|
|
19
|
+
"wafer evaluate",
|
|
20
|
+
# Profiling — AMD
|
|
21
|
+
"wafer amd rocprof-compute",
|
|
22
|
+
"wafer amd rocprof-sdk",
|
|
23
|
+
"wafer amd rocprof-systems",
|
|
24
|
+
# Profiling — NVIDIA
|
|
25
|
+
"wafer nvidia ncu",
|
|
26
|
+
"wafer nvidia nsys",
|
|
27
|
+
# Analysis
|
|
28
|
+
"wafer compiler-analyze",
|
|
29
|
+
# Sub-agents
|
|
30
|
+
"wafer agent -t ask-docs",
|
|
31
|
+
# General utilities
|
|
32
|
+
"python",
|
|
33
|
+
"python3",
|
|
34
|
+
"timeout",
|
|
35
|
+
"ls",
|
|
36
|
+
"cat",
|
|
37
|
+
"head",
|
|
38
|
+
"tail",
|
|
39
|
+
"wc",
|
|
40
|
+
"pwd",
|
|
41
|
+
"which",
|
|
42
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Billing CLI - Manage credits and subscription.
|
|
2
2
|
|
|
3
|
-
This module provides the implementation for the `wafer billing` subcommand.
|
|
3
|
+
This module provides the implementation for the `wafer config billing` subcommand.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import json
|
|
@@ -126,7 +126,7 @@ def format_usage_text(usage: dict) -> str:
|
|
|
126
126
|
lines.extend([
|
|
127
127
|
"",
|
|
128
128
|
"Upgrade to Pro for hardware counters and credit topups:",
|
|
129
|
-
" wafer billing portal",
|
|
129
|
+
" wafer config billing portal",
|
|
130
130
|
])
|
|
131
131
|
|
|
132
132
|
return "\n".join(lines)
|
|
@@ -153,7 +153,7 @@ def get_usage(json_output: bool = False) -> str:
|
|
|
153
153
|
usage = response.json()
|
|
154
154
|
except httpx.HTTPStatusError as e:
|
|
155
155
|
if e.response.status_code == 401:
|
|
156
|
-
raise RuntimeError("Not authenticated. Run: wafer login") from e
|
|
156
|
+
raise RuntimeError("Not authenticated. Run: wafer auth login") from e
|
|
157
157
|
raise RuntimeError(f"API error: {e.response.status_code} - {e.response.text}") from e
|
|
158
158
|
except httpx.RequestError as e:
|
|
159
159
|
raise RuntimeError(f"Could not reach API: {e}") from e
|
|
@@ -188,7 +188,7 @@ def create_topup(amount_cents: int) -> dict:
|
|
|
188
188
|
return response.json()
|
|
189
189
|
except httpx.HTTPStatusError as e:
|
|
190
190
|
if e.response.status_code == 401:
|
|
191
|
-
raise RuntimeError("Not authenticated. Run: wafer login") from e
|
|
191
|
+
raise RuntimeError("Not authenticated. Run: wafer auth login") from e
|
|
192
192
|
if e.response.status_code == 400:
|
|
193
193
|
# Invalid amount
|
|
194
194
|
try:
|
|
@@ -200,7 +200,7 @@ def create_topup(amount_cents: int) -> dict:
|
|
|
200
200
|
# Start tier or other restriction
|
|
201
201
|
raise RuntimeError(
|
|
202
202
|
"Topup not available for your subscription tier.\n"
|
|
203
|
-
"Upgrade your subscription first: wafer billing portal"
|
|
203
|
+
"Upgrade your subscription first: wafer config billing portal"
|
|
204
204
|
) from e
|
|
205
205
|
if e.response.status_code == 503:
|
|
206
206
|
raise RuntimeError("Billing service temporarily unavailable. Please try again later.") from e
|
|
@@ -227,7 +227,7 @@ def get_portal_url() -> dict:
|
|
|
227
227
|
return response.json()
|
|
228
228
|
except httpx.HTTPStatusError as e:
|
|
229
229
|
if e.response.status_code == 401:
|
|
230
|
-
raise RuntimeError("Not authenticated. Run: wafer login") from e
|
|
230
|
+
raise RuntimeError("Not authenticated. Run: wafer auth login") from e
|
|
231
231
|
raise RuntimeError(f"API error: {e.response.status_code} - {e.response.text}") from e
|
|
232
232
|
except httpx.RequestError as e:
|
|
233
233
|
raise RuntimeError(f"Could not reach API: {e}") from e
|