vastly 0.2.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.
- vastly-0.2.0/.gitignore +14 -0
- vastly-0.2.0/PKG-INFO +142 -0
- vastly-0.2.0/README.md +120 -0
- vastly-0.2.0/pyproject.toml +35 -0
- vastly-0.2.0/src/vastly/__init__.py +1 -0
- vastly-0.2.0/src/vastly/__main__.py +6 -0
- vastly-0.2.0/src/vastly/cli.py +124 -0
- vastly-0.2.0/src/vastly/config.py +61 -0
- vastly-0.2.0/src/vastly/data/.vastly.template.json +13 -0
- vastly-0.2.0/src/vastly/data/__init__.py +0 -0
- vastly-0.2.0/src/vastly/data/setup-remote.sh +253 -0
- vastly-0.2.0/src/vastly/ide.py +20 -0
- vastly-0.2.0/src/vastly/instance.py +212 -0
- vastly-0.2.0/src/vastly/remote.py +117 -0
- vastly-0.2.0/src/vastly/ssh.py +120 -0
- vastly-0.2.0/tests/test_vastly.py +449 -0
vastly-0.2.0/.gitignore
ADDED
vastly-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vastly
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Connect to Vast.ai GPU instances -- sync SSH configs, set up your project, and open your IDE.
|
|
5
|
+
Project-URL: Repository, https://github.com/seamusfallows/vastly
|
|
6
|
+
Project-URL: Issues, https://github.com/seamusfallows/vastly/issues
|
|
7
|
+
Author: Seamus Fallows
|
|
8
|
+
Keywords: gpu,remote-development,ssh,vast.ai
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Vastly
|
|
24
|
+
|
|
25
|
+
Connect to Vast.ai GPU instances from your terminal: sync SSH configs, set up your project remotely, and open your IDE in one command.
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
|
|
29
|
+
- Python 3.9+
|
|
30
|
+
- [Vast.ai CLI](https://vast.ai/docs/cli/getting-started) (`pip install vastai`) with API key configured
|
|
31
|
+
- Git
|
|
32
|
+
- SSH
|
|
33
|
+
- [VS Code](https://code.visualstudio.com) or [Cursor](https://cursor.com) with the Remote-SSH extension
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
pip install vastly
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
cd your-project # any local git repo
|
|
45
|
+
vst # checks setup -> opens IDE (sets up on first run)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
vst 1xRTX4090-TW # target a specific instance by name
|
|
50
|
+
vst --no-setup # open IDE on the remote without cloning or installing anything
|
|
51
|
+
vst --version # show version
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## How It Works
|
|
55
|
+
|
|
56
|
+
### 1. Sync
|
|
57
|
+
|
|
58
|
+
Calls the Vast.ai API and writes an SSH config for each running instance to `~/.ssh/vast.d/`. On first run, adds `Include vast.d/*` to `~/.ssh/config`.
|
|
59
|
+
|
|
60
|
+
Instances are named by GPU and region (e.g. `1xRTX4090-TW`, `2xA100-US`). Duplicates get the instance ID appended (`1xRTX4090-TW-12345`).
|
|
61
|
+
|
|
62
|
+
### 2. Select
|
|
63
|
+
|
|
64
|
+
One instance is selected automatically. Multiple instances prompt you to pick one or select all. You can also pass the name directly: `vst 1xRTX4090-TW`.
|
|
65
|
+
|
|
66
|
+
### 3. Setup (first run only)
|
|
67
|
+
|
|
68
|
+
For each selected instance, `vst` checks if the project has already been set up by looking for a marker file at `~/.vastly/setup/<repo>.json` on the instance. If the marker exists, it skips straight to opening the IDE.
|
|
69
|
+
|
|
70
|
+
On first run (no marker), `vst` reads the remote URL from your local git repo, copies a setup script to the instance, and runs it.
|
|
71
|
+
|
|
72
|
+
The setup script ([setup-remote.sh](src/vastly/data/setup-remote.sh)):
|
|
73
|
+
|
|
74
|
+
- Disables auto-tmux (on by default, configurable)
|
|
75
|
+
- Configures git identity from your local `git config`
|
|
76
|
+
- Adds `github.com` to SSH known hosts
|
|
77
|
+
- Clones your repo into the workspace
|
|
78
|
+
- Installs Python dependencies (auto-detected)
|
|
79
|
+
- Runs any configured post-install commands
|
|
80
|
+
- Writes VS Code settings and patches `.bashrc`
|
|
81
|
+
- Writes a setup marker (`~/.vastly/setup/<repo>.json`) so setup is skipped next time
|
|
82
|
+
|
|
83
|
+
**Dependency auto-detection** (checked in order):
|
|
84
|
+
|
|
85
|
+
1. `uv.lock` or `[tool.uv]` in pyproject.toml -- `uv sync` (installs uv if needed)
|
|
86
|
+
2. `[project]` in pyproject.toml -- `pip install -e .`
|
|
87
|
+
3. `requirements*.txt` -- `pip install -r` for each file
|
|
88
|
+
4. `setup.py` -- `pip install -e .`
|
|
89
|
+
|
|
90
|
+
Override with `installCommand` in your config.
|
|
91
|
+
|
|
92
|
+
### 4. Open
|
|
93
|
+
|
|
94
|
+
Launches your IDE via Remote-SSH at the project directory. If already open, focuses the existing window.
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
On first run, `vst` creates `~/.vastly.json` with defaults:
|
|
99
|
+
|
|
100
|
+
```jsonc
|
|
101
|
+
{
|
|
102
|
+
// "code" (VS Code) or "cursor"
|
|
103
|
+
"ide": "code",
|
|
104
|
+
|
|
105
|
+
// Path to SSH private key. null = use your SSH config or ssh-agent
|
|
106
|
+
"sshKeyPath": null,
|
|
107
|
+
|
|
108
|
+
// SSH user on remote instances
|
|
109
|
+
"sshUser": "root",
|
|
110
|
+
|
|
111
|
+
// Ports to forward to localhost. Set to [] to disable
|
|
112
|
+
// Local ports auto-increment when multiple instances are running
|
|
113
|
+
"portForwards": [
|
|
114
|
+
{ "local": 8080, "remote": 8080 }
|
|
115
|
+
],
|
|
116
|
+
|
|
117
|
+
// Remote directory where projects are cloned
|
|
118
|
+
"workspace": "/workspace",
|
|
119
|
+
|
|
120
|
+
// Creates ~/.no_auto_tmux to prevent auto-tmux on Vast images
|
|
121
|
+
"disableAutoTmux": true,
|
|
122
|
+
|
|
123
|
+
// Which git remote to read the repo URL from
|
|
124
|
+
"gitRemote": "origin",
|
|
125
|
+
|
|
126
|
+
// Commands to run after dependency install
|
|
127
|
+
// e.g. ["curl -fsSL https://claude.ai/install.sh | bash"]
|
|
128
|
+
"postInstall": [],
|
|
129
|
+
|
|
130
|
+
// Override auto-detected install method. null = auto-detect
|
|
131
|
+
// e.g. "uv sync", "pip install -e '.[dev]'", "conda env update -f environment.yml"
|
|
132
|
+
"installCommand": null
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Troubleshooting
|
|
137
|
+
|
|
138
|
+
**"Missing: vastai CLI"** -- `pip install vastai`, then `vastai set api-key <key>`.
|
|
139
|
+
|
|
140
|
+
**SSH connection timeout** -- Instance may still be booting. Setup retries 3 times. Run `vastai show instances` to check status.
|
|
141
|
+
|
|
142
|
+
**"Not in a git repo"** -- `vst` reads the remote URL from your local repo. Run from inside a git repo, or use `--no-setup`.
|
vastly-0.2.0/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Vastly
|
|
2
|
+
|
|
3
|
+
Connect to Vast.ai GPU instances from your terminal: sync SSH configs, set up your project remotely, and open your IDE in one command.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Python 3.9+
|
|
8
|
+
- [Vast.ai CLI](https://vast.ai/docs/cli/getting-started) (`pip install vastai`) with API key configured
|
|
9
|
+
- Git
|
|
10
|
+
- SSH
|
|
11
|
+
- [VS Code](https://code.visualstudio.com) or [Cursor](https://cursor.com) with the Remote-SSH extension
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pip install vastly
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
cd your-project # any local git repo
|
|
23
|
+
vst # checks setup -> opens IDE (sets up on first run)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
vst 1xRTX4090-TW # target a specific instance by name
|
|
28
|
+
vst --no-setup # open IDE on the remote without cloning or installing anything
|
|
29
|
+
vst --version # show version
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## How It Works
|
|
33
|
+
|
|
34
|
+
### 1. Sync
|
|
35
|
+
|
|
36
|
+
Calls the Vast.ai API and writes an SSH config for each running instance to `~/.ssh/vast.d/`. On first run, adds `Include vast.d/*` to `~/.ssh/config`.
|
|
37
|
+
|
|
38
|
+
Instances are named by GPU and region (e.g. `1xRTX4090-TW`, `2xA100-US`). Duplicates get the instance ID appended (`1xRTX4090-TW-12345`).
|
|
39
|
+
|
|
40
|
+
### 2. Select
|
|
41
|
+
|
|
42
|
+
One instance is selected automatically. Multiple instances prompt you to pick one or select all. You can also pass the name directly: `vst 1xRTX4090-TW`.
|
|
43
|
+
|
|
44
|
+
### 3. Setup (first run only)
|
|
45
|
+
|
|
46
|
+
For each selected instance, `vst` checks if the project has already been set up by looking for a marker file at `~/.vastly/setup/<repo>.json` on the instance. If the marker exists, it skips straight to opening the IDE.
|
|
47
|
+
|
|
48
|
+
On first run (no marker), `vst` reads the remote URL from your local git repo, copies a setup script to the instance, and runs it.
|
|
49
|
+
|
|
50
|
+
The setup script ([setup-remote.sh](src/vastly/data/setup-remote.sh)):
|
|
51
|
+
|
|
52
|
+
- Disables auto-tmux (on by default, configurable)
|
|
53
|
+
- Configures git identity from your local `git config`
|
|
54
|
+
- Adds `github.com` to SSH known hosts
|
|
55
|
+
- Clones your repo into the workspace
|
|
56
|
+
- Installs Python dependencies (auto-detected)
|
|
57
|
+
- Runs any configured post-install commands
|
|
58
|
+
- Writes VS Code settings and patches `.bashrc`
|
|
59
|
+
- Writes a setup marker (`~/.vastly/setup/<repo>.json`) so setup is skipped next time
|
|
60
|
+
|
|
61
|
+
**Dependency auto-detection** (checked in order):
|
|
62
|
+
|
|
63
|
+
1. `uv.lock` or `[tool.uv]` in pyproject.toml -- `uv sync` (installs uv if needed)
|
|
64
|
+
2. `[project]` in pyproject.toml -- `pip install -e .`
|
|
65
|
+
3. `requirements*.txt` -- `pip install -r` for each file
|
|
66
|
+
4. `setup.py` -- `pip install -e .`
|
|
67
|
+
|
|
68
|
+
Override with `installCommand` in your config.
|
|
69
|
+
|
|
70
|
+
### 4. Open
|
|
71
|
+
|
|
72
|
+
Launches your IDE via Remote-SSH at the project directory. If already open, focuses the existing window.
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
On first run, `vst` creates `~/.vastly.json` with defaults:
|
|
77
|
+
|
|
78
|
+
```jsonc
|
|
79
|
+
{
|
|
80
|
+
// "code" (VS Code) or "cursor"
|
|
81
|
+
"ide": "code",
|
|
82
|
+
|
|
83
|
+
// Path to SSH private key. null = use your SSH config or ssh-agent
|
|
84
|
+
"sshKeyPath": null,
|
|
85
|
+
|
|
86
|
+
// SSH user on remote instances
|
|
87
|
+
"sshUser": "root",
|
|
88
|
+
|
|
89
|
+
// Ports to forward to localhost. Set to [] to disable
|
|
90
|
+
// Local ports auto-increment when multiple instances are running
|
|
91
|
+
"portForwards": [
|
|
92
|
+
{ "local": 8080, "remote": 8080 }
|
|
93
|
+
],
|
|
94
|
+
|
|
95
|
+
// Remote directory where projects are cloned
|
|
96
|
+
"workspace": "/workspace",
|
|
97
|
+
|
|
98
|
+
// Creates ~/.no_auto_tmux to prevent auto-tmux on Vast images
|
|
99
|
+
"disableAutoTmux": true,
|
|
100
|
+
|
|
101
|
+
// Which git remote to read the repo URL from
|
|
102
|
+
"gitRemote": "origin",
|
|
103
|
+
|
|
104
|
+
// Commands to run after dependency install
|
|
105
|
+
// e.g. ["curl -fsSL https://claude.ai/install.sh | bash"]
|
|
106
|
+
"postInstall": [],
|
|
107
|
+
|
|
108
|
+
// Override auto-detected install method. null = auto-detect
|
|
109
|
+
// e.g. "uv sync", "pip install -e '.[dev]'", "conda env update -f environment.yml"
|
|
110
|
+
"installCommand": null
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Troubleshooting
|
|
115
|
+
|
|
116
|
+
**"Missing: vastai CLI"** -- `pip install vastai`, then `vastai set api-key <key>`.
|
|
117
|
+
|
|
118
|
+
**SSH connection timeout** -- Instance may still be booting. Setup retries 3 times. Run `vastai show instances` to check status.
|
|
119
|
+
|
|
120
|
+
**"Not in a git repo"** -- `vst` reads the remote URL from your local repo. Run from inside a git repo, or use `--no-setup`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vastly"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Connect to Vast.ai GPU instances -- sync SSH configs, set up your project, and open your IDE."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
authors = [{ name = "Seamus Fallows" }]
|
|
12
|
+
keywords = ["vast.ai", "gpu", "ssh", "remote-development"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Repository = "https://github.com/seamusfallows/vastly"
|
|
29
|
+
Issues = "https://github.com/seamusfallows/vastly/issues"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
vst = "vastly.cli:main"
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["src/vastly"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""CLI entry point for the `vst` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
|
|
9
|
+
from vastly import __version__
|
|
10
|
+
from vastly.config import load_config
|
|
11
|
+
from vastly.ide import check_ide, open_ide
|
|
12
|
+
from vastly.instance import get_synced_instances, select_instance, show_table
|
|
13
|
+
from vastly.remote import convert_to_ssh_url, setup_instances
|
|
14
|
+
from vastly.ssh import run_ssh
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _check_prerequisites(*, need_ide: bool = False, ide: str) -> bool:
|
|
18
|
+
"""Verify required tools are available."""
|
|
19
|
+
ok = True
|
|
20
|
+
|
|
21
|
+
if not shutil.which("vastai"):
|
|
22
|
+
print("\033[31mMissing: vastai CLI. Install with: pip install vastai\033[0m")
|
|
23
|
+
ok = False
|
|
24
|
+
if not shutil.which("git"):
|
|
25
|
+
print("\033[31mMissing: git. Install from https://git-scm.com\033[0m")
|
|
26
|
+
ok = False
|
|
27
|
+
if not shutil.which("ssh"):
|
|
28
|
+
print("\033[31mMissing: ssh.\033[0m")
|
|
29
|
+
ok = False
|
|
30
|
+
if need_ide and not check_ide(ide):
|
|
31
|
+
urls = {"code": "https://code.visualstudio.com", "cursor": "https://cursor.com"}
|
|
32
|
+
url = urls.get(ide, "")
|
|
33
|
+
hint = f" Download from {url}" if url else ""
|
|
34
|
+
print(f"\033[31mMissing: {ide}.{hint}\033[0m")
|
|
35
|
+
ok = False
|
|
36
|
+
|
|
37
|
+
return ok
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _local_repo_info(git_remote: str) -> tuple[str, str] | None:
|
|
41
|
+
"""Return (repo_url, repo_name) from the local git repo, or None."""
|
|
42
|
+
try:
|
|
43
|
+
repo_url = subprocess.run(
|
|
44
|
+
["git", "remote", "get-url", git_remote],
|
|
45
|
+
capture_output=True, text=True,
|
|
46
|
+
).stdout.strip()
|
|
47
|
+
except FileNotFoundError:
|
|
48
|
+
return None
|
|
49
|
+
if not repo_url:
|
|
50
|
+
return None
|
|
51
|
+
repo_url = convert_to_ssh_url(repo_url)
|
|
52
|
+
repo_name = repo_url.rsplit("/", 1)[-1].removesuffix(".git")
|
|
53
|
+
return repo_url, repo_name
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _connect(name: str | None, no_setup: bool) -> None:
|
|
57
|
+
"""Main connect flow -- sync instances, check setup, run setup if needed, open IDE."""
|
|
58
|
+
config = load_config()
|
|
59
|
+
|
|
60
|
+
if not _check_prerequisites(need_ide=True, ide=config["ide"]):
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
instances = get_synced_instances(config)
|
|
64
|
+
if not instances:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
show_table(instances)
|
|
68
|
+
|
|
69
|
+
selected = select_instance(instances, name)
|
|
70
|
+
if not selected:
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
repo_info = _local_repo_info(config["gitRemote"])
|
|
74
|
+
|
|
75
|
+
for inst in selected:
|
|
76
|
+
inst_name = inst["name"]
|
|
77
|
+
|
|
78
|
+
if no_setup or not repo_info:
|
|
79
|
+
# Skip setup -- open workspace root
|
|
80
|
+
if not repo_info and not no_setup:
|
|
81
|
+
print(f" \033[33mNot in a git repo. Tip: run vst from inside a git repo to auto-setup.\033[0m")
|
|
82
|
+
print(f" \033[32mOpening {config['workspace']}\033[0m")
|
|
83
|
+
open_ide(config["ide"], inst_name, config["workspace"])
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
repo_url, repo_name = repo_info
|
|
87
|
+
remote_path = f"{config['workspace']}/{repo_name}"
|
|
88
|
+
|
|
89
|
+
# Check if project is already set up via marker file
|
|
90
|
+
result = run_ssh(inst_name, f"test -f ~/.vastly/setup/{repo_name}.json && echo done")
|
|
91
|
+
|
|
92
|
+
if result.returncode != 0:
|
|
93
|
+
print(f" \033[31m{inst_name}: unreachable via SSH.\033[0m")
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
if result.stdout.strip() == "done":
|
|
97
|
+
print(f" \033[32mOpening {remote_path}\033[0m")
|
|
98
|
+
open_ide(config["ide"], inst_name, remote_path)
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
# Not set up yet -- run setup
|
|
102
|
+
success = setup_instances([inst], repo_url, repo_name, config)
|
|
103
|
+
if success:
|
|
104
|
+
open_ide(config["ide"], inst_name, remote_path)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def main() -> None:
|
|
108
|
+
"""Parse arguments and run the connect flow."""
|
|
109
|
+
parser = argparse.ArgumentParser(
|
|
110
|
+
prog="vst",
|
|
111
|
+
description="Connect to a Vast.ai instance -- sync SSH, set up your project, and open your IDE.",
|
|
112
|
+
epilog=(
|
|
113
|
+
"prerequisites: vastai CLI (pip install vastai), git, ssh, VS Code or Cursor\n"
|
|
114
|
+
"config: ~/.vastly.json (created on first run)\n"
|
|
115
|
+
"docs: https://github.com/seamusfallows/vastly"
|
|
116
|
+
),
|
|
117
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
118
|
+
)
|
|
119
|
+
parser.add_argument("name", nargs="?", help="instance name (tab-complete from ~/.ssh/vast.d/)")
|
|
120
|
+
parser.add_argument("--no-setup", action="store_true", help="open IDE without cloning or installing")
|
|
121
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
122
|
+
|
|
123
|
+
args = parser.parse_args()
|
|
124
|
+
_connect(args.name, args.no_setup)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Load and manage ~/.vastly.json configuration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from importlib import resources
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
CONFIG_PATH = Path.home() / ".vastly.json"
|
|
11
|
+
|
|
12
|
+
DEFAULTS = {
|
|
13
|
+
"ide": "code",
|
|
14
|
+
"sshUser": "root",
|
|
15
|
+
"workspace": "/workspace",
|
|
16
|
+
"disableAutoTmux": True,
|
|
17
|
+
"gitRemote": "origin",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_config(path: Path | None = None) -> dict:
|
|
22
|
+
"""Load config from disk, creating from template if missing.
|
|
23
|
+
|
|
24
|
+
Returns a dict with all keys populated (user values override defaults).
|
|
25
|
+
"""
|
|
26
|
+
path = path or CONFIG_PATH
|
|
27
|
+
|
|
28
|
+
if not path.exists():
|
|
29
|
+
template = resources.files("vastly.data").joinpath(".vastly.template.json")
|
|
30
|
+
path.write_text(template.read_text(encoding="utf-8"), encoding="utf-8")
|
|
31
|
+
print(f"Created config at {path}")
|
|
32
|
+
print("Edit it to change IDE, SSH key, port forwarding, and more.")
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
36
|
+
except json.JSONDecodeError as e:
|
|
37
|
+
print(f"\033[31mInvalid JSON in {path}: {e}\033[0m")
|
|
38
|
+
print("Fix the file or delete it to regenerate from template.")
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
port_forwards = raw.get("portForwards")
|
|
42
|
+
if port_forwards is None:
|
|
43
|
+
port_forwards = [{"local": 8080, "remote": 8080}]
|
|
44
|
+
|
|
45
|
+
post_install = raw.get("postInstall")
|
|
46
|
+
if isinstance(post_install, str):
|
|
47
|
+
post_install = [post_install]
|
|
48
|
+
elif not post_install:
|
|
49
|
+
post_install = []
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
"ide": raw.get("ide") or DEFAULTS["ide"],
|
|
53
|
+
"sshKeyPath": raw.get("sshKeyPath"),
|
|
54
|
+
"sshUser": raw.get("sshUser") or DEFAULTS["sshUser"],
|
|
55
|
+
"portForwards": list(port_forwards),
|
|
56
|
+
"workspace": raw.get("workspace") or DEFAULTS["workspace"],
|
|
57
|
+
"disableAutoTmux": raw.get("disableAutoTmux", DEFAULTS["disableAutoTmux"]),
|
|
58
|
+
"gitRemote": raw.get("gitRemote") or DEFAULTS["gitRemote"],
|
|
59
|
+
"postInstall": list(post_install),
|
|
60
|
+
"installCommand": raw.get("installCommand"),
|
|
61
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ide": "code",
|
|
3
|
+
"sshKeyPath": null,
|
|
4
|
+
"sshUser": "root",
|
|
5
|
+
"portForwards": [
|
|
6
|
+
{ "local": 8080, "remote": 8080 }
|
|
7
|
+
],
|
|
8
|
+
"workspace": "/workspace",
|
|
9
|
+
"disableAutoTmux": true,
|
|
10
|
+
"gitRemote": "origin",
|
|
11
|
+
"postInstall": [],
|
|
12
|
+
"installCommand": null
|
|
13
|
+
}
|
|
File without changes
|