substrate-setup 0.2.1__tar.gz → 0.3.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.
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/.gitignore +77 -74
- substrate_setup-0.3.0/PKG-INFO +81 -0
- substrate_setup-0.3.0/README.md +51 -0
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/pyproject.toml +63 -62
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/scripts/lint_no_app_import.sh +10 -10
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/scripts/regenerate_fallback_catalog.py +68 -68
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/__init__.py +6 -6
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/__main__.py +5 -5
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/agents/__init__.py +24 -20
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/agents/aider.py +114 -37
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/agents/base.py +56 -56
- substrate_setup-0.3.0/substrate_setup/agents/claude_code.py +339 -0
- substrate_setup-0.3.0/substrate_setup/agents/codex.py +345 -0
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/agents/continue_dev.py +5 -1
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/agents/cursor.py +94 -90
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/agents/hermes.py +126 -10
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/backup.py +32 -32
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/catalog.py +115 -109
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/cli.py +249 -225
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/credentials.py +82 -82
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/data/fallback_catalog.json +343 -343
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/substrate_setup/markers.py +27 -27
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/agents/test_aider.py +97 -11
- substrate_setup-0.3.0/tests/agents/test_claude_code.py +262 -0
- substrate_setup-0.3.0/tests/agents/test_codex.py +278 -0
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/agents/test_continue_dev.py +27 -0
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/agents/test_cursor.py +111 -87
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/agents/test_hermes.py +109 -1
- substrate_setup-0.3.0/tests/conftest.py +19 -0
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/test_backup.py +38 -38
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/test_catalog.py +95 -95
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/test_cli.py +226 -175
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/test_credentials.py +80 -80
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/test_e2e.py +152 -111
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/test_markers.py +45 -45
- substrate_setup-0.2.1/PKG-INFO +0 -40
- substrate_setup-0.2.1/README.md +0 -11
- substrate_setup-0.2.1/tests/conftest.py +0 -1
- substrate_setup-0.2.1/uv.lock +0 -341
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/__init__.py +0 -0
- {substrate_setup-0.2.1 → substrate_setup-0.3.0}/tests/agents/__init__.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,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: substrate-setup
|
|
3
|
+
Version: 0.3.0
|
|
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
|
+
Requires-Dist: tomli-w>=1.2
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy<2,>=1.13; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff<1,>=0.7; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# substrate-setup
|
|
32
|
+
|
|
33
|
+
One-shot configurator that points local coding agents at a Substrate gateway.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
`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:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Option A — uv (recommended; downloads Python 3.12 on demand if missing)
|
|
41
|
+
uv tool install substrate-setup
|
|
42
|
+
|
|
43
|
+
# Option B — pipx
|
|
44
|
+
pipx install substrate-setup
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Don't have `uv`? Install it once with:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh # macOS / Linux
|
|
51
|
+
# or on Windows:
|
|
52
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
> **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.
|
|
56
|
+
|
|
57
|
+
## Use
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
export SUBSTRATE_API_KEY="sk-substrate-..." # or be prompted
|
|
61
|
+
|
|
62
|
+
substrate-setup configure # detect installed agents and wire them up
|
|
63
|
+
substrate-setup verify # read-only: confirm everything points at the gateway
|
|
64
|
+
substrate-setup remove # strip the substrate-managed entries
|
|
65
|
+
substrate-setup --help
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Supported agents: `hermes`, `cursor`, `aider`, `continue`, `claude-code`, `codex`.
|
|
69
|
+
|
|
70
|
+
Subset with `--agents-only hermes,aider`. Preview without writing: `--dry-run`. Override the gateway base URL: `--base-url https://your-gateway.example.com`.
|
|
71
|
+
|
|
72
|
+
### Per-agent catalog UX (0.3.0+)
|
|
73
|
+
|
|
74
|
+
| Agent | How it learns about Substrate's models |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `hermes` | Live URL fetch via `model_catalog.providers.substrate.url`. Picker shows all chat-capable Substrate models, refreshed on Hermes' 24h TTL. |
|
|
77
|
+
| `cursor` | Walkthrough printed after configure — copy the base URL, key, and model ids into Cursor's Settings → Models. |
|
|
78
|
+
| `aider` | `~/.aider.model.metadata.json` written with one entry per chat-capable Substrate model. Use `--model openai/<id>` to switch. |
|
|
79
|
+
| `continue` | All chat-capable Substrate models written as separate `models:` entries in `~/.continue/config.yaml`. |
|
|
80
|
+
| `claude-code` | `~/.claude/settings.json` env block (`ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_MODEL`). The `/model` picker stays opus/sonnet/haiku — use `claude --model openai/<id>` for any other Substrate model. **Note:** requires Substrate's `/v1/messages` endpoint, which is gated behind a Fly feature flag — until it's flipped, requests return 404. |
|
|
81
|
+
| `codex` | `~/.codex/config.toml` `[model_providers.substrate]` block with `wire_api = "responses"`. Set `SUBSTRATE_API_KEY` in your shell rc. **Note:** requires Substrate's `/v1/responses` endpoint (gateway protocol adapters Phase 2 — not yet started). |
|
|
@@ -0,0 +1,51 @@
|
|
|
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`, `claude-code`, `codex`.
|
|
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`.
|
|
41
|
+
|
|
42
|
+
### Per-agent catalog UX (0.3.0+)
|
|
43
|
+
|
|
44
|
+
| Agent | How it learns about Substrate's models |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `hermes` | Live URL fetch via `model_catalog.providers.substrate.url`. Picker shows all chat-capable Substrate models, refreshed on Hermes' 24h TTL. |
|
|
47
|
+
| `cursor` | Walkthrough printed after configure — copy the base URL, key, and model ids into Cursor's Settings → Models. |
|
|
48
|
+
| `aider` | `~/.aider.model.metadata.json` written with one entry per chat-capable Substrate model. Use `--model openai/<id>` to switch. |
|
|
49
|
+
| `continue` | All chat-capable Substrate models written as separate `models:` entries in `~/.continue/config.yaml`. |
|
|
50
|
+
| `claude-code` | `~/.claude/settings.json` env block (`ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_MODEL`). The `/model` picker stays opus/sonnet/haiku — use `claude --model openai/<id>` for any other Substrate model. **Note:** requires Substrate's `/v1/messages` endpoint, which is gated behind a Fly feature flag — until it's flipped, requests return 404. |
|
|
51
|
+
| `codex` | `~/.codex/config.toml` `[model_providers.substrate]` block with `wire_api = "responses"`. Set `SUBSTRATE_API_KEY` in your shell rc. **Note:** requires Substrate's `/v1/responses` endpoint (gateway protocol adapters Phase 2 — not yet started). |
|
|
@@ -1,62 +1,63 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
3
|
-
build-backend = "hatchling.build"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "substrate-setup"
|
|
7
|
-
version = "0.
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"pytest
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "substrate-setup"
|
|
7
|
+
version = "0.3.0"
|
|
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
|
+
"tomli-w>=1.2",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/FrankXiaA/substrate-solutions"
|
|
33
|
+
Source = "https://github.com/FrankXiaA/substrate-solutions/tree/main/substrate-api/substrate_setup"
|
|
34
|
+
Issues = "https://github.com/FrankXiaA/substrate-solutions/issues"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=8.0",
|
|
39
|
+
"pytest-httpx>=0.30",
|
|
40
|
+
"ruff>=0.7,<1",
|
|
41
|
+
"mypy>=1.13,<2",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
substrate-setup = "substrate_setup.cli:main"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["substrate_setup"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
|
|
53
|
+
[tool.mypy]
|
|
54
|
+
python_version = "3.12"
|
|
55
|
+
strict = true
|
|
56
|
+
ignore_missing_imports = true
|
|
57
|
+
packages = ["substrate_setup"]
|
|
58
|
+
|
|
59
|
+
[[tool.mypy.overrides]]
|
|
60
|
+
module = "tests.*"
|
|
61
|
+
disallow_untyped_defs = false
|
|
62
|
+
disallow_incomplete_defs = false
|
|
63
|
+
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.
|
|
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.3.0"
|
|
@@ -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,24 @@
|
|
|
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.
|
|
10
|
-
from substrate_setup.agents.
|
|
11
|
-
from substrate_setup.agents.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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.claude_code import ClaudeCodeAgent
|
|
10
|
+
from substrate_setup.agents.codex import CodexAgent
|
|
11
|
+
from substrate_setup.agents.continue_dev import ContinueAgent
|
|
12
|
+
from substrate_setup.agents.cursor import CursorAgent
|
|
13
|
+
from substrate_setup.agents.hermes import HermesAgent
|
|
14
|
+
|
|
15
|
+
ALL_AGENTS: list[Agent] = [
|
|
16
|
+
HermesAgent(),
|
|
17
|
+
CursorAgent(),
|
|
18
|
+
AiderAgent(),
|
|
19
|
+
ContinueAgent(),
|
|
20
|
+
ClaudeCodeAgent(),
|
|
21
|
+
CodexAgent(),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
AGENT_NAMES: list[str] = [a.name for a in ALL_AGENTS]
|