openapi-cli4ai 0.5.1__tar.gz → 0.7.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.
Files changed (67) hide show
  1. openapi_cli4ai-0.7.0/.github/CODEOWNERS +7 -0
  2. openapi_cli4ai-0.7.0/.github/pull_request_template.md +22 -0
  3. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.github/workflows/ci.yml +10 -8
  4. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.github/workflows/codeql.yml +3 -3
  5. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.github/workflows/publish.yml +4 -4
  6. openapi_cli4ai-0.7.0/.github/workflows/validate-profiles.yml +26 -0
  7. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/CHANGELOG.md +39 -0
  8. openapi_cli4ai-0.7.0/CONTRIBUTING.md +78 -0
  9. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/PKG-INFO +5 -2
  10. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/README.md +2 -0
  11. openapi_cli4ai-0.7.0/profiles/README.md +114 -0
  12. openapi_cli4ai-0.7.0/profiles/community/.gitkeep +2 -0
  13. openapi_cli4ai-0.7.0/profiles/community/xquik.toml +11 -0
  14. openapi_cli4ai-0.7.0/profiles/index.json +33 -0
  15. openapi_cli4ai-0.7.0/profiles/verified/petstore.toml +11 -0
  16. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/pyproject.toml +22 -1
  17. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/src/openapi_cli4ai/__init__.py +1 -1
  18. openapi_cli4ai-0.7.0/src/openapi_cli4ai/_state.py +22 -0
  19. openapi_cli4ai-0.7.0/src/openapi_cli4ai/_ui.py +16 -0
  20. openapi_cli4ai-0.7.0/src/openapi_cli4ai/catalog.py +247 -0
  21. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/src/openapi_cli4ai/cli.py +384 -249
  22. openapi_cli4ai-0.7.0/src/openapi_cli4ai/config.py +254 -0
  23. openapi_cli4ai-0.7.0/src/openapi_cli4ai/validator.py +253 -0
  24. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/conftest.py +13 -3
  25. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_auth_flow_security.py +2 -2
  26. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_auth_flows.py +5 -8
  27. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_caching.py +1 -1
  28. openapi_cli4ai-0.7.0/tests/test_catalog.py +387 -0
  29. openapi_cli4ai-0.7.0/tests/test_catalog_remote.py +162 -0
  30. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_device_flow.py +0 -1
  31. openapi_cli4ai-0.7.0/tests/test_domain_verified.py +122 -0
  32. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_error_handling.py +10 -8
  33. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_features.py +52 -51
  34. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_foundation.py +12 -10
  35. openapi_cli4ai-0.7.0/tests/test_profile_anywhere.py +79 -0
  36. openapi_cli4ai-0.7.0/tests/test_profiles_d.py +119 -0
  37. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_schema_composition.py +0 -1
  38. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_security.py +5 -7
  39. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_v040_hardening.py +11 -10
  40. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/uv.lock +181 -126
  41. openapi_cli4ai-0.5.1/.github/CODEOWNERS +0 -2
  42. openapi_cli4ai-0.5.1/CONTRIBUTING.md +0 -50
  43. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  44. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  45. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.github/dependabot.yml +0 -0
  46. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.github/release.yml +0 -0
  47. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.gitignore +0 -0
  48. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/.pre-commit-config.yaml +0 -0
  49. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/LICENSE +0 -0
  50. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/SECURITY.md +0 -0
  51. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/demo.gif +0 -0
  52. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/examples/profiles.toml.example +0 -0
  53. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/openapi-cli4ai +0 -0
  54. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/src/openapi_cli4ai/__main__.py +0 -0
  55. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/src/openapi_cli4ai/py.typed +0 -0
  56. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/__init__.py +0 -0
  57. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/fixtures/petstore_spec.json +0 -0
  58. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_auth.py +0 -0
  59. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_cli_commands.py +1 -1
  60. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_commands.py +0 -0
  61. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_coverage_gaps.py +0 -0
  62. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_integration.py +0 -0
  63. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_oidc.py +0 -0
  64. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_profile_management.py +0 -0
  65. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_run_command.py +0 -0
  66. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_spec_parsing.py +0 -0
  67. {openapi_cli4ai-0.5.1 → openapi_cli4ai-0.7.0}/tests/test_sse_streaming.py +0 -0
@@ -0,0 +1,7 @@
1
+ # Default: all PRs require review from a maintainer
2
+ * @dbgorilla/maintainers
3
+
4
+ # Catalog profiles. Both tiers require maintainer approval for now; the
5
+ # community tier can later point at a broader reviewer team.
6
+ /profiles/verified/ @dbgorilla/maintainers
7
+ /profiles/community/ @dbgorilla/maintainers
@@ -0,0 +1,22 @@
1
+ <!-- Thanks for contributing! Describe your change below. -->
2
+
3
+ ## What & why
4
+
5
+
6
+
7
+ ---
8
+
9
+ <!-- ▼ Delete this whole section if you are NOT adding a catalog profile ▼ -->
10
+
11
+ ### Adding a catalog profile — please confirm
12
+
13
+ - [ ] The profile is for a **publicly accessible** API with a stable OpenAPI spec.
14
+ - [ ] I am the API owner, or have the owner's permission.
15
+ - [ ] `source` is the API's **own** developer/docs URL (same registrable domain as `base_url`).
16
+ - [ ] No secrets are inlined — credentials are referenced via `*_env_var` fields only.
17
+ - [ ] `description` is factual (no marketing copy).
18
+ - [ ] I ran `uv run openapi-cli4ai catalog validate profiles/community/<slug>.toml` locally and it passed.
19
+
20
+ > **Reviewer:** independently verify that `base_url` resolves to the claimed
21
+ > service (typosquats and look-alike domains are the main risk), and that the
22
+ > auth shape can't leak a credential to an unexpected host.
@@ -16,8 +16,8 @@ jobs:
16
16
  matrix:
17
17
  python-version: ["3.11", "3.12", "3.13"]
18
18
  steps:
19
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
20
- - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
21
21
  with:
22
22
  enable-cache: true
23
23
  - name: Set up Python ${{ matrix.python-version }}
@@ -30,18 +30,20 @@ jobs:
30
30
  lint:
31
31
  runs-on: ubuntu-latest
32
32
  steps:
33
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
34
- - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
33
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
34
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
35
+ - name: Sync dependencies
36
+ run: uv sync --locked
35
37
  - name: Ruff check
36
- run: uvx ruff check .
38
+ run: uv run ruff check .
37
39
  - name: Ruff format check
38
- run: uvx ruff format --check .
40
+ run: uv run ruff format --check .
39
41
 
40
42
  typecheck:
41
43
  runs-on: ubuntu-latest
42
44
  steps:
43
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
44
- - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
45
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
46
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
45
47
  - name: Sync dependencies
46
48
  run: uv sync --locked
47
49
  - name: Run mypy
@@ -16,8 +16,8 @@ jobs:
16
16
  analyze:
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
20
- - uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
+ - uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
21
21
  with:
22
22
  languages: python
23
- - uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
23
+ - uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
@@ -13,8 +13,8 @@ jobs:
13
13
  permissions:
14
14
  contents: read
15
15
  steps:
16
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
17
- - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
16
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
18
18
  - name: Build sdist and wheel
19
19
  run: uv build
20
20
  - name: Upload artifacts
@@ -42,8 +42,8 @@ jobs:
42
42
  name: dist
43
43
  path: dist/
44
44
  - name: Generate attestations
45
- uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
45
+ uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
46
46
  with:
47
47
  subject-path: dist/*
48
48
  - name: Publish to PyPI
49
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
49
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -0,0 +1,26 @@
1
+ name: Validate profiles
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "profiles/**"
8
+ - ".github/workflows/validate-profiles.yml"
9
+ pull_request:
10
+ paths:
11
+ - "profiles/**"
12
+ - ".github/workflows/validate-profiles.yml"
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ validate:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
23
+ - name: Validate catalog profiles
24
+ run: uv run --locked openapi-cli4ai catalog validate --all
25
+ - name: Check profiles/index.json is current
26
+ run: uv run --locked openapi-cli4ai catalog index --check
@@ -4,6 +4,45 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [0.7.0] - 2026-09-11
8
+
9
+ ### Added
10
+
11
+ - `--profile` is accepted after the subcommand (`openapi-cli4ai endpoints --profile x`); long form only, since `-p` is reused by subcommands (#52)
12
+ - `catalog install --dry-run` prints the exact profile file that would be written and changes nothing (#53)
13
+ - `catalog uninstall <name>` removes an installed catalog profile plus its cached spec and token, with confirm prompt and `--force` (#54)
14
+ - Drop-in profiles: one file per profile under `~/.openapi-cli4ai/profiles.d/<name>.toml`, read alongside `~/.openapi-cli4ai.toml` (a drop-in overrides a same-named entry). `catalog install` writes there. No migration needed (#56)
15
+ - Remote catalog index: `catalog list`/`search`/`show`/`install` merge `profiles/index.json` from `main` over the bundled catalog (3 s timeout, cached one hour, silent fallback to cache then bundled; every remote entry is re-validated). `OAC_CATALOG_OFFLINE=1` disables it. New `catalog index` command generates the committed index; CI checks it is current (#57)
16
+ - DNS TXT domain verification: a profile may set `domain_verified = true`; `catalog validate` resolves `_openapi-cli4ai.<domain>` and requires `github=<maintainer>`. Shown as a `✓ dns` badge, separate from the tier (#58)
17
+
18
+ ### Changed
19
+
20
+ - `cli.py` split into `config.py`, `catalog.py`, `validator.py` plus small state/UI modules; behavior unchanged (#55)
21
+ - Adopted ruff 0.16's default rule set; the rule selection is no longer implicit (#50, #51)
22
+ - Dependency updates: `typer` 0.27.2, `click` 8.5, `python-dotenv`, `mypy`, `types-PyYAML`, and the GitHub Actions group (#49, #50)
23
+
24
+ ### Fixed
25
+
26
+ - Rich swallowed TOML table headers such as `[profiles.x]` in `profile show` output; now rendered as plain text (#53)
27
+ - File paths in install output are never wrapped across lines (#56)
28
+
29
+ ## [0.6.0] - 2026-07-31
30
+
31
+ ### Added
32
+
33
+ - **Profile catalog** — browse and install ready-made API profiles: `catalog search` / `show` / `list` / `install` / `validate`. Bundled into the wheel, so it works offline, with two trust tiers (`verified` / `community`) (#25)
34
+ - Global `--profile` / `-p` flag to select a profile for a single invocation (precedence: flag > `OAC_PROFILE` > active profile) (#25)
35
+ - Community-contributed profile: Xquik (#23)
36
+
37
+ ### Changed
38
+
39
+ - Dependency updates: `typer` 0.27, `mypy` 2.3, `types-PyYAML`, and the GitHub Actions group (#38, #37)
40
+ - Pin `ruff` and run the locked version in CI so lint is deterministic (no more `uvx`-latest drift) (#39)
41
+
42
+ ### Security
43
+
44
+ - Catalog validation hardening: SSRF guard on spec fetches (rejects cloud-metadata/private/loopback hosts, pre- and post-redirect), Public Suffix List domain-ownership check, spec size/redirect/timeout limits, prompt-injection scan of descriptions, and an explicit confirmation before installing unverified community profiles (#25)
45
+
7
46
  ## [0.5.1] - 2026-07-04
8
47
 
9
48
  ### Fixed
@@ -0,0 +1,78 @@
1
+ # Contributing to openapi-cli4ai
2
+
3
+ Thanks for your interest in contributing!
4
+
5
+ ## Getting Started
6
+
7
+ 1. Fork the repository
8
+ 2. Clone your fork
9
+ 3. Install [uv](https://docs.astral.sh/uv/) if you don't have it
10
+ 4. Install in editable mode: `uv pip install -e .`
11
+ 5. Run `openapi-cli4ai --help` to verify everything works
12
+
13
+ ## Project Structure
14
+
15
+ ```
16
+ src/openapi_cli4ai/
17
+ __init__.py # Package exports
18
+ __main__.py # python -m support
19
+ cli.py # Typer commands, HTTP/auth flows, spec fetching
20
+ config.py # Config file, cache dir, active-profile precedence
21
+ catalog.py # Bundled profile catalog: load, find, map to a profile
22
+ validator.py # Catalog profile validator (shared by `catalog validate` and CI)
23
+ _state.py # Runtime flags set by the root command (--verbose, --profile, ...)
24
+ _ui.py # Shared Rich consoles and the verbose logger
25
+ openapi-cli4ai # Standalone shim (imports from package)
26
+ tests/ # pytest tests
27
+ ```
28
+
29
+ Commands live in `src/openapi_cli4ai/cli.py`; it imports the helpers it needs from the service modules, so tests can patch either the service module or the `cli` binding. The standalone `openapi-cli4ai` script is a thin shim that imports from the package.
30
+
31
+ ## Testing
32
+
33
+ ```bash
34
+ pytest tests/ -m "not integration" -v
35
+ ```
36
+
37
+ ## Submitting a Pull Request
38
+
39
+ 1. Create a feature branch (`git checkout -b my-feature`)
40
+ 2. Make your changes
41
+ 3. Run the tests
42
+ 4. Commit with a clear message
43
+ 5. Push and open a PR
44
+
45
+ ## Contributing a Profile to the Catalog
46
+
47
+ `openapi-cli4ai` ships a community catalog of ready-made API profiles under
48
+ [`profiles/`](profiles/README.md). To add one:
49
+
50
+ 1. Create `profiles/community/<slug>.toml` following the format in
51
+ [`profiles/README.md`](profiles/README.md).
52
+ 2. Reference secrets only via `*_env_var` fields — never commit a token.
53
+ 3. Keep the `description` factual. `source` must be the API's own developer or
54
+ docs URL (same domain as `base_url`).
55
+ 4. Validate locally: `uv run openapi-cli4ai catalog validate profiles/community/<slug>.toml`
56
+ 5. Regenerate the index and commit it: `uv run openapi-cli4ai catalog index`
57
+ 6. Optional: claim the domain-verified badge by setting `domain_verified = true`
58
+ and publishing a `_openapi-cli4ai.<domain>` TXT record naming you; see
59
+ "Domain verification" in [`profiles/README.md`](profiles/README.md).
60
+
61
+ CI runs `catalog validate --all` and `catalog index --check` on every PR touching
62
+ `profiles/`, checking the fields, a live spec fetch, ownership, absence of inline
63
+ secrets, any `domain_verified` claim against DNS, and that `profiles/index.json`
64
+ is current — errors appear inline on the PR. Once merged, the profile is visible to every installed CLI within an hour
65
+ via the remote index (see `profiles/README.md`, "Freshness"). Profiles that are primarily promotional will be closed
66
+ without review.
67
+
68
+ ## Reporting Issues
69
+
70
+ Open an issue on GitHub. Include:
71
+ - What you tried
72
+ - What happened
73
+ - What you expected
74
+ - The API spec you were using (if relevant)
75
+
76
+ ## License
77
+
78
+ By contributing, you agree that your contributions will be licensed under the MIT License.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: openapi-cli4ai
3
- Version: 0.5.1
3
+ Version: 0.7.0
4
4
  Summary: Turn any REST API with an OpenAPI spec into an AI-ready CLI
5
5
  Project-URL: Homepage, https://github.com/dbgorilla/openapi-cli4ai
6
6
  Project-URL: Repository, https://github.com/dbgorilla/openapi-cli4ai
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Topic :: Software Development :: Libraries
20
20
  Requires-Python: >=3.11
21
21
  Requires-Dist: httpx<1,>=0.28
22
+ Requires-Dist: publicsuffix2<3,>=2.2
22
23
  Requires-Dist: python-dotenv<2,>=1.0
23
24
  Requires-Dist: pyyaml<7,>=6
24
25
  Requires-Dist: rich<16,>=13
@@ -236,6 +237,8 @@ openapi-cli4ai logout
236
237
 
237
238
  Profiles are stored in `~/.openapi-cli4ai.toml`. Secrets are referenced via environment variables — never stored in the config file.
238
239
 
240
+ Profiles can also live one-per-file under `~/.openapi-cli4ai/profiles.d/<name>.toml`, where the file holds the profile's keys directly (no `[profiles.<name>]` header) and the file name is the profile name. `catalog install` writes there so installing, upgrading and uninstalling a profile touch only that file. Both locations are read; a drop-in overrides a same-named entry in `~/.openapi-cli4ai.toml`. `active_profile` is only read from `~/.openapi-cli4ai.toml`.
241
+
239
242
  ```toml
240
243
  active_profile = "myapi"
241
244
 
@@ -208,6 +208,8 @@ openapi-cli4ai logout
208
208
 
209
209
  Profiles are stored in `~/.openapi-cli4ai.toml`. Secrets are referenced via environment variables — never stored in the config file.
210
210
 
211
+ Profiles can also live one-per-file under `~/.openapi-cli4ai/profiles.d/<name>.toml`, where the file holds the profile's keys directly (no `[profiles.<name>]` header) and the file name is the profile name. `catalog install` writes there so installing, upgrading and uninstalling a profile touch only that file. Both locations are read; a drop-in overrides a same-named entry in `~/.openapi-cli4ai.toml`. `active_profile` is only read from `~/.openapi-cli4ai.toml`.
212
+
211
213
  ```toml
212
214
  active_profile = "myapi"
213
215
 
@@ -0,0 +1,114 @@
1
+ # Profile catalog
2
+
3
+ Ready-made **profiles** for public APIs. A profile is the small bit of config
4
+ `openapi-cli4ai` needs to talk to an API: a base URL, where its OpenAPI spec
5
+ lives, and how it authenticates. The catalog is bundled into the package, so
6
+ these commands work offline; when online they also pick up profiles merged to
7
+ `main` since your release (see [Freshness](#freshness)):
8
+
9
+ ```bash
10
+ openapi-cli4ai catalog search cov # find profiles
11
+ openapi-cli4ai catalog show codecov # preview one
12
+ openapi-cli4ai catalog install codecov # add it to your config, with next steps
13
+ openapi-cli4ai --profile codecov endpoints # use it (no activation needed)
14
+ ```
15
+
16
+ `install` maps the catalog entry into your `~/.openapi-cli4ai.toml`, tells you
17
+ exactly which environment variable to set for auth, and hands you the next
18
+ command — no docs required.
19
+
20
+ ## Tiers
21
+
22
+ Listing a profile here is not an endorsement of the product behind it. Trust is
23
+ a **tier**, shown next to every entry, not a gate on being listed.
24
+
25
+ | Tier | Directory | What it means |
26
+ | --- | --- | --- |
27
+ | **Verified** | `verified/` | A maintainer confirmed the spec loads and the auth flow works. |
28
+ | **Community** | `community/` | Contributed via PR and passed automated validation. Not manually vetted. |
29
+
30
+ Independently of the tier, a profile can carry a **domain-verified** badge
31
+ (`✓ dns` in listings): the owner of the API's domain has published a DNS TXT
32
+ record naming the maintainer, so the listing is backed by domain control, not
33
+ just a self-declared `source`. See [Domain verification](#domain-verification).
34
+
35
+ New submissions go to `community/`; a maintainer may promote a profile to
36
+ `verified/` after checking it end to end. Installing a **community** profile
37
+ prompts for confirmation (it shows the `base_url` your credentials would be
38
+ sent to); **verified** profiles install without a prompt. Use `--yes` to skip
39
+ the prompt in scripts.
40
+
41
+ ## Freshness
42
+
43
+ The bundled catalog is the offline baseline. On top of it, `catalog list`,
44
+ `search`, `show` and `install` fetch [`index.json`](index.json) from this
45
+ repository's `main` branch (3 s timeout, cached for an hour under
46
+ `~/.cache/openapi-cli4ai/`) and merge it in, so a profile is usable as soon as
47
+ its PR merges. Any failure falls back to the cached index, then to the bundled
48
+ copy, silently. Every remote entry is re-checked with the offline validator
49
+ before use; a bad one is skipped. Set `OAC_CATALOG_OFFLINE=1` to never fetch.
50
+
51
+ `index.json` is generated from the TOML files by `openapi-cli4ai catalog index`
52
+ and committed; CI fails a PR whose index is stale.
53
+
54
+ ## Profile format
55
+
56
+ One profile per file, named `<slug>.toml`:
57
+
58
+ ```toml
59
+ name = "example" # must match the file name
60
+ description = "Example REST API" # one factual line, no marketing
61
+ maintainer = "your-github-username"
62
+ source = "https://example.com/docs" # the API's official docs (same domain as base_url)
63
+
64
+ base_url = "https://api.example.com"
65
+ openapi_url = "https://api.example.com/openapi.json" # or: openapi_path = "/openapi.json"
66
+
67
+ domain_verified = true # optional: see "Domain verification" below
68
+
69
+ [auth]
70
+ type = "api-key" # none | bearer | oidc | device | api-key | basic
71
+ env_var = "EXAMPLE_API_KEY" # reference secrets by env var — never inline them
72
+ header = "x-api-key"
73
+ ```
74
+
75
+ Auth field names match the CLI's runtime config; see
76
+ [`../examples/profiles.toml.example`](../examples/profiles.toml.example) for
77
+ each auth type.
78
+
79
+ ## Domain verification
80
+
81
+ To claim the `✓ dns` badge, set `domain_verified = true` in the profile and have
82
+ the API's domain owner publish this TXT record (registrable domain of
83
+ `base_url`, e.g. `api.acme.co.uk` → `acme.co.uk`):
84
+
85
+ ```
86
+ _openapi-cli4ai.acme.co.uk. IN TXT "github=<maintainer>"
87
+ ```
88
+
89
+ `<maintainer>` must equal the profile's `maintainer` field (case-insensitive).
90
+ `catalog validate` resolves the record and fails the PR if it is missing or
91
+ names someone else; the claim is re-checked on every profile change. Remove the
92
+ claim (or the record) to drop the badge. The lookup needs `dnspython`, which
93
+ `uv sync` installs for contributors; end users never resolve DNS.
94
+
95
+ ## Contributing a profile
96
+
97
+ 1. Create `community/<slug>.toml` in the format above.
98
+ 2. Reference secrets only via `*_env_var` fields — **never commit a token**.
99
+ 3. Keep `description` factual. `source` must be the API's own developer/docs
100
+ URL (same registrable domain as `base_url`).
101
+
102
+ Validate before opening the PR — the CLI is the single source of truth — and
103
+ regenerate the index:
104
+
105
+ ```bash
106
+ uv run openapi-cli4ai catalog validate profiles/community/<slug>.toml
107
+ uv run openapi-cli4ai catalog index
108
+ ```
109
+
110
+ CI runs `catalog validate --all` on every PR touching `profiles/`. It checks the
111
+ fields and (Public Suffix List-based) domain ownership, fetches the OpenAPI spec
112
+ behind SSRF guards with size/redirect/timeout limits, rejects inlined secrets,
113
+ and flags prompt-injection markers in the description. Errors are posted inline
114
+ on the PR.
@@ -0,0 +1,2 @@
1
+ # Community-contributed profiles land here (see ../README.md).
2
+ # This file keeps the directory tracked while it is empty.
@@ -0,0 +1,11 @@
1
+ name = "xquik"
2
+ description = "Xquik REST API"
3
+ maintainer = "kriptoburak"
4
+ source = "https://xquik.com"
5
+ base_url = "https://xquik.com"
6
+ openapi_url = "https://xquik.com/openapi.json"
7
+
8
+ [auth]
9
+ type = "api-key"
10
+ env_var = "XQUIK_API_KEY"
11
+ header = "x-api-key"
@@ -0,0 +1,33 @@
1
+ {
2
+ "profiles": [
3
+ {
4
+ "auth": {
5
+ "env_var": "XQUIK_API_KEY",
6
+ "header": "x-api-key",
7
+ "type": "api-key"
8
+ },
9
+ "base_url": "https://xquik.com",
10
+ "description": "Xquik REST API",
11
+ "maintainer": "kriptoburak",
12
+ "name": "xquik",
13
+ "openapi_url": "https://xquik.com/openapi.json",
14
+ "slug": "xquik",
15
+ "source": "https://xquik.com",
16
+ "tier": "community"
17
+ },
18
+ {
19
+ "auth": {
20
+ "type": "none"
21
+ },
22
+ "base_url": "https://petstore3.swagger.io/api/v3",
23
+ "description": "Swagger Petstore \u2014 the OpenAPI 3 sample API",
24
+ "maintainer": "dbgorilla",
25
+ "name": "petstore",
26
+ "openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json",
27
+ "slug": "petstore",
28
+ "source": "https://petstore3.swagger.io",
29
+ "tier": "verified"
30
+ }
31
+ ],
32
+ "version": 1
33
+ }
@@ -0,0 +1,11 @@
1
+ # Verified catalog profile. Validated by a maintainer against a live spec.
2
+ name = "petstore"
3
+ description = "Swagger Petstore — the OpenAPI 3 sample API"
4
+ maintainer = "dbgorilla"
5
+ source = "https://petstore3.swagger.io"
6
+
7
+ base_url = "https://petstore3.swagger.io/api/v3"
8
+ openapi_url = "https://petstore3.swagger.io/api/v3/openapi.json"
9
+
10
+ [auth]
11
+ type = "none"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "openapi-cli4ai"
7
- version = "0.5.1"
7
+ version = "0.7.0"
8
8
  description = "Turn any REST API with an OpenAPI spec into an AI-ready CLI"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -30,6 +30,7 @@ dependencies = [
30
30
  "pyyaml>=6,<7",
31
31
  "python-dotenv>=1.0,<2",
32
32
  "tomli-w>=1.0,<2",
33
+ "publicsuffix2>=2.2,<3",
33
34
  ]
34
35
 
35
36
  [project.urls]
@@ -45,17 +46,37 @@ dev = [
45
46
  "pytest>=8",
46
47
  "pytest-cov>=5",
47
48
  "click>=8,<9",
49
+ # CI runs the locked ruff (uv run) instead of uvx-latest so lint is
50
+ # deterministic; a bump comes via Dependabot and is gated by CI.
51
+ "ruff>=0.15,<0.17",
48
52
  "mypy>=1.10,<3",
49
53
  "types-PyYAML>=6,<7",
54
+ # DNS TXT domain verification in `catalog validate` (CI + contributors);
55
+ # end users never resolve DNS, so this is not a runtime dependency.
56
+ "dnspython>=2.4,<3",
50
57
  ]
51
58
 
52
59
  [tool.hatch.build.targets.wheel]
53
60
  packages = ["src/openapi_cli4ai"]
54
61
 
62
+ # Bundle the profile catalog into the wheel so `catalog` commands work offline.
63
+ [tool.hatch.build.targets.wheel.force-include]
64
+ "profiles/verified" = "openapi_cli4ai/_catalog/verified"
65
+ "profiles/community" = "openapi_cli4ai/_catalog/community"
66
+
55
67
  [tool.ruff]
56
68
  line-length = 120
57
69
  target-version = "py311"
58
70
 
71
+ [tool.ruff.lint]
72
+ # Rule set = ruff's defaults for the locked version. Drift is guarded by the
73
+ # version cap on the dev dependency plus uv.lock; a ruff bump arrives via
74
+ # Dependabot and is gated by CI, so new rules are adopted deliberately.
75
+ # RUF059 (unused unpacked variable) is ignored in tests: fixtures return
76
+ # tuples like `mod, tmp_path, cache_dir = tmp_config` and unpacking the
77
+ # full shape is clearer than `_`-prefixing whichever half a test skips.
78
+ per-file-ignores = { "tests/**" = ["RUF059"] }
79
+
59
80
  [tool.pytest.ini_options]
60
81
  testpaths = ["tests"]
61
82
  markers = ["integration: live API tests (may be slow)"]
@@ -3,4 +3,4 @@
3
3
  from openapi_cli4ai.cli import VERSION, app
4
4
 
5
5
  __version__ = VERSION
6
- __all__ = ["app", "VERSION", "__version__"]
6
+ __all__ = ["VERSION", "__version__", "app"]
@@ -0,0 +1,22 @@
1
+ """Runtime settings set once by the root command and read by every layer.
2
+
3
+ Module globals on purpose: the CLI is a single process handling one command,
4
+ and tests set these directly (``_state._verbose_mode = True``).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ _insecure_mode = False
10
+ _verbose_mode = False
11
+ _timeout_seconds = 60.0
12
+ _max_retries = 0
13
+ _profile_override: str | None = None
14
+
15
+
16
+ def set_insecure_mode(insecure: bool) -> None:
17
+ global _insecure_mode
18
+ _insecure_mode = insecure
19
+
20
+
21
+ def get_verify_ssl() -> bool:
22
+ return not _insecure_mode
@@ -0,0 +1,16 @@
1
+ """Shared Rich consoles and the verbose logger."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from rich.console import Console
6
+
7
+ from openapi_cli4ai import _state
8
+
9
+ console = Console()
10
+ err_console = Console(stderr=True)
11
+
12
+
13
+ def _verbose(msg: str) -> None:
14
+ """Print a verbose message to stderr if verbose mode is enabled."""
15
+ if _state._verbose_mode:
16
+ err_console.print(f"[dim]> {msg}[/dim]")