substrate-setup 0.2.2__tar.gz → 0.3.1__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.
Files changed (44) hide show
  1. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/.gitignore +77 -77
  2. substrate_setup-0.3.1/CHANGELOG.md +22 -0
  3. substrate_setup-0.3.1/PKG-INFO +116 -0
  4. substrate_setup-0.3.1/README.md +86 -0
  5. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/pyproject.toml +63 -62
  6. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/scripts/lint_no_app_import.sh +10 -10
  7. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/scripts/regenerate_fallback_catalog.py +68 -68
  8. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/__init__.py +6 -6
  9. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/__main__.py +5 -5
  10. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/agents/__init__.py +24 -20
  11. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/agents/aider.py +397 -316
  12. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/agents/base.py +56 -56
  13. substrate_setup-0.3.1/substrate_setup/agents/claude_code.py +343 -0
  14. substrate_setup-0.3.1/substrate_setup/agents/codex.py +357 -0
  15. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/agents/continue_dev.py +286 -278
  16. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/agents/cursor.py +99 -90
  17. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/agents/hermes.py +508 -388
  18. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/backup.py +32 -32
  19. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/catalog.py +115 -109
  20. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/cli.py +249 -249
  21. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/credentials.py +82 -82
  22. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/data/fallback_catalog.json +343 -343
  23. substrate_setup-0.3.1/substrate_setup/env_persist.py +226 -0
  24. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/substrate_setup/markers.py +27 -27
  25. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/agents/test_aider.py +390 -278
  26. substrate_setup-0.3.1/tests/agents/test_claude_code.py +286 -0
  27. substrate_setup-0.3.1/tests/agents/test_codex.py +362 -0
  28. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/agents/test_continue_dev.py +283 -230
  29. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/agents/test_cursor.py +149 -87
  30. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/agents/test_hermes.py +630 -498
  31. substrate_setup-0.3.1/tests/conftest.py +19 -0
  32. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/test_backup.py +38 -38
  33. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/test_catalog.py +95 -95
  34. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/test_cli.py +226 -246
  35. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/test_credentials.py +80 -80
  36. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/test_e2e.py +152 -111
  37. substrate_setup-0.3.1/tests/test_env_persist.py +180 -0
  38. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/test_markers.py +45 -45
  39. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/uv.lock +12 -1
  40. substrate_setup-0.2.2/PKG-INFO +0 -69
  41. substrate_setup-0.2.2/README.md +0 -40
  42. substrate_setup-0.2.2/tests/conftest.py +0 -1
  43. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/__init__.py +0 -0
  44. {substrate_setup-0.2.2 → substrate_setup-0.3.1}/tests/agents/__init__.py +0 -0
@@ -1,77 +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
75
-
76
- # brainstorming companion working dir
77
- .superpowers/
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,22 @@
1
+ # Changelog
2
+
3
+ ## 0.3.1 — 2026-05-24
4
+
5
+ - Persist `SUBSTRATE_API_KEY` automatically during `configure`:
6
+ on Windows, writes `HKCU\Environment\SUBSTRATE_API_KEY` via `winreg`
7
+ + `WM_SETTINGCHANGE` broadcast — visible to GUI agents (Codex Desktop)
8
+ on next launch without the user editing rc files or restarting their
9
+ session. On macOS / Linux, appends a marker-fenced `export` block (or
10
+ fish-syntax `set -gx` for fish users) to the user's shell rc. Re-runs
11
+ collapse stale blocks idempotently — the rc never grows on repeated
12
+ configures.
13
+ - Codex adapter now prints a one-line Windows Defender exclusion command
14
+ to eliminate SmartScreen prompts on per-action helper binary spawns.
15
+ Informational only — substrate-setup never elevates itself.
16
+ - Adapters surface env-persistence failures with a clear `WARNING:`
17
+ prefix + manual fallback hint so users notice when the convenience
18
+ step fails but the config file itself was written successfully.
19
+ - Codex adapter drops the stale "/v1/responses NOT YET STARTED" warning
20
+ — Phase 2 shipped on 2026-05-24.
21
+ - No config schema changes from 0.3.0. Upgrade cleanly via
22
+ `uv tool install --force substrate-setup` (or pipx).
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.4
2
+ Name: substrate-setup
3
+ Version: 0.3.1
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
+ substrate-setup configure # detect installed agents and wire them up
61
+ substrate-setup verify # read-only: confirm everything points at the gateway
62
+ substrate-setup remove # strip the substrate-managed entries
63
+ substrate-setup --help
64
+ ```
65
+
66
+ ### After running configure
67
+
68
+ `substrate-setup configure` writes the API key for you automatically. On Windows it lands in `HKCU\Environment\SUBSTRATE_API_KEY` (visible to Codex Desktop and other GUI agents on next launch). On macOS / Linux it lands in your shell rc (`~/.zshrc`, `~/.bashrc`, or `~/.config/fish/config.fish` depending on `$SHELL`) inside a marker-fenced block — re-running configure replaces the block in place, no duplicates.
69
+
70
+ You should not need to set `SUBSTRATE_API_KEY` by hand. If auto-persistence fails (the printed message will say `API key WARNING: …`), you can set it manually:
71
+
72
+ - **Windows:** `[Environment]::SetEnvironmentVariable("SUBSTRATE_API_KEY", "<your-key>", "User")` then re-launch the agent.
73
+ - **macOS / Linux:** add `export SUBSTRATE_API_KEY=<your-key>` to your shell rc and `source` it.
74
+
75
+ Supported agents: `hermes`, `cursor`, `aider`, `continue`, `claude-code`, `codex`.
76
+
77
+ Subset with `--agents-only hermes,aider`. Preview without writing: `--dry-run`. Override the gateway base URL: `--base-url https://your-gateway.example.com`.
78
+
79
+ ### Per-agent catalog UX (0.3.0+)
80
+
81
+ | Agent | How it learns about Substrate's models |
82
+ |---|---|
83
+ | `hermes` | Live URL fetch via `model_catalog.providers.substrate.url`. Picker shows all chat-capable Substrate models, refreshed on Hermes' 24h TTL. |
84
+ | `cursor` | Walkthrough printed after configure — copy the base URL, key, and model ids into Cursor's Settings → Models. |
85
+ | `aider` | `~/.aider.model.metadata.json` written with one entry per chat-capable Substrate model. Use `--model openai/<id>` to switch. |
86
+ | `continue` | All chat-capable Substrate models written as separate `models:` entries in `~/.continue/config.yaml`. |
87
+ | `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. Requires Substrate's `/v1/messages` endpoint — shipped in gateway Phase 1. |
88
+ | `codex` | `~/.codex/config.toml` `[model_providers.substrate]` block with `wire_api = "responses"`. API key is auto-persisted (see above). Requires Substrate's `/v1/responses` endpoint — shipped in gateway Phase 2. |
89
+
90
+ ### Heads-up: tool calling on Gemini 3.1 Pro Preview
91
+
92
+ If your CLI agent (Hermes, Aider, etc.) uses tool calling against
93
+ `google/gemini-3.1-pro-preview`, expect occasional misses (~20-30% of
94
+ attempts). All other Substrate models hit ≥95% reliability for tool calls.
95
+
96
+ ## Troubleshooting
97
+
98
+ ### Windows: Defender SmartScreen prompts on every Codex click
99
+
100
+ Codex Desktop spawns helper binaries (`codex-command-runner.exe`,
101
+ `node_repl.exe`) per UI action. With `sandbox = "elevated"` in
102
+ `~/.codex/config.toml`, each spawn triggers Windows Defender
103
+ SmartScreen, prompting you on every click until the Codex install
104
+ directory is whitelisted.
105
+
106
+ One-time fix — open an **Administrator** PowerShell window and run:
107
+
108
+ ```powershell
109
+ Add-MpPreference -ExclusionPath "$env:LOCALAPPDATA\OpenAI\Codex"
110
+ ```
111
+
112
+ This excludes Codex's binaries from real-time scanning. Reverse with
113
+ `Remove-MpPreference -ExclusionPath "$env:LOCALAPPDATA\OpenAI\Codex"`.
114
+
115
+ `substrate-setup configure --agents-only codex` also prints this
116
+ command at the end of its walkthrough on Windows.
@@ -0,0 +1,86 @@
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
+ substrate-setup configure # detect installed agents and wire them up
31
+ substrate-setup verify # read-only: confirm everything points at the gateway
32
+ substrate-setup remove # strip the substrate-managed entries
33
+ substrate-setup --help
34
+ ```
35
+
36
+ ### After running configure
37
+
38
+ `substrate-setup configure` writes the API key for you automatically. On Windows it lands in `HKCU\Environment\SUBSTRATE_API_KEY` (visible to Codex Desktop and other GUI agents on next launch). On macOS / Linux it lands in your shell rc (`~/.zshrc`, `~/.bashrc`, or `~/.config/fish/config.fish` depending on `$SHELL`) inside a marker-fenced block — re-running configure replaces the block in place, no duplicates.
39
+
40
+ You should not need to set `SUBSTRATE_API_KEY` by hand. If auto-persistence fails (the printed message will say `API key WARNING: …`), you can set it manually:
41
+
42
+ - **Windows:** `[Environment]::SetEnvironmentVariable("SUBSTRATE_API_KEY", "<your-key>", "User")` then re-launch the agent.
43
+ - **macOS / Linux:** add `export SUBSTRATE_API_KEY=<your-key>` to your shell rc and `source` it.
44
+
45
+ Supported agents: `hermes`, `cursor`, `aider`, `continue`, `claude-code`, `codex`.
46
+
47
+ Subset with `--agents-only hermes,aider`. Preview without writing: `--dry-run`. Override the gateway base URL: `--base-url https://your-gateway.example.com`.
48
+
49
+ ### Per-agent catalog UX (0.3.0+)
50
+
51
+ | Agent | How it learns about Substrate's models |
52
+ |---|---|
53
+ | `hermes` | Live URL fetch via `model_catalog.providers.substrate.url`. Picker shows all chat-capable Substrate models, refreshed on Hermes' 24h TTL. |
54
+ | `cursor` | Walkthrough printed after configure — copy the base URL, key, and model ids into Cursor's Settings → Models. |
55
+ | `aider` | `~/.aider.model.metadata.json` written with one entry per chat-capable Substrate model. Use `--model openai/<id>` to switch. |
56
+ | `continue` | All chat-capable Substrate models written as separate `models:` entries in `~/.continue/config.yaml`. |
57
+ | `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. Requires Substrate's `/v1/messages` endpoint — shipped in gateway Phase 1. |
58
+ | `codex` | `~/.codex/config.toml` `[model_providers.substrate]` block with `wire_api = "responses"`. API key is auto-persisted (see above). Requires Substrate's `/v1/responses` endpoint — shipped in gateway Phase 2. |
59
+
60
+ ### Heads-up: tool calling on Gemini 3.1 Pro Preview
61
+
62
+ If your CLI agent (Hermes, Aider, etc.) uses tool calling against
63
+ `google/gemini-3.1-pro-preview`, expect occasional misses (~20-30% of
64
+ attempts). All other Substrate models hit ≥95% reliability for tool calls.
65
+
66
+ ## Troubleshooting
67
+
68
+ ### Windows: Defender SmartScreen prompts on every Codex click
69
+
70
+ Codex Desktop spawns helper binaries (`codex-command-runner.exe`,
71
+ `node_repl.exe`) per UI action. With `sandbox = "elevated"` in
72
+ `~/.codex/config.toml`, each spawn triggers Windows Defender
73
+ SmartScreen, prompting you on every click until the Codex install
74
+ directory is whitelisted.
75
+
76
+ One-time fix — open an **Administrator** PowerShell window and run:
77
+
78
+ ```powershell
79
+ Add-MpPreference -ExclusionPath "$env:LOCALAPPDATA\OpenAI\Codex"
80
+ ```
81
+
82
+ This excludes Codex's binaries from real-time scanning. Reverse with
83
+ `Remove-MpPreference -ExclusionPath "$env:LOCALAPPDATA\OpenAI\Codex"`.
84
+
85
+ `substrate-setup configure --agents-only codex` also prints this
86
+ command at the end of its walkthrough on Windows.
@@ -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.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
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "substrate-setup"
7
+ version = "0.3.1"
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())