substrate-setup 0.2.0__tar.gz → 0.2.2__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.
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/.gitignore +77 -74
- substrate_setup-0.2.2/PKG-INFO +69 -0
- substrate_setup-0.2.2/README.md +40 -0
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/pyproject.toml +62 -62
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/scripts/lint_no_app_import.sh +10 -10
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/scripts/regenerate_fallback_catalog.py +68 -68
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/__init__.py +6 -6
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/__main__.py +5 -5
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/agents/__init__.py +20 -20
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/agents/aider.py +37 -7
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/agents/base.py +56 -56
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/agents/cursor.py +90 -90
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/agents/hermes.py +50 -13
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/backup.py +32 -32
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/catalog.py +109 -109
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/cli.py +249 -211
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/credentials.py +82 -82
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/data/fallback_catalog.json +343 -343
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/markers.py +27 -27
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/agents/test_aider.py +54 -0
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/agents/test_cursor.py +87 -87
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/agents/test_hermes.py +69 -0
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/conftest.py +1 -1
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/test_backup.py +38 -38
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/test_catalog.py +95 -95
- substrate_setup-0.2.2/tests/test_cli.py +246 -0
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/test_credentials.py +80 -80
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/test_e2e.py +111 -111
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/test_markers.py +45 -45
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/uv.lock +1 -1
- substrate_setup-0.2.0/PKG-INFO +0 -40
- substrate_setup-0.2.0/README.md +0 -11
- substrate_setup-0.2.0/tests/test_cli.py +0 -118
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/substrate_setup/agents/continue_dev.py +0 -0
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/__init__.py +0 -0
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/agents/__init__.py +0 -0
- {substrate_setup-0.2.0 → substrate_setup-0.2.2}/tests/agents/test_continue_dev.py +0 -0
|
@@ -1,74 +1,77 @@
|
|
|
1
|
-
# Secrets
|
|
2
|
-
.env
|
|
3
|
-
.env.local
|
|
4
|
-
.env.*.local
|
|
5
|
-
*.secret
|
|
6
|
-
secrets/
|
|
7
|
-
credentials.txt
|
|
8
|
-
credentials.*.txt
|
|
9
|
-
|
|
10
|
-
# Python
|
|
11
|
-
__pycache__/
|
|
12
|
-
*.py[cod]
|
|
13
|
-
*$py.class
|
|
14
|
-
*.so
|
|
15
|
-
.Python
|
|
16
|
-
.venv/
|
|
17
|
-
venv/
|
|
18
|
-
env/
|
|
19
|
-
.pytest_cache/
|
|
20
|
-
.mypy_cache/
|
|
21
|
-
.ruff_cache/
|
|
22
|
-
*.egg-info/
|
|
23
|
-
dist/
|
|
24
|
-
build/
|
|
25
|
-
|
|
26
|
-
# Node / Next.js
|
|
27
|
-
node_modules/
|
|
28
|
-
.next/
|
|
29
|
-
.vercel/
|
|
30
|
-
.turbo/
|
|
31
|
-
|
|
32
|
-
# CodeGraph (local-only code knowledge graph index)
|
|
33
|
-
.codegraph/
|
|
34
|
-
out/
|
|
35
|
-
*.tsbuildinfo
|
|
36
|
-
.npm/
|
|
37
|
-
.yarn/
|
|
38
|
-
|
|
39
|
-
# IDE
|
|
40
|
-
.vscode/
|
|
41
|
-
.idea/
|
|
42
|
-
*.swp
|
|
43
|
-
*.swo
|
|
44
|
-
|
|
45
|
-
# OS
|
|
46
|
-
.DS_Store
|
|
47
|
-
Thumbs.db
|
|
48
|
-
desktop.ini
|
|
49
|
-
|
|
50
|
-
# Logs
|
|
51
|
-
*.log
|
|
52
|
-
logs/
|
|
53
|
-
|
|
54
|
-
# Local databases
|
|
55
|
-
*.db
|
|
56
|
-
*.sqlite
|
|
57
|
-
*.sqlite3
|
|
58
|
-
|
|
59
|
-
# Editors
|
|
60
|
-
*~
|
|
61
|
-
.#*
|
|
62
|
-
|
|
63
|
-
# Coverage
|
|
64
|
-
.coverage
|
|
65
|
-
htmlcov/
|
|
66
|
-
coverage.xml
|
|
67
|
-
*.cover
|
|
68
|
-
|
|
69
|
-
# Claude Code project state (if any project-local)
|
|
70
|
-
.claude/
|
|
71
|
-
|
|
72
|
-
# Misc
|
|
73
|
-
*.bak
|
|
74
|
-
*.tmp
|
|
1
|
+
# Secrets
|
|
2
|
+
.env
|
|
3
|
+
.env.local
|
|
4
|
+
.env.*.local
|
|
5
|
+
*.secret
|
|
6
|
+
secrets/
|
|
7
|
+
credentials.txt
|
|
8
|
+
credentials.*.txt
|
|
9
|
+
|
|
10
|
+
# Python
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*$py.class
|
|
14
|
+
*.so
|
|
15
|
+
.Python
|
|
16
|
+
.venv/
|
|
17
|
+
venv/
|
|
18
|
+
env/
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.mypy_cache/
|
|
21
|
+
.ruff_cache/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
dist/
|
|
24
|
+
build/
|
|
25
|
+
|
|
26
|
+
# Node / Next.js
|
|
27
|
+
node_modules/
|
|
28
|
+
.next/
|
|
29
|
+
.vercel/
|
|
30
|
+
.turbo/
|
|
31
|
+
|
|
32
|
+
# CodeGraph (local-only code knowledge graph index)
|
|
33
|
+
.codegraph/
|
|
34
|
+
out/
|
|
35
|
+
*.tsbuildinfo
|
|
36
|
+
.npm/
|
|
37
|
+
.yarn/
|
|
38
|
+
|
|
39
|
+
# IDE
|
|
40
|
+
.vscode/
|
|
41
|
+
.idea/
|
|
42
|
+
*.swp
|
|
43
|
+
*.swo
|
|
44
|
+
|
|
45
|
+
# OS
|
|
46
|
+
.DS_Store
|
|
47
|
+
Thumbs.db
|
|
48
|
+
desktop.ini
|
|
49
|
+
|
|
50
|
+
# Logs
|
|
51
|
+
*.log
|
|
52
|
+
logs/
|
|
53
|
+
|
|
54
|
+
# Local databases
|
|
55
|
+
*.db
|
|
56
|
+
*.sqlite
|
|
57
|
+
*.sqlite3
|
|
58
|
+
|
|
59
|
+
# Editors
|
|
60
|
+
*~
|
|
61
|
+
.#*
|
|
62
|
+
|
|
63
|
+
# Coverage
|
|
64
|
+
.coverage
|
|
65
|
+
htmlcov/
|
|
66
|
+
coverage.xml
|
|
67
|
+
*.cover
|
|
68
|
+
|
|
69
|
+
# Claude Code project state (if any project-local)
|
|
70
|
+
.claude/
|
|
71
|
+
|
|
72
|
+
# Misc
|
|
73
|
+
*.bak
|
|
74
|
+
*.tmp
|
|
75
|
+
|
|
76
|
+
# brainstorming companion working dir
|
|
77
|
+
.superpowers/
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: substrate-setup
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: One-shot local configurator for coding agents against a Substrate gateway
|
|
5
|
+
Project-URL: Homepage, https://github.com/FrankXiaA/substrate-solutions
|
|
6
|
+
Project-URL: Source, https://github.com/FrankXiaA/substrate-solutions/tree/main/substrate-api/substrate_setup
|
|
7
|
+
Project-URL: Issues, https://github.com/FrankXiaA/substrate-solutions/issues
|
|
8
|
+
Author: Substrate Solutions
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: aider,continue,gateway,hermes,llm,openai-compatible,substrate
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: httpx>=0.27
|
|
22
|
+
Requires-Dist: ruamel-yaml>=0.18
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: mypy<2,>=1.13; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff<1,>=0.7; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# substrate-setup
|
|
31
|
+
|
|
32
|
+
One-shot configurator that points local coding agents at a Substrate gateway.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
`substrate-setup` requires Python 3.12+. The installers below pick the right interpreter automatically — you don't need a Python 3.12 already on your machine:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Option A — uv (recommended; downloads Python 3.12 on demand if missing)
|
|
40
|
+
uv tool install substrate-setup
|
|
41
|
+
|
|
42
|
+
# Option B — pipx
|
|
43
|
+
pipx install substrate-setup
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Don't have `uv`? Install it once with:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh # macOS / Linux
|
|
50
|
+
# or on Windows:
|
|
51
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> **Why not `pip install substrate-setup`?** It works only if the `pip` on your PATH is bound to a Python 3.12+ interpreter. Anaconda's default `pip` (Python 3.9) is the common pitfall — PyPI hides every release from it with `Requires-Python >=3.12` and the error message is unhelpful. `uv` and `pipx` both create an isolated 3.12 venv for the tool, so they sidestep the issue entirely.
|
|
55
|
+
|
|
56
|
+
## Use
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
export SUBSTRATE_API_KEY="sk-substrate-..." # or be prompted
|
|
60
|
+
|
|
61
|
+
substrate-setup configure # detect installed agents and wire them up
|
|
62
|
+
substrate-setup verify # read-only: confirm everything points at the gateway
|
|
63
|
+
substrate-setup remove # strip the substrate-managed entries
|
|
64
|
+
substrate-setup --help
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Supported agents: `hermes`, `cursor`, `aider`, `continue`.
|
|
68
|
+
|
|
69
|
+
Subset with `--agents-only hermes,aider`. Preview without writing: `--dry-run`. Override the gateway base URL: `--base-url https://your-gateway.example.com`.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# substrate-setup
|
|
2
|
+
|
|
3
|
+
One-shot configurator that points local coding agents at a Substrate gateway.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
`substrate-setup` requires Python 3.12+. The installers below pick the right interpreter automatically — you don't need a Python 3.12 already on your machine:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Option A — uv (recommended; downloads Python 3.12 on demand if missing)
|
|
11
|
+
uv tool install substrate-setup
|
|
12
|
+
|
|
13
|
+
# Option B — pipx
|
|
14
|
+
pipx install substrate-setup
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Don't have `uv`? Install it once with:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh # macOS / Linux
|
|
21
|
+
# or on Windows:
|
|
22
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> **Why not `pip install substrate-setup`?** It works only if the `pip` on your PATH is bound to a Python 3.12+ interpreter. Anaconda's default `pip` (Python 3.9) is the common pitfall — PyPI hides every release from it with `Requires-Python >=3.12` and the error message is unhelpful. `uv` and `pipx` both create an isolated 3.12 venv for the tool, so they sidestep the issue entirely.
|
|
26
|
+
|
|
27
|
+
## Use
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
export SUBSTRATE_API_KEY="sk-substrate-..." # or be prompted
|
|
31
|
+
|
|
32
|
+
substrate-setup configure # detect installed agents and wire them up
|
|
33
|
+
substrate-setup verify # read-only: confirm everything points at the gateway
|
|
34
|
+
substrate-setup remove # strip the substrate-managed entries
|
|
35
|
+
substrate-setup --help
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Supported agents: `hermes`, `cursor`, `aider`, `continue`.
|
|
39
|
+
|
|
40
|
+
Subset with `--agents-only hermes,aider`. Preview without writing: `--dry-run`. Override the gateway base URL: `--base-url https://your-gateway.example.com`.
|
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
3
|
-
build-backend = "hatchling.build"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "substrate-setup"
|
|
7
|
-
version = "0.2.
|
|
8
|
-
description = "One-shot local configurator for coding agents against a Substrate gateway"
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.12"
|
|
11
|
-
authors = [{ name = "Substrate Solutions" }]
|
|
12
|
-
license = { text = "MIT" }
|
|
13
|
-
keywords = ["substrate", "llm", "gateway", "aider", "continue", "hermes", "openai-compatible"]
|
|
14
|
-
classifiers = [
|
|
15
|
-
"Development Status :: 4 - Beta",
|
|
16
|
-
"Environment :: Console",
|
|
17
|
-
"Intended Audience :: Developers",
|
|
18
|
-
"License :: OSI Approved :: MIT License",
|
|
19
|
-
"Operating System :: OS Independent",
|
|
20
|
-
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
-
"Programming Language :: Python :: 3.12",
|
|
22
|
-
"Topic :: Software Development :: Code Generators",
|
|
23
|
-
"Topic :: Utilities",
|
|
24
|
-
]
|
|
25
|
-
dependencies = [
|
|
26
|
-
"httpx>=0.27",
|
|
27
|
-
"ruamel.yaml>=0.18",
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
[project.urls]
|
|
31
|
-
Homepage = "https://github.com/FrankXiaA/substrate-solutions"
|
|
32
|
-
Source = "https://github.com/FrankXiaA/substrate-solutions/tree/main/substrate-api/substrate_setup"
|
|
33
|
-
Issues = "https://github.com/FrankXiaA/substrate-solutions/issues"
|
|
34
|
-
|
|
35
|
-
[project.optional-dependencies]
|
|
36
|
-
dev = [
|
|
37
|
-
"pytest>=8.0",
|
|
38
|
-
"pytest-httpx>=0.30",
|
|
39
|
-
"ruff>=0.7,<1",
|
|
40
|
-
"mypy>=1.13,<2",
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
[project.scripts]
|
|
44
|
-
substrate-setup = "substrate_setup.cli:main"
|
|
45
|
-
|
|
46
|
-
[tool.hatch.build.targets.wheel]
|
|
47
|
-
packages = ["substrate_setup"]
|
|
48
|
-
|
|
49
|
-
[tool.pytest.ini_options]
|
|
50
|
-
testpaths = ["tests"]
|
|
51
|
-
|
|
52
|
-
[tool.mypy]
|
|
53
|
-
python_version = "3.12"
|
|
54
|
-
strict = true
|
|
55
|
-
ignore_missing_imports = true
|
|
56
|
-
packages = ["substrate_setup"]
|
|
57
|
-
|
|
58
|
-
[[tool.mypy.overrides]]
|
|
59
|
-
module = "tests.*"
|
|
60
|
-
disallow_untyped_defs = false
|
|
61
|
-
disallow_incomplete_defs = false
|
|
62
|
-
check_untyped_defs = false
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "substrate-setup"
|
|
7
|
+
version = "0.2.2"
|
|
8
|
+
description = "One-shot local configurator for coding agents against a Substrate gateway"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
authors = [{ name = "Substrate Solutions" }]
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
keywords = ["substrate", "llm", "gateway", "aider", "continue", "hermes", "openai-compatible"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Software Development :: Code Generators",
|
|
23
|
+
"Topic :: Utilities",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"httpx>=0.27",
|
|
27
|
+
"ruamel.yaml>=0.18",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/FrankXiaA/substrate-solutions"
|
|
32
|
+
Source = "https://github.com/FrankXiaA/substrate-solutions/tree/main/substrate-api/substrate_setup"
|
|
33
|
+
Issues = "https://github.com/FrankXiaA/substrate-solutions/issues"
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=8.0",
|
|
38
|
+
"pytest-httpx>=0.30",
|
|
39
|
+
"ruff>=0.7,<1",
|
|
40
|
+
"mypy>=1.13,<2",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
substrate-setup = "substrate_setup.cli:main"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["substrate_setup"]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests"]
|
|
51
|
+
|
|
52
|
+
[tool.mypy]
|
|
53
|
+
python_version = "3.12"
|
|
54
|
+
strict = true
|
|
55
|
+
ignore_missing_imports = true
|
|
56
|
+
packages = ["substrate_setup"]
|
|
57
|
+
|
|
58
|
+
[[tool.mypy.overrides]]
|
|
59
|
+
module = "tests.*"
|
|
60
|
+
disallow_untyped_defs = false
|
|
61
|
+
disallow_incomplete_defs = false
|
|
62
|
+
check_untyped_defs = false
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# CI lint: substrate_setup/ MUST NOT import anything from app/.
|
|
3
|
-
# The two packages share a source tree, not a dependency graph.
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
PKG_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
6
|
-
if grep -RIn --include='*.py' '^from app\b\|^import app\b' "$PKG_DIR/substrate_setup"; then
|
|
7
|
-
echo "ERROR: substrate_setup/ must not import from app/. See package boundary rule in spec."
|
|
8
|
-
exit 1
|
|
9
|
-
fi
|
|
10
|
-
echo "OK: no app/ imports in substrate_setup/"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CI lint: substrate_setup/ MUST NOT import anything from app/.
|
|
3
|
+
# The two packages share a source tree, not a dependency graph.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
PKG_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
6
|
+
if grep -RIn --include='*.py' '^from app\b\|^import app\b' "$PKG_DIR/substrate_setup"; then
|
|
7
|
+
echo "ERROR: substrate_setup/ must not import from app/. See package boundary rule in spec."
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
echo "OK: no app/ imports in substrate_setup/"
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
"""Regenerate the bundled fallback catalog snapshot.
|
|
2
|
-
|
|
3
|
-
Run manually before each PyPI release of substrate-setup. NOT run by CI cron —
|
|
4
|
-
a transient production glitch should NEVER be baked into the next release.
|
|
5
|
-
|
|
6
|
-
Usage:
|
|
7
|
-
SUBSTRATE_API_KEY=sk-substrate-... \
|
|
8
|
-
python scripts/regenerate_fallback_catalog.py [--base-url https://...]
|
|
9
|
-
|
|
10
|
-
Writes substrate_setup/data/fallback_catalog.json. Applies a sanity check
|
|
11
|
-
(>= 5 models, all five expected providers present) before writing.
|
|
12
|
-
"""
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
import argparse
|
|
16
|
-
import json
|
|
17
|
-
import os
|
|
18
|
-
import sys
|
|
19
|
-
from pathlib import Path
|
|
20
|
-
|
|
21
|
-
import httpx
|
|
22
|
-
|
|
23
|
-
EXPECTED_PROVIDERS = {"anthropic", "openai", "google", "deepseek", "moonshot"}
|
|
24
|
-
DEFAULT_BASE_URL = "https://substrate-solutions-api.fly.dev/v1"
|
|
25
|
-
TARGET = Path(__file__).resolve().parents[1] / "substrate_setup" / "data" / "fallback_catalog.json"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def main(argv: list[str] | None = None) -> int:
|
|
29
|
-
parser = argparse.ArgumentParser()
|
|
30
|
-
parser.add_argument("--base-url", default=DEFAULT_BASE_URL)
|
|
31
|
-
args = parser.parse_args(argv)
|
|
32
|
-
|
|
33
|
-
api_key = os.environ.get("SUBSTRATE_API_KEY")
|
|
34
|
-
if not api_key:
|
|
35
|
-
print("ERROR: SUBSTRATE_API_KEY env var required.", file=sys.stderr)
|
|
36
|
-
return 1
|
|
37
|
-
|
|
38
|
-
# Tolerate --base-url with or without a trailing /v1 (same convention as
|
|
39
|
-
# substrate_setup.catalog.fetch_catalog).
|
|
40
|
-
normalized = args.base_url.rstrip("/")
|
|
41
|
-
if normalized.endswith("/v1"):
|
|
42
|
-
normalized = normalized[:-3]
|
|
43
|
-
|
|
44
|
-
resp = httpx.get(
|
|
45
|
-
f"{normalized}/v1/models",
|
|
46
|
-
headers={"Authorization": f"Bearer {api_key}"},
|
|
47
|
-
timeout=15.0,
|
|
48
|
-
)
|
|
49
|
-
resp.raise_for_status()
|
|
50
|
-
payload = resp.json()
|
|
51
|
-
|
|
52
|
-
data = payload.get("data") or []
|
|
53
|
-
providers = {item.get("owned_by") for item in data}
|
|
54
|
-
if len(data) < 5:
|
|
55
|
-
print(f"REFUSED: only {len(data)} models in catalog (expected >= 5).", file=sys.stderr)
|
|
56
|
-
return 2
|
|
57
|
-
if not EXPECTED_PROVIDERS.issubset(providers):
|
|
58
|
-
missing = EXPECTED_PROVIDERS - providers
|
|
59
|
-
print(f"REFUSED: missing providers {missing}.", file=sys.stderr)
|
|
60
|
-
return 2
|
|
61
|
-
|
|
62
|
-
TARGET.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
|
|
63
|
-
print(f"Wrote {len(data)} models to {TARGET}")
|
|
64
|
-
return 0
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if __name__ == "__main__":
|
|
68
|
-
raise SystemExit(main())
|
|
1
|
+
"""Regenerate the bundled fallback catalog snapshot.
|
|
2
|
+
|
|
3
|
+
Run manually before each PyPI release of substrate-setup. NOT run by CI cron —
|
|
4
|
+
a transient production glitch should NEVER be baked into the next release.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
SUBSTRATE_API_KEY=sk-substrate-... \
|
|
8
|
+
python scripts/regenerate_fallback_catalog.py [--base-url https://...]
|
|
9
|
+
|
|
10
|
+
Writes substrate_setup/data/fallback_catalog.json. Applies a sanity check
|
|
11
|
+
(>= 5 models, all five expected providers present) before writing.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
import httpx
|
|
22
|
+
|
|
23
|
+
EXPECTED_PROVIDERS = {"anthropic", "openai", "google", "deepseek", "moonshot"}
|
|
24
|
+
DEFAULT_BASE_URL = "https://substrate-solutions-api.fly.dev/v1"
|
|
25
|
+
TARGET = Path(__file__).resolve().parents[1] / "substrate_setup" / "data" / "fallback_catalog.json"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main(argv: list[str] | None = None) -> int:
|
|
29
|
+
parser = argparse.ArgumentParser()
|
|
30
|
+
parser.add_argument("--base-url", default=DEFAULT_BASE_URL)
|
|
31
|
+
args = parser.parse_args(argv)
|
|
32
|
+
|
|
33
|
+
api_key = os.environ.get("SUBSTRATE_API_KEY")
|
|
34
|
+
if not api_key:
|
|
35
|
+
print("ERROR: SUBSTRATE_API_KEY env var required.", file=sys.stderr)
|
|
36
|
+
return 1
|
|
37
|
+
|
|
38
|
+
# Tolerate --base-url with or without a trailing /v1 (same convention as
|
|
39
|
+
# substrate_setup.catalog.fetch_catalog).
|
|
40
|
+
normalized = args.base_url.rstrip("/")
|
|
41
|
+
if normalized.endswith("/v1"):
|
|
42
|
+
normalized = normalized[:-3]
|
|
43
|
+
|
|
44
|
+
resp = httpx.get(
|
|
45
|
+
f"{normalized}/v1/models",
|
|
46
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
|
47
|
+
timeout=15.0,
|
|
48
|
+
)
|
|
49
|
+
resp.raise_for_status()
|
|
50
|
+
payload = resp.json()
|
|
51
|
+
|
|
52
|
+
data = payload.get("data") or []
|
|
53
|
+
providers = {item.get("owned_by") for item in data}
|
|
54
|
+
if len(data) < 5:
|
|
55
|
+
print(f"REFUSED: only {len(data)} models in catalog (expected >= 5).", file=sys.stderr)
|
|
56
|
+
return 2
|
|
57
|
+
if not EXPECTED_PROVIDERS.issubset(providers):
|
|
58
|
+
missing = EXPECTED_PROVIDERS - providers
|
|
59
|
+
print(f"REFUSED: missing providers {missing}.", file=sys.stderr)
|
|
60
|
+
return 2
|
|
61
|
+
|
|
62
|
+
TARGET.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
|
|
63
|
+
print(f"Wrote {len(data)} models to {TARGET}")
|
|
64
|
+
return 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
raise SystemExit(main())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""substrate-setup — one-shot configurator for coding agents.
|
|
2
|
-
|
|
3
|
-
See https://github.com/FrankXiaA/substrate-solutions for the gateway it
|
|
4
|
-
configures against.
|
|
5
|
-
"""
|
|
6
|
-
__version__ = "0.2.
|
|
1
|
+
"""substrate-setup — one-shot configurator for coding agents.
|
|
2
|
+
|
|
3
|
+
See https://github.com/FrankXiaA/substrate-solutions for the gateway it
|
|
4
|
+
configures against.
|
|
5
|
+
"""
|
|
6
|
+
__version__ = "0.2.2"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"""Module entry point: ``python -m substrate_setup``."""
|
|
2
|
-
from substrate_setup.cli import main
|
|
3
|
-
|
|
4
|
-
if __name__ == "__main__":
|
|
5
|
-
raise SystemExit(main())
|
|
1
|
+
"""Module entry point: ``python -m substrate_setup``."""
|
|
2
|
+
from substrate_setup.cli import main
|
|
3
|
+
|
|
4
|
+
if __name__ == "__main__":
|
|
5
|
+
raise SystemExit(main())
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
"""Agent registry. Imports each agent module; exposes the list.
|
|
2
|
-
|
|
3
|
-
Order of this list is the order they print in the summary.
|
|
4
|
-
"""
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from substrate_setup.agents.aider import AiderAgent
|
|
8
|
-
from substrate_setup.agents.base import Agent
|
|
9
|
-
from substrate_setup.agents.continue_dev import ContinueAgent
|
|
10
|
-
from substrate_setup.agents.cursor import CursorAgent
|
|
11
|
-
from substrate_setup.agents.hermes import HermesAgent
|
|
12
|
-
|
|
13
|
-
ALL_AGENTS: list[Agent] = [
|
|
14
|
-
HermesAgent(),
|
|
15
|
-
CursorAgent(),
|
|
16
|
-
AiderAgent(),
|
|
17
|
-
ContinueAgent(),
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
AGENT_NAMES: list[str] = [a.name for a in ALL_AGENTS]
|
|
1
|
+
"""Agent registry. Imports each agent module; exposes the list.
|
|
2
|
+
|
|
3
|
+
Order of this list is the order they print in the summary.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from substrate_setup.agents.aider import AiderAgent
|
|
8
|
+
from substrate_setup.agents.base import Agent
|
|
9
|
+
from substrate_setup.agents.continue_dev import ContinueAgent
|
|
10
|
+
from substrate_setup.agents.cursor import CursorAgent
|
|
11
|
+
from substrate_setup.agents.hermes import HermesAgent
|
|
12
|
+
|
|
13
|
+
ALL_AGENTS: list[Agent] = [
|
|
14
|
+
HermesAgent(),
|
|
15
|
+
CursorAgent(),
|
|
16
|
+
AiderAgent(),
|
|
17
|
+
ContinueAgent(),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
AGENT_NAMES: list[str] = [a.name for a in ALL_AGENTS]
|
|
@@ -120,6 +120,30 @@ def _user_has_meaningful_keys(payload: dict[str, Any]) -> bool:
|
|
|
120
120
|
return False
|
|
121
121
|
|
|
122
122
|
|
|
123
|
+
def _existing_matches_substrate_shape(
|
|
124
|
+
payload: dict[str, Any], ctx: ConfigureContext
|
|
125
|
+
) -> bool:
|
|
126
|
+
"""True if existing top-level values look substrate-shaped already.
|
|
127
|
+
|
|
128
|
+
Specifically:
|
|
129
|
+
- openai-api-base matches ctx.base_url
|
|
130
|
+
- openai-api-key starts with "sk-substrate-" (the user may have
|
|
131
|
+
rotated keys since, so we don't require an exact match)
|
|
132
|
+
|
|
133
|
+
Lets us silently take ownership of configs that were either manually
|
|
134
|
+
configured to match our schema or written by an earlier substrate-setup
|
|
135
|
+
version that didn't drop the marker. Without this auto-claim, every
|
|
136
|
+
user upgrading from 0.1 (or who manually fixed their config) sees a
|
|
137
|
+
refusal ERROR on first 0.2.x run.
|
|
138
|
+
"""
|
|
139
|
+
if payload.get("openai-api-base") != ctx.base_url:
|
|
140
|
+
return False
|
|
141
|
+
api_key = payload.get("openai-api-key")
|
|
142
|
+
if not isinstance(api_key, str) or not api_key.startswith("sk-substrate-"):
|
|
143
|
+
return False
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
|
|
123
147
|
def _pick_default_model(catalog: list[Any]) -> str:
|
|
124
148
|
if not catalog:
|
|
125
149
|
raise ValueError("Cannot pick default model from empty catalog")
|
|
@@ -140,13 +164,19 @@ class AiderAgent(Agent):
|
|
|
140
164
|
payload = _load_yaml(conf)
|
|
141
165
|
|
|
142
166
|
if not _has_substrate_marker(payload) and _user_has_meaningful_keys(payload):
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
if _existing_matches_substrate_shape(payload, ctx):
|
|
168
|
+
# Shape already matches us -- silently take ownership.
|
|
169
|
+
# Fall through to the normal write below, which adds the
|
|
170
|
+
# marker and refreshes our 3 keys.
|
|
171
|
+
pass
|
|
172
|
+
else:
|
|
173
|
+
return AgentResult(
|
|
174
|
+
ResultStatus.ERROR,
|
|
175
|
+
"Aider ~/.aider.conf.yml has user-owned openai-api-base / "
|
|
176
|
+
"openai-api-key / model keys but no substrate-setup marker; "
|
|
177
|
+
"refused to overwrite. To take over, remove those keys (or "
|
|
178
|
+
"the whole file) and re-run substrate-setup.",
|
|
179
|
+
)
|
|
150
180
|
|
|
151
181
|
if conf.exists():
|
|
152
182
|
backup = backup_once(conf)
|