gpu4agents 0.5.0__tar.gz → 0.6.0__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.
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/CHANGELOG.md +14 -0
- gpu4agents-0.6.0/INSTALL.md +53 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/PKG-INFO +23 -10
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/README.md +22 -9
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/ACCEPTANCE.md +2 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/AGENTS.md +39 -13
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/__init__.py +1 -1
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/agent/install.py +138 -17
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/agent/skill/SKILL.md +58 -9
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/account.py +87 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/agent.py +3 -3
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/main.py +1 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/credentials.py +26 -0
- gpu4agents-0.6.0/install.sh +104 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/pyproject.toml +1 -1
- gpu4agents-0.6.0/skills/gpu4agents/SKILL.md +117 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_agent.py +88 -6
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_cli.py +74 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.env.example +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.github/dependabot.yml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.github/pull_request_template.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.github/workflows/ci.yml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.github/workflows/release.yml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/.gitignore +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/CODE_OF_CONDUCT.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/CONTRIBUTING.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/LICENSE +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/SECURITY.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/ADDING_A_PROVIDER.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/CHOOSING_A_MODEL.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/CONFIG.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/INTEGRATION.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/LIFECYCLE.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/USAGE.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/assets/banner.png +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/docs/assets/jambu-logo.jpg +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/experiment.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/jambu.yaml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/mastra-agent/README.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/mastra-agent/package-lock.json +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/mastra-agent/package.json +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/mastra-agent/src/agent.ts +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/mastra-agent/src/index.ts +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/mastra-agent/src/tools/weather-tool.ts +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/mastra-agent/tsconfig.json +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/typescript-client/README.md +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/typescript-client/package-lock.json +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/typescript-client/package.json +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/typescript-client/src/gpu.ts +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/typescript-client/src/index.ts +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/examples/typescript-client/tsconfig.json +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/__main__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/agent/__init__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/agent/jobs.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/agent/mcp.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/__init__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/_common.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/inspect.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/lifecycle.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/providers.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/run.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/setup.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/status.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/timer.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/cli/validate.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/__init__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/beacon.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/config.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/durations.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/engine.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/errors.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/events.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/execution.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/health.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/hosts.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/model_inspect.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/models.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/paths.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/policy.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/state.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/sweep.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/core/tool_parsers.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/providers/__init__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/providers/base.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/providers/registry.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/providers/vast/__init__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/providers/vast/client.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/providers/vast/config.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/providers/vast/mapper.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/providers/vast/provider.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/runtime/__init__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/runtime/base.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/runtime/vllm.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/watchdog/__init__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/watchdog/agent_body.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/gpu4agents/watchdog/builder.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/jambu.example.yaml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/jambu.models.yaml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/jambu.yaml +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/jambu_gpu/__init__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/jambu_gpu/__main__.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/conftest.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/fakes.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_config.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_engine.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_engine_sessions.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_integration.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_model_inspect.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_paths.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_policy.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_runtime_vllm.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_state.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_sweep.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_vast_mapper.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_vast_provider.py +0 -0
- {gpu4agents-0.5.0 → gpu4agents-0.6.0}/tests/test_watchdog.py +0 -0
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [0.6.0] - 2026-11-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Agents install it themselves.** Paste one line into Claude Code, Codex, Gemini CLI or Cursor; the agent follows [`INSTALL.md`](INSTALL.md) (served at `gpu4agents.jambu.ai/install.md`) and sets everything up, without renting anything.
|
|
9
|
+
- `install.sh`: needs only `curl`. Installs uv if missing and `gpu4agents` with a Python uv downloads when needed, removes an old `jambu-gpu` tool, and runs `gpu agent install` for the runtimes it finds (`--agent` to pick one).
|
|
10
|
+
- `gpu login`: the user types the Vast.ai key into a hidden prompt; it is checked against the API, then saved in `~/.gpu/credentials` (mode 600). `--check` reports whether a working key is found and where; `--stdin` reads it from a pipe.
|
|
11
|
+
- `gpu agent install -t codex` (skill in `.agents/skills/`, MCP server in `~/.codex/config.toml`), `-t gemini` (skill in `.agents/skills/`, MCP server in `.gemini/settings.json`) and `-t auto` (every runtime found).
|
|
12
|
+
- The skill is published at `skills/gpu4agents/SKILL.md`, so `npx skills add jambuai/gpu4agents` installs it, and it now covers installation, connecting the account, monitoring (`status`, `logs`, `events`, `ls`, `sweep`) and the exit codes.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- The skill is named `gpu4agents` (it was `gpu`). `gpu agent install` replaces the old `gpu` skill folder it wrote before; a folder of the same name from someone else is left alone.
|
|
16
|
+
- Cursor now gets the skill in `.agents/skills/` instead of `.claude/skills/`.
|
|
17
|
+
|
|
5
18
|
## [0.5.0] - 2026-11-02
|
|
6
19
|
|
|
7
20
|
### Changed
|
|
@@ -52,6 +65,7 @@ All notable changes to this project. The format follows [Keep a Changelog](https
|
|
|
52
65
|
- A per-instance endpoint key; `gpu --json setup` / `gpu env` return `base_url`, `api_key` and `model`.
|
|
53
66
|
- [docs/INTEGRATION.md](docs/INTEGRATION.md) and a one-file TypeScript client.
|
|
54
67
|
|
|
68
|
+
[0.6.0]: https://github.com/jambuai/gpu4agents/compare/v0.5.0...v0.6.0
|
|
55
69
|
[0.5.0]: https://github.com/jambuai/gpu4agents/compare/v0.4.1...v0.5.0
|
|
56
70
|
[0.4.1]: https://github.com/jambuai/gpu4agents/compare/v0.4.0...v0.4.1
|
|
57
71
|
[0.4.0]: https://github.com/jambuai/gpu4agents/compare/v0.3.1...v0.4.0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Install GPU for Agents (instructions for the agent)
|
|
2
|
+
|
|
3
|
+
You are a coding agent (Claude Code, Codex, Gemini CLI, Cursor, or another) and the user asked you to set up **GPU for Agents**: a `gpu` command, a skill and MCP tools that let you rent a cloud GPU, serve a model and run experiments, while a watchdog on the machine shuts it down when idle so the user pays only while it works.
|
|
4
|
+
|
|
5
|
+
Follow these steps in order. Run the commands yourself; tell the user briefly what each step did.
|
|
6
|
+
|
|
7
|
+
## 1. Install the `gpu` command and give it to this agent
|
|
8
|
+
|
|
9
|
+
Pick the name of the runtime you are running in: `claude` (Claude Code), `codex` (Codex), `gemini` (Gemini CLI) or `cursor` (Cursor). If you are none of these, use `auto`.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
curl -fsSL https://gpu4agents.jambu.ai/install.sh | sh -s -- --agent <runtime>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If that address does not resolve, use the copy in the repository:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
curl -fsSL https://raw.githubusercontent.com/jambuai/gpu4agents/main/install.sh | sh -s -- --agent <runtime>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The script needs only `curl` (Linux or macOS; on Windows, run it inside WSL). It installs [uv](https://docs.astral.sh/uv/) if missing, uses it to install the `gpu4agents` package with a Python it downloads by itself, and then installs the skill and registers the `gpu` MCP server for your runtime. It does not ask for or read any key.
|
|
22
|
+
|
|
23
|
+
Check: `~/.local/bin/gpu --version` prints 0.6.0 or later. Use `~/.local/bin/gpu` for the rest of this session if plain `gpu` is not found.
|
|
24
|
+
|
|
25
|
+
## 2. Connect the user's GPU account
|
|
26
|
+
|
|
27
|
+
Run `gpu login --check`.
|
|
28
|
+
|
|
29
|
+
- Exit code 0: the account is connected. Go to step 3.
|
|
30
|
+
- Exit code 5: no working key yet. Tell the user, in these words or close:
|
|
31
|
+
|
|
32
|
+
> Create an API key at https://cloud.vast.ai/manage-keys/ and add a few dollars of credit. Then open your own terminal, run `gpu login`, and paste the key into the hidden prompt.
|
|
33
|
+
|
|
34
|
+
Wait until the user says it is done, then run `gpu login --check` again.
|
|
35
|
+
|
|
36
|
+
Never ask the user to paste the key into this conversation, never read `~/.gpu/credentials` or `.env` files, and never print the key.
|
|
37
|
+
|
|
38
|
+
## 3. Learn how to use it
|
|
39
|
+
|
|
40
|
+
Run `gpu agent skill` and read the output: it is the skill you just installed, with the rules (ask before renting, wait out a slow setup, clean up), the commands, monitoring and error codes. Follow it from now on in this session.
|
|
41
|
+
|
|
42
|
+
The MCP tools (`gpu_setup`, `gpu_status`, ...) appear after the user restarts the agent. Until then, the CLI does everything: `gpu --json <command>`.
|
|
43
|
+
|
|
44
|
+
## 4. Tell the user it is ready
|
|
45
|
+
|
|
46
|
+
Summarize in a few lines:
|
|
47
|
+
|
|
48
|
+
- what was installed (the `gpu` command, the skill, the MCP server for this runtime);
|
|
49
|
+
- whether the account is connected, or what they still need to do (`gpu login`);
|
|
50
|
+
- that they should restart the agent to get the tools;
|
|
51
|
+
- an example request: "Compare Qwen 7B and Llama 8B on 50 prompts", and that you will show the price and ask before renting anything.
|
|
52
|
+
|
|
53
|
+
Do not rent a GPU as part of the installation.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: gpu4agents
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: GPU for Agents: your coding agent rents the GPU it needs, on its own. You pay only while it works.
|
|
5
5
|
Project-URL: Homepage, https://gpu4agents.jambu.ai
|
|
6
6
|
Project-URL: Documentation, https://github.com/jambuai/gpu4agents/tree/main/docs
|
|
@@ -67,22 +67,35 @@ Description-Content-Type: text/markdown
|
|
|
67
67
|
|
|
68
68
|
Ask Claude Code, Cursor or any MCP client to "compare Qwen 7B and Llama 8B on these 50 prompts". The agent finds a GPU under your budget, serves the model behind an OpenAI-compatible API with vLLM, and runs the experiment. A watchdog runs on the machine itself, so when nothing uses it for `idle_timeout` (15 minutes by default) it shuts down, even if the agent forgot, the chat closed or your laptop is asleep.
|
|
69
69
|
|
|
70
|
-
- **Self-service GPUs for agents.** A skill and local MCP tools (`gpu_offers`, `gpu_setup`, `gpu_run`, `gpu_status`, `gpu_destroy`, ...) give the agent the whole lifecycle. The skill tells it to show you the price and ask before renting.
|
|
70
|
+
- **Self-service GPUs for agents.** Claude Code, Codex, Gemini CLI and Cursor install it themselves from one pasted line. A skill and local MCP tools (`gpu_offers`, `gpu_setup`, `gpu_run`, `gpu_status`, `gpu_destroy`, ...) give the agent the whole lifecycle. The skill tells it to show you the price and ask before renting.
|
|
71
71
|
- **Billing that ends when the work does.** Idle timeout, maximum lifetime, hourly and session budgets, and a sweep that cleans up anything left behind.
|
|
72
72
|
- **Any app, any language.** `gpu --json setup` returns `base_url`, `api_key` and `model` for any OpenAI SDK.
|
|
73
73
|
|
|
74
74
|
Vast.ai is the first provider; another provider is a new adapter, and the lifecycle rule stays the same.
|
|
75
75
|
|
|
76
|
-
## Quickstart:
|
|
76
|
+
## Quickstart: let your agent install it
|
|
77
|
+
|
|
78
|
+
Paste this into Claude Code, Codex, Gemini CLI or Cursor (say which one you use):
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
Set up GPU for Agents for me. I'm using Claude Code. Fetch https://gpu4agents.jambu.ai/install.md with curl and follow its steps.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The agent installs the `gpu` command (with [uv](https://docs.astral.sh/uv/), which brings its own Python), the `gpu4agents` skill and the `gpu` MCP tools. Then you connect your GPU account in your own terminal, so the agent never sees the key:
|
|
77
85
|
|
|
78
86
|
```bash
|
|
79
|
-
|
|
80
|
-
cd your-project
|
|
81
|
-
echo "VAST_API_KEY=..." >> .env # or ~/.gpu/credentials; never in jambu.yaml
|
|
82
|
-
gpu agent install # Claude Code; -t cursor, -t desktop, --global
|
|
87
|
+
gpu login # paste your Vast.ai key (https://cloud.vast.ai/manage-keys/) into the hidden prompt
|
|
83
88
|
```
|
|
84
89
|
|
|
85
|
-
|
|
90
|
+
Restart the agent and ask for an experiment. It shows the price and asks before renting anything. See [Letting a coding agent rent GPUs](docs/AGENTS.md).
|
|
91
|
+
|
|
92
|
+
Prefer a terminal? These do the same:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
curl -fsSL https://gpu4agents.jambu.ai/install.sh | sh # uv + Python + gpu, then every agent it finds
|
|
96
|
+
npx skills add jambuai/gpu4agents # just the skill; it installs gpu on first use
|
|
97
|
+
uv tool install gpu4agents && gpu agent install -t codex --global # by hand: claude | codex | gemini | cursor | auto
|
|
98
|
+
```
|
|
86
99
|
|
|
87
100
|
## Quickstart: from a shell or an app
|
|
88
101
|
|
|
@@ -116,11 +129,11 @@ gpu_runtime:
|
|
|
116
129
|
|
|
117
130
|
A full example is [`jambu.example.yaml`](jambu.example.yaml). As a last line of defense, `gpu ls` shows every machine billing in the account and `gpu sweep --install-timer` guards every project on the machine and cleans up orphans. See [Keeping the account clean](docs/USAGE.md#keeping-the-account-clean).
|
|
118
131
|
|
|
119
|
-
|
|
132
|
+
`gpu login` saves the API key in `~/.gpu/credentials` (mode 600) after checking it; the environment and a project `.env` work too. It never goes in the manifest, and the agent never sees it.
|
|
120
133
|
|
|
121
134
|
## Docs
|
|
122
135
|
|
|
123
|
-
- [Letting a coding agent rent GPUs (Claude Code, Cursor, Claude Desktop)](docs/AGENTS.md)
|
|
136
|
+
- [Letting a coding agent rent GPUs (Claude Code, Codex, Gemini CLI, Cursor, Claude Desktop)](docs/AGENTS.md)
|
|
124
137
|
- [Integrating into an application (any language)](docs/INTEGRATION.md)
|
|
125
138
|
- [Commands, workloads, and agents](docs/USAGE.md)
|
|
126
139
|
- [Every config field](docs/CONFIG.md)
|
|
@@ -26,22 +26,35 @@
|
|
|
26
26
|
|
|
27
27
|
Ask Claude Code, Cursor or any MCP client to "compare Qwen 7B and Llama 8B on these 50 prompts". The agent finds a GPU under your budget, serves the model behind an OpenAI-compatible API with vLLM, and runs the experiment. A watchdog runs on the machine itself, so when nothing uses it for `idle_timeout` (15 minutes by default) it shuts down, even if the agent forgot, the chat closed or your laptop is asleep.
|
|
28
28
|
|
|
29
|
-
- **Self-service GPUs for agents.** A skill and local MCP tools (`gpu_offers`, `gpu_setup`, `gpu_run`, `gpu_status`, `gpu_destroy`, ...) give the agent the whole lifecycle. The skill tells it to show you the price and ask before renting.
|
|
29
|
+
- **Self-service GPUs for agents.** Claude Code, Codex, Gemini CLI and Cursor install it themselves from one pasted line. A skill and local MCP tools (`gpu_offers`, `gpu_setup`, `gpu_run`, `gpu_status`, `gpu_destroy`, ...) give the agent the whole lifecycle. The skill tells it to show you the price and ask before renting.
|
|
30
30
|
- **Billing that ends when the work does.** Idle timeout, maximum lifetime, hourly and session budgets, and a sweep that cleans up anything left behind.
|
|
31
31
|
- **Any app, any language.** `gpu --json setup` returns `base_url`, `api_key` and `model` for any OpenAI SDK.
|
|
32
32
|
|
|
33
33
|
Vast.ai is the first provider; another provider is a new adapter, and the lifecycle rule stays the same.
|
|
34
34
|
|
|
35
|
-
## Quickstart:
|
|
35
|
+
## Quickstart: let your agent install it
|
|
36
|
+
|
|
37
|
+
Paste this into Claude Code, Codex, Gemini CLI or Cursor (say which one you use):
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
Set up GPU for Agents for me. I'm using Claude Code. Fetch https://gpu4agents.jambu.ai/install.md with curl and follow its steps.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The agent installs the `gpu` command (with [uv](https://docs.astral.sh/uv/), which brings its own Python), the `gpu4agents` skill and the `gpu` MCP tools. Then you connect your GPU account in your own terminal, so the agent never sees the key:
|
|
36
44
|
|
|
37
45
|
```bash
|
|
38
|
-
|
|
39
|
-
cd your-project
|
|
40
|
-
echo "VAST_API_KEY=..." >> .env # or ~/.gpu/credentials; never in jambu.yaml
|
|
41
|
-
gpu agent install # Claude Code; -t cursor, -t desktop, --global
|
|
46
|
+
gpu login # paste your Vast.ai key (https://cloud.vast.ai/manage-keys/) into the hidden prompt
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
Restart the agent and ask for an experiment. It shows the price and asks before renting anything. See [Letting a coding agent rent GPUs](docs/AGENTS.md).
|
|
50
|
+
|
|
51
|
+
Prefer a terminal? These do the same:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl -fsSL https://gpu4agents.jambu.ai/install.sh | sh # uv + Python + gpu, then every agent it finds
|
|
55
|
+
npx skills add jambuai/gpu4agents # just the skill; it installs gpu on first use
|
|
56
|
+
uv tool install gpu4agents && gpu agent install -t codex --global # by hand: claude | codex | gemini | cursor | auto
|
|
57
|
+
```
|
|
45
58
|
|
|
46
59
|
## Quickstart: from a shell or an app
|
|
47
60
|
|
|
@@ -75,11 +88,11 @@ gpu_runtime:
|
|
|
75
88
|
|
|
76
89
|
A full example is [`jambu.example.yaml`](jambu.example.yaml). As a last line of defense, `gpu ls` shows every machine billing in the account and `gpu sweep --install-timer` guards every project on the machine and cleans up orphans. See [Keeping the account clean](docs/USAGE.md#keeping-the-account-clean).
|
|
77
90
|
|
|
78
|
-
|
|
91
|
+
`gpu login` saves the API key in `~/.gpu/credentials` (mode 600) after checking it; the environment and a project `.env` work too. It never goes in the manifest, and the agent never sees it.
|
|
79
92
|
|
|
80
93
|
## Docs
|
|
81
94
|
|
|
82
|
-
- [Letting a coding agent rent GPUs (Claude Code, Cursor, Claude Desktop)](docs/AGENTS.md)
|
|
95
|
+
- [Letting a coding agent rent GPUs (Claude Code, Codex, Gemini CLI, Cursor, Claude Desktop)](docs/AGENTS.md)
|
|
83
96
|
- [Integrating into an application (any language)](docs/INTEGRATION.md)
|
|
84
97
|
- [Commands, workloads, and agents](docs/USAGE.md)
|
|
85
98
|
- [Every config field](docs/CONFIG.md)
|
|
@@ -61,6 +61,8 @@ It found two more bugs, both fixed and tested: Vast reports a stopping instance
|
|
|
61
61
|
|
|
62
62
|
On 2026-10-25 a headless Claude Code with no prior context (`claude -p`) was given a fresh directory holding only `gpu agent install`'s skill and `.mcp.json`, plus `VAST_API_KEY` in `.env`. It was asked in one sentence to test Qwen2.5-0.5B on a rented GPU, ask it the capital of Brazil, report time and cost, and destroy the machine. It loaded the `gpu` skill by itself and called `gpu_init` (with the cap and VRAM from the request), `gpu_validate`, `gpu_offers`, then `gpu_setup` until ready, `curl` for the question, `gpu_status`, `gpu_destroy` and `gpu_ls`. The model answered "Brasília", the account was empty at the end, and the agent reported setup time and cost correctly, noting that `session_cost_usd` leaves out the replaced host.
|
|
63
63
|
|
|
64
|
+
On 2026-11-02 the self-install was checked the same way. In an empty home directory with no `uv`, `gpu` or Python tool on `PATH`, a headless Claude Code got one sentence: "Set up GPU for Agents for me. I'm using Claude Code. Fetch …/install.md with curl and follow its steps." It ran `install.sh --agent claude`, which put `gpu` 0.6.0 in `~/.local/bin`, the skill in `~/.claude/skills/gpu4agents/` and the `gpu` server in the user MCP config. `gpu login --check` exited 5, and the agent told the user to create a key and run `gpu login` in their own terminal, without asking for the key. It read the skill with `gpu agent skill` and rented nothing. Separately, `install.sh` run with `GPU4AGENTS_PYTHON=3.13` on a machine without Python 3.13 had uv download CPython 3.13 and install the tool on it.
|
|
65
|
+
|
|
64
66
|
| What was checked | What happened |
|
|
65
67
|
| --- | --- |
|
|
66
68
|
| Setup longer than any tool timeout | `gpu_setup` returned `running` every 300 s for 42 minutes and re-attached to the same background job each time: one machine at a time, never a second setup. |
|
|
@@ -1,31 +1,57 @@
|
|
|
1
1
|
# Letting a coding agent rent GPUs
|
|
2
2
|
|
|
3
|
-
`gpu` ships a skill and a local MCP server, so Claude Code, Cursor or Claude Desktop can rent a GPU, serve a model, run an experiment against it and shut it down, from a request like "compare Qwen 7B and Llama 8B on these 50 prompts".
|
|
3
|
+
`gpu` ships a skill and a local MCP server, so Claude Code, Codex, Gemini CLI, Cursor or Claude Desktop can rent a GPU, serve a model, run an experiment against it and shut it down, from a request like "compare Qwen 7B and Llama 8B on these 50 prompts".
|
|
4
4
|
|
|
5
5
|
The watchdog on the machine is what makes this safe to hand to an agent: a machine the agent forgets, or a conversation that ends mid-experiment, stops by itself after `lifecycle.idle_timeout`.
|
|
6
6
|
|
|
7
|
-
## Install
|
|
7
|
+
## Install: let the agent do it
|
|
8
|
+
|
|
9
|
+
Paste into the agent, naming it:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
Set up GPU for Agents for me. I'm using Codex. Fetch https://gpu4agents.jambu.ai/install.md with curl and follow its steps.
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
[`INSTALL.md`](../INSTALL.md) (served at `/install.md`) tells the agent to run [`install.sh`](../install.sh) with `--agent <runtime>`, check the account with `gpu login --check`, send the user to `gpu login` if there is no key, read the skill with `gpu agent skill`, and stop there: nothing is rented during installation.
|
|
16
|
+
|
|
17
|
+
`install.sh` needs only `curl` on Linux or macOS (Windows: WSL). It installs [uv](https://docs.astral.sh/uv/) if missing and runs `uv tool install --upgrade --python 3.12 gpu4agents`, so uv downloads a Python when the machine has no suitable one. It removes an old `jambu-gpu` tool, then runs `gpu agent install -t <runtime> --global` (`auto` by default: every runtime it finds). Options: `--agent claude|codex|gemini|cursor|auto|none`, `--no-modify-path`; `GPU4AGENTS_SPEC` installs a pinned version or a wheel.
|
|
18
|
+
|
|
19
|
+
With the [skills](https://skills.sh) CLI, `npx skills add jambuai/gpu4agents` installs only the skill (from [`skills/gpu4agents/`](../skills/gpu4agents/SKILL.md)); its first section installs the `gpu` command when it is missing.
|
|
20
|
+
|
|
21
|
+
## Connect the account: `gpu login`
|
|
22
|
+
|
|
23
|
+
The user, in their own terminal:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
gpu login # hidden prompt; checks the key against Vast.ai, then saves it
|
|
27
|
+
gpu login --check # exit 0 and where the key was found, or exit 5
|
|
28
|
+
echo "$KEY" | gpu login --stdin
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The key goes to `~/.gpu/credentials` (mode 600, directory 700); other lines in the file are kept. A key in the environment or a project `.env` still works and wins over the file. The skill tells the agent never to ask for the key in chat, read those files, or print it.
|
|
32
|
+
|
|
33
|
+
## Install by hand
|
|
8
34
|
|
|
9
35
|
```bash
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
gpu agent install # Claude Code
|
|
14
|
-
gpu agent install -t cursor # Cursor
|
|
15
|
-
gpu agent install -t desktop # Claude Desktop
|
|
36
|
+
uv tool install --python 3.12 gpu4agents # or: pipx install gpu4agents
|
|
37
|
+
gpu agent install -t claude # this project; add --global for every project
|
|
38
|
+
gpu agent install -t auto --global # every runtime on this machine
|
|
16
39
|
```
|
|
17
40
|
|
|
18
41
|
| Target | Skill | MCP server |
|
|
19
42
|
| --- | --- | --- |
|
|
20
|
-
| `claude`
|
|
21
|
-
| `
|
|
43
|
+
| `claude` | `.claude/skills/gpu4agents/SKILL.md` | `.mcp.json` (Claude Code asks once to approve it); `--global`: `claude mcp add --scope user` |
|
|
44
|
+
| `codex` | `.agents/skills/gpu4agents/SKILL.md` | `[mcp_servers.gpu]` in `$CODEX_HOME/config.toml` (`~/.codex`), always user-wide |
|
|
45
|
+
| `gemini` | `.agents/skills/gpu4agents/SKILL.md` | `mcpServers.gpu` in `.gemini/settings.json` |
|
|
46
|
+
| `cursor` | `.agents/skills/gpu4agents/SKILL.md` | `.cursor/mcp.json`, with `--project ${workspaceFolder}` |
|
|
22
47
|
| `desktop` | `~/.gpu/gpu-skill.zip`, to upload under Settings > Capabilities > Skills | `claude_desktop_config.json`, pinned to this project |
|
|
48
|
+
| `auto` | every runtime found: its command on `PATH` or its config directory | |
|
|
23
49
|
|
|
24
|
-
|
|
50
|
+
`.agents/skills/` is the shared directory Codex, Gemini CLI and Cursor all read. `--global` puts skills and settings under the home directory instead of the project. Existing config files are merged; only the `gpu` entry is written, and a skill installed by an older version under the name `gpu` is replaced. Re-running is safe. Restart the agent afterwards so it loads the skill and the tools.
|
|
25
51
|
|
|
26
|
-
Claude Desktop and Cursor do not get your shell's environment. Keep `VAST_API_KEY` in the project `.env
|
|
52
|
+
Claude Desktop and Cursor do not get your shell's environment. Keep `VAST_API_KEY` in `~/.gpu/credentials` (`gpu login`) or the project `.env`, not only in an `export`.
|
|
27
53
|
|
|
28
|
-
Another agent that reads `SKILL.md` files: `gpu agent skill > path/to/skills/
|
|
54
|
+
Another agent that reads `SKILL.md` files: `gpu agent skill > path/to/skills/gpu4agents/SKILL.md`. Any MCP client: run `gpu mcp --project /path/to/project` over stdio.
|
|
29
55
|
|
|
30
56
|
## The MCP tools
|
|
31
57
|
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
"""`gpu agent install`: put the skill and the MCP server where agents look for them.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Skills follow the open Agent Skills layout, one folder with a SKILL.md:
|
|
4
|
+
|
|
5
|
+
- claude: ``.claude/skills/gpu4agents/`` (Claude Code; Cursor reads it too)
|
|
6
|
+
- codex, gemini, cursor: ``.agents/skills/gpu4agents/``, the shared directory
|
|
7
|
+
Codex, Gemini CLI and Cursor all read
|
|
8
|
+
|
|
9
|
+
Each is per project, or under the home directory with --global. The MCP server
|
|
10
|
+
is registered per client:
|
|
6
11
|
|
|
7
12
|
- claude: ``.mcp.json`` in the project; with --global, ``claude mcp add -s user``
|
|
13
|
+
- codex: ``[mcp_servers.gpu]`` in ``$CODEX_HOME/config.toml`` (``~/.codex``)
|
|
14
|
+
- gemini: ``mcpServers.gpu`` in ``.gemini/settings.json``, or ``~/.gemini/settings.json``
|
|
8
15
|
- cursor: ``.cursor/mcp.json`` in the project, or ``~/.cursor/mcp.json``
|
|
9
16
|
- desktop: Claude Desktop's ``claude_desktop_config.json``, pinned to this
|
|
10
17
|
project, plus a zip of the skill to upload in Settings
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
entry is written.
|
|
19
|
+
``auto`` picks every runtime found on this machine. Existing config files are
|
|
20
|
+
merged, never replaced: only the ``gpu`` server entry is written.
|
|
14
21
|
"""
|
|
15
22
|
|
|
16
23
|
from __future__ import annotations
|
|
@@ -18,6 +25,7 @@ from __future__ import annotations
|
|
|
18
25
|
import json
|
|
19
26
|
import os
|
|
20
27
|
import platform
|
|
28
|
+
import re
|
|
21
29
|
import shutil
|
|
22
30
|
import subprocess
|
|
23
31
|
import sys
|
|
@@ -29,8 +37,10 @@ from ..core.errors import ConfigError
|
|
|
29
37
|
from ..core.paths import gpu_home
|
|
30
38
|
|
|
31
39
|
SERVER_NAME = "gpu"
|
|
32
|
-
SKILL_NAME = "
|
|
33
|
-
|
|
40
|
+
SKILL_NAME = "gpu4agents"
|
|
41
|
+
LEGACY_SKILL_NAME = "gpu"
|
|
42
|
+
TARGETS = ("claude", "codex", "gemini", "cursor", "desktop")
|
|
43
|
+
SHARED_SKILL_TARGETS = ("codex", "gemini", "cursor")
|
|
34
44
|
|
|
35
45
|
|
|
36
46
|
def skill_source() -> Path:
|
|
@@ -66,6 +76,25 @@ def desktop_config_path() -> Path:
|
|
|
66
76
|
return (Path(base) if base else Path.home() / ".config") / "Claude" / "claude_desktop_config.json"
|
|
67
77
|
|
|
68
78
|
|
|
79
|
+
def codex_home() -> Path:
|
|
80
|
+
return Path(os.environ.get("CODEX_HOME") or Path.home() / ".codex")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def detect_targets() -> list[str]:
|
|
84
|
+
"""Runtimes present on this machine: their command on PATH or their config directory."""
|
|
85
|
+
home = Path.home()
|
|
86
|
+
found = []
|
|
87
|
+
if shutil.which("claude") or (home / ".claude").is_dir():
|
|
88
|
+
found.append("claude")
|
|
89
|
+
if shutil.which("codex") or codex_home().is_dir():
|
|
90
|
+
found.append("codex")
|
|
91
|
+
if shutil.which("gemini") or (home / ".gemini").is_dir():
|
|
92
|
+
found.append("gemini")
|
|
93
|
+
if shutil.which("cursor") or shutil.which("cursor-agent") or (home / ".cursor").is_dir():
|
|
94
|
+
found.append("cursor")
|
|
95
|
+
return found
|
|
96
|
+
|
|
97
|
+
|
|
69
98
|
def merge_server(path: Path, entry: dict[str, Any]) -> str:
|
|
70
99
|
"""Write ``mcpServers.gpu`` into a JSON config. Returns created/updated/unchanged."""
|
|
71
100
|
data: dict[str, Any] = {}
|
|
@@ -90,8 +119,60 @@ def merge_server(path: Path, entry: dict[str, Any]) -> str:
|
|
|
90
119
|
return action
|
|
91
120
|
|
|
92
121
|
|
|
122
|
+
_TOML_TABLE = re.compile(r"^\s*\[\[?[^\]]+\]\]?\s*(#.*)?$")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def merge_codex_server(path: Path, command: list[str]) -> str:
|
|
126
|
+
"""Write ``[mcp_servers.gpu]`` into Codex's config.toml, keeping everything else.
|
|
127
|
+
|
|
128
|
+
Python 3.10 has no TOML parser, and only one table is ours, so the table is
|
|
129
|
+
replaced as text: from its header to the next table header.
|
|
130
|
+
"""
|
|
131
|
+
header = f"[mcp_servers.{SERVER_NAME}]"
|
|
132
|
+
block = [
|
|
133
|
+
header,
|
|
134
|
+
f"command = {json.dumps(command[0])}",
|
|
135
|
+
f"args = {json.dumps([*command[1:], 'mcp'])}",
|
|
136
|
+
]
|
|
137
|
+
lines = path.read_text().splitlines() if path.is_file() else []
|
|
138
|
+
start = next((i for i, line in enumerate(lines) if line.strip() == header), None)
|
|
139
|
+
if start is None:
|
|
140
|
+
while lines and not lines[-1].strip():
|
|
141
|
+
lines.pop()
|
|
142
|
+
new = lines + ([""] if lines else []) + block
|
|
143
|
+
action = "created"
|
|
144
|
+
else:
|
|
145
|
+
end = next(
|
|
146
|
+
(i for i in range(start + 1, len(lines)) if _TOML_TABLE.match(lines[i])),
|
|
147
|
+
len(lines),
|
|
148
|
+
)
|
|
149
|
+
old = [line for line in lines[start:end] if line.strip()]
|
|
150
|
+
if old == block:
|
|
151
|
+
return "unchanged"
|
|
152
|
+
tail = lines[end:]
|
|
153
|
+
new = lines[:start] + block + ([""] + tail if tail else [])
|
|
154
|
+
action = "updated"
|
|
155
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
156
|
+
path.write_text("\n".join(new) + "\n")
|
|
157
|
+
return action
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _is_our_legacy_skill(path: Path) -> bool:
|
|
161
|
+
"""The skill shipped before 0.6.0 under the name ``gpu``."""
|
|
162
|
+
try:
|
|
163
|
+
text = path.read_text()
|
|
164
|
+
except OSError:
|
|
165
|
+
return False
|
|
166
|
+
return text.startswith(f"---\nname: {LEGACY_SKILL_NAME}\n") and "gpu_setup" in text
|
|
167
|
+
|
|
168
|
+
|
|
93
169
|
def write_skill(directory: Path) -> str:
|
|
94
170
|
target = directory / SKILL_NAME / "SKILL.md"
|
|
171
|
+
legacy = directory / LEGACY_SKILL_NAME / "SKILL.md"
|
|
172
|
+
if _is_our_legacy_skill(legacy):
|
|
173
|
+
legacy.unlink()
|
|
174
|
+
if not any(legacy.parent.iterdir()):
|
|
175
|
+
legacy.parent.rmdir()
|
|
95
176
|
text = skill_text()
|
|
96
177
|
if target.is_file() and target.read_text() == text:
|
|
97
178
|
return "unchanged"
|
|
@@ -109,6 +190,21 @@ def write_skill_zip(path: Path) -> str:
|
|
|
109
190
|
return "created"
|
|
110
191
|
|
|
111
192
|
|
|
193
|
+
def resolve_targets(targets: list[str]) -> list[str]:
|
|
194
|
+
wanted: list[str] = []
|
|
195
|
+
for target in targets:
|
|
196
|
+
expanded = detect_targets() if target == "auto" else [target]
|
|
197
|
+
for name in expanded:
|
|
198
|
+
if name not in wanted:
|
|
199
|
+
wanted.append(name)
|
|
200
|
+
unknown = [t for t in wanted if t not in TARGETS]
|
|
201
|
+
if unknown:
|
|
202
|
+
raise ConfigError(
|
|
203
|
+
f"unknown target {', '.join(unknown)} (choose from {', '.join(TARGETS)}, or auto)"
|
|
204
|
+
)
|
|
205
|
+
return wanted
|
|
206
|
+
|
|
207
|
+
|
|
112
208
|
def install(
|
|
113
209
|
targets: list[str],
|
|
114
210
|
project_dir: Path,
|
|
@@ -116,16 +212,27 @@ def install(
|
|
|
116
212
|
run=subprocess.run,
|
|
117
213
|
) -> dict[str, Any]:
|
|
118
214
|
"""Install for each target. ``run`` is a seam for tests (``claude mcp add``)."""
|
|
119
|
-
|
|
120
|
-
if unknown:
|
|
121
|
-
raise ConfigError(f"unknown target {', '.join(unknown)} (choose from {', '.join(TARGETS)})")
|
|
215
|
+
targets = resolve_targets(targets)
|
|
122
216
|
project_dir = project_dir.resolve()
|
|
217
|
+
base = Path.home() if global_scope else project_dir
|
|
123
218
|
done: list[dict[str, Any]] = []
|
|
124
219
|
notes: list[str] = []
|
|
220
|
+
if not targets:
|
|
221
|
+
notes.append(
|
|
222
|
+
"no agent runtime found (Claude Code, Codex, Gemini CLI, Cursor); "
|
|
223
|
+
"name one with -t claude|codex|gemini|cursor|desktop"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if "claude" in targets:
|
|
227
|
+
skills = base / ".claude" / "skills"
|
|
228
|
+
done.append({"what": "skill (Claude Code)", "path": str(skills / SKILL_NAME / "SKILL.md"), "action": write_skill(skills)})
|
|
125
229
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
230
|
+
shared = [t for t in targets if t in SHARED_SKILL_TARGETS]
|
|
231
|
+
if shared:
|
|
232
|
+
skills = base / ".agents" / "skills"
|
|
233
|
+
names = {"codex": "Codex", "gemini": "Gemini CLI", "cursor": "Cursor"}
|
|
234
|
+
label = ", ".join(names[t] for t in shared)
|
|
235
|
+
done.append({"what": f"skill ({label})", "path": str(skills / SKILL_NAME / "SKILL.md"), "action": write_skill(skills)})
|
|
129
236
|
|
|
130
237
|
if "claude" in targets:
|
|
131
238
|
if global_scope:
|
|
@@ -147,6 +254,18 @@ def install(
|
|
|
147
254
|
done.append({"what": "MCP server (Claude Code)", "path": str(path), "action": merge_server(path, entry)})
|
|
148
255
|
notes.append("Claude Code asks once to approve the project's .mcp.json servers")
|
|
149
256
|
|
|
257
|
+
if "codex" in targets:
|
|
258
|
+
# Codex reads MCP servers from the user config; the server works in
|
|
259
|
+
# whatever directory Codex runs, so one entry serves every project.
|
|
260
|
+
path = codex_home() / "config.toml"
|
|
261
|
+
done.append({"what": "MCP server (Codex)", "path": str(path), "action": merge_codex_server(path, gpu_command(portable=False))})
|
|
262
|
+
|
|
263
|
+
if "gemini" in targets:
|
|
264
|
+
command = gpu_command(portable=not global_scope)
|
|
265
|
+
entry = {"command": command[0], "args": [*command[1:], "mcp"]}
|
|
266
|
+
path = base / ".gemini" / "settings.json"
|
|
267
|
+
done.append({"what": "MCP server (Gemini CLI)", "path": str(path), "action": merge_server(path, entry)})
|
|
268
|
+
|
|
150
269
|
if "cursor" in targets:
|
|
151
270
|
command = gpu_command(portable=not global_scope)
|
|
152
271
|
entry = {
|
|
@@ -154,7 +273,7 @@ def install(
|
|
|
154
273
|
"command": command[0],
|
|
155
274
|
"args": [*command[1:], "mcp", "--project", "${workspaceFolder}"],
|
|
156
275
|
}
|
|
157
|
-
path =
|
|
276
|
+
path = base / ".cursor" / "mcp.json"
|
|
158
277
|
done.append({"what": "MCP server (Cursor)", "path": str(path), "action": merge_server(path, entry)})
|
|
159
278
|
|
|
160
279
|
if "desktop" in targets:
|
|
@@ -166,8 +285,10 @@ def install(
|
|
|
166
285
|
done.append({"what": "skill zip (Claude Desktop)", "path": str(archive), "action": write_skill_zip(archive)})
|
|
167
286
|
notes.append(f"Claude Desktop: restart it, then upload {archive} under Settings > Capabilities > Skills")
|
|
168
287
|
notes.append(
|
|
169
|
-
"Claude Desktop does not get your shell's environment: keep VAST_API_KEY in
|
|
170
|
-
"project .env
|
|
288
|
+
"Claude Desktop does not get your shell's environment: keep VAST_API_KEY in "
|
|
289
|
+
"~/.gpu/credentials (gpu login) or the project .env"
|
|
171
290
|
)
|
|
172
291
|
|
|
173
|
-
|
|
292
|
+
if any(t in targets for t in ("claude", "codex", "gemini", "cursor")):
|
|
293
|
+
notes.append("restart the agent (or start a new session) so it loads the skill and the gpu tools")
|
|
294
|
+
return {"installed": done, "targets": targets, "notes": notes}
|