oed-cli 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oed_cli-0.2.0/LICENSE +17 -0
- oed_cli-0.2.0/PKG-INFO +373 -0
- oed_cli-0.2.0/README.md +339 -0
- oed_cli-0.2.0/pyproject.toml +68 -0
- oed_cli-0.2.0/setup.cfg +4 -0
- oed_cli-0.2.0/src/oed_cli/__init__.py +6 -0
- oed_cli-0.2.0/src/oed_cli/__main__.py +6 -0
- oed_cli-0.2.0/src/oed_cli/cli.py +278 -0
- oed_cli-0.2.0/src/oed_cli/discovery.py +168 -0
- oed_cli-0.2.0/src/oed_cli/dynamic.py +473 -0
- oed_cli-0.2.0/src/oed_cli/errors.py +45 -0
- oed_cli-0.2.0/src/oed_cli/http.py +150 -0
- oed_cli-0.2.0/src/oed_cli/invoke.py +305 -0
- oed_cli-0.2.0/src/oed_cli/main.py +409 -0
- oed_cli-0.2.0/src/oed_cli/py.typed +0 -0
- oed_cli-0.2.0/src/oed_cli.egg-info/PKG-INFO +373 -0
- oed_cli-0.2.0/src/oed_cli.egg-info/SOURCES.txt +21 -0
- oed_cli-0.2.0/src/oed_cli.egg-info/dependency_links.txt +1 -0
- oed_cli-0.2.0/src/oed_cli.egg-info/entry_points.txt +2 -0
- oed_cli-0.2.0/src/oed_cli.egg-info/requires.txt +8 -0
- oed_cli-0.2.0/src/oed_cli.egg-info/top_level.txt +1 -0
- oed_cli-0.2.0/tests/test_cli.py +294 -0
- oed_cli-0.2.0/tests/test_dynamic.py +737 -0
oed_cli-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 oed-cli contributors
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
oed_cli-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oed-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: oed — openEuler Infra command line. Auto-discovered, AI-friendly.
|
|
5
|
+
Author: oed-cli contributors
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://gitee.com/openeuler/oed-cli
|
|
8
|
+
Project-URL: Documentation, https://gitee.com/openeuler/oed-cli/blob/master/docs/cli-design.md
|
|
9
|
+
Project-URL: Bug Tracker, https://gitee.com/openeuler/oed-cli/issues
|
|
10
|
+
Keywords: openEuler,cli,discovery,ai-agent,openapi
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: click>=8.1
|
|
27
|
+
Requires-Dist: httpx>=0.27
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
31
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# oed-cli
|
|
36
|
+
|
|
37
|
+
> **oed** — one CLI for openEuler community services. Auto-discovered,
|
|
38
|
+
> JSON-first, AI-friendly. Built for humans and LLM agents.
|
|
39
|
+
|
|
40
|
+
[](https://www.python.org)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
[](https://pypi.org/project/oed-cli/)
|
|
43
|
+
|
|
44
|
+
`oed` doesn't ship a static list of commands. It reads the openEuler Infra
|
|
45
|
+
[Discovery Service](https://api-gateway.osinfra.cn/discovery/apis) at runtime
|
|
46
|
+
and builds its entire command surface dynamically. When a new service ships,
|
|
47
|
+
`oed` picks it up automatically — no upgrade required.
|
|
48
|
+
## Why oed?
|
|
49
|
+
|
|
50
|
+
`oed` exists to solve one specific problem: a CLI that talks to *many
|
|
51
|
+
evolving services* without multiplying that complexity. Shipping a
|
|
52
|
+
hand-written client per service per version is exactly the versioning
|
|
53
|
+
pressure that the
|
|
54
|
+
[Zylos API versioning research](https://zylos.ai/research/2026-05-20-api-versioning-strategies-multi-agent-platforms/)
|
|
55
|
+
warns against — and for AI agent consumers that pressure is acute: a
|
|
56
|
+
renamed field silently breaks a tool call, a new required parameter
|
|
57
|
+
crashes an otherwise healthy workflow. `oed` sidesteps the whole problem
|
|
58
|
+
by being one client that **discovers every service at runtime**, so the
|
|
59
|
+
only thing that has to be versioned is the gateway's OpenAPI spec itself.
|
|
60
|
+
|
|
61
|
+
- **Zero boilerplate.** No copy-pasted OpenAPI clients, no per-service
|
|
62
|
+
SDKs, no `--data` to escape, no `User-Agent` headers to remember.
|
|
63
|
+
- **Runtime discovery.** The `oed cve-sa-backend --help` list you saw
|
|
64
|
+
above is built from `https://api-gateway.osinfra.cn/discovery/apis` on
|
|
65
|
+
every call. New services, new endpoints, and new schema fields show up
|
|
66
|
+
without an `oed` upgrade. A 10-minute cache keeps CI bursts cheap;
|
|
67
|
+
`oed cache refresh` forces an immediate re-fetch when you know the
|
|
68
|
+
gateway just shipped.
|
|
69
|
+
- **AI-friendly output.** Single JSON object on stdout, deterministic
|
|
70
|
+
exit codes (`0` success · `1` user · `2` network · `3` upstream · `4`
|
|
71
|
+
not found). Logs and progress go to stderr so `| jq` is always safe.
|
|
72
|
+
- **Claude / Cursor ready.** Ships with
|
|
73
|
+
[`.claude/skills/oed-cli/SKILL.md`](.claude/skills/oed-cli/SKILL.md)
|
|
74
|
+
so agents know how to use it without a custom prompt.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Install
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install oed-cli
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or in an isolated environment (recommended for CI):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pipx install oed-cli
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
From source:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
git clone https://gitee.com/openeuler/oed-cli && cd oed-cli
|
|
94
|
+
pip install -e .
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Verify:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
oed --version # → oed, version 0.2.0
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Quick start: install, then call
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pip install oed-cli
|
|
109
|
+
oed cve-sa-backend getSecurityNoticeByCveId --cve-id CVE-2019-10082
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
That's it. `oed` discovers the service from the gateway, pulls its OpenAPI
|
|
113
|
+
spec, derives `--cve-id` from the declared `query` parameter, fills WAF-safe
|
|
114
|
+
browser headers, and ships the request through the production gateway:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"ok": true,
|
|
119
|
+
"status": 200,
|
|
120
|
+
"url": "https://apig.osinfra.cn/cve-security-notice-server/securitynotice/getByCveId",
|
|
121
|
+
"response": {
|
|
122
|
+
"code": 0,
|
|
123
|
+
"result": [
|
|
124
|
+
{
|
|
125
|
+
"cveId": "CVE-2019-10082",
|
|
126
|
+
"affectedProduct": "openEuler-20.03-LTS",
|
|
127
|
+
"affectedComponent": "httpd-2.4.34-18",
|
|
128
|
+
"announcementTime": "2020-05-13",
|
|
129
|
+
...
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Output is plain JSON on stdout — pipe straight into `jq`:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
oed cve-sa-backend getSecurityNoticeByCveId --cve-id CVE-2019-10082 \
|
|
140
|
+
| jq '.response.result[0] | {cveId, affectedProduct, affectedComponent}'
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"cveId": "CVE-2019-10082",
|
|
146
|
+
"affectedProduct": "openEuler-20.03-LTS",
|
|
147
|
+
"affectedComponent": "httpd-2.4.34-18"
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Want to look around first? (optional)
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
oed info # gateway snapshot: services_total, cache status, community
|
|
155
|
+
oed services # full service list — pick one to call next
|
|
156
|
+
oed <service> --help # list that service's operations
|
|
157
|
+
oed <service> <operation> --help # see every flag for one operation
|
|
158
|
+
oed <service> <operation> --dry-run --… # preview the request, no network
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The first `oed <service> <operation>` call after install will pull a fresh
|
|
162
|
+
discovery feed + that service's OpenAPI spec; subsequent calls within
|
|
163
|
+
10 minutes reuse the local cache. Run `oed cache refresh` to force a
|
|
164
|
+
re-fetch when you know the gateway just shipped something new.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## "What flags does this operation take?" → `--help`
|
|
169
|
+
|
|
170
|
+
Don't guess. Each operation auto-derives its own flags from the OpenAPI
|
|
171
|
+
schema:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
oed cve-sa-backend getSecurityNoticeByCveId --help
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"help_for": "getSecurityNoticeByCveId",
|
|
180
|
+
"operation_id_raw": "getSecurityNoticeByCveId",
|
|
181
|
+
"method": "GET",
|
|
182
|
+
"path": "/cve-security-notice-server/securitynotice/getByCveId",
|
|
183
|
+
"url": "https://apig.osinfra.cn/cve-security-notice-server/securitynotice/getByCveId",
|
|
184
|
+
"parameters": [
|
|
185
|
+
{
|
|
186
|
+
"name": "cveId",
|
|
187
|
+
"in": "query",
|
|
188
|
+
"required": true,
|
|
189
|
+
"flag": "--cve-id",
|
|
190
|
+
"alt_flag": "--cveId",
|
|
191
|
+
"type": "string",
|
|
192
|
+
"description": "CVE ID"
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
"usage": "oed <service> getSecurityNoticeByCveId --cve-id <value> [--dry-run]"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Same idea at the service level — list every operation with one flag each:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
oed cve-sa-backend --help | jq '.operations | length'
|
|
203
|
+
# → 37
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## More examples
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Dry-run — preview the request without hitting the network
|
|
212
|
+
oed cve-sa-backend getSecurityNoticeByCveId --cve-id 1 --dry-run
|
|
213
|
+
|
|
214
|
+
# Path placeholder — `--id` is a path param, auto-substituted into the URL
|
|
215
|
+
oed software-package-server getSoftwarePackage --id 12345 --language zh_CN
|
|
216
|
+
|
|
217
|
+
# Integer / number flags auto-coerce from string
|
|
218
|
+
oed software-package-server listSoftwarePackages --page-num 1 --count-per-page 5
|
|
219
|
+
|
|
220
|
+
# POST with JSON body — Content-Type auto-set
|
|
221
|
+
oed software-package-server applyNewSoftwarePackage \
|
|
222
|
+
--json '{"pkg_name":"demo","version":"1.0.0"}'
|
|
223
|
+
|
|
224
|
+
# Bulk JSON for scripts — `--params` is the escape hatch when you have many fields
|
|
225
|
+
oed cve-sa-backend getSecurityNoticeByCveId --params '{"cveId":"1"}'
|
|
226
|
+
|
|
227
|
+
# Raw OpenAPI spec, for debugging
|
|
228
|
+
oed schema cve-sa-backend | jq '.paths | keys'
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Local development
|
|
234
|
+
|
|
235
|
+
### Clone and install (editable)
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
git clone https://gitee.com/openeuler/oed-cli && cd oed-cli
|
|
239
|
+
pip install -e ".[dev]"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`pip install -e .` makes source edits take effect on the next `oed`
|
|
243
|
+
invocation. Drop it with `pip uninstall oed-cli` when you're done.
|
|
244
|
+
|
|
245
|
+
### Run the tests (~0.3 s, fully offline)
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
python -m pytest -q
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
41 tests cover v0.1 + v0.2 dispatch, the operation-help cheatsheet,
|
|
252
|
+
per-parameter flag coercion, the `API_`-prefix alias, and every exit
|
|
253
|
+
code path. They monkeypatch the discovery layer so no gateway access
|
|
254
|
+
is needed.
|
|
255
|
+
|
|
256
|
+
### Smoke-test against the live gateway
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# 1) Health check
|
|
260
|
+
oed info
|
|
261
|
+
# → {"ok": true, "services_total": 8, "community": "openeuler", ...}
|
|
262
|
+
|
|
263
|
+
# 2) Real CVE query (canonical end-to-end test)
|
|
264
|
+
oed cve-sa-backend getSecurityNoticeByCveId --cve-id CVE-2019-10082
|
|
265
|
+
|
|
266
|
+
# 3) Dry-run to inspect URL + body without hitting the network
|
|
267
|
+
oed cve-sa-backend getSecurityNoticeByCveId --cve-id 1 --dry-run
|
|
268
|
+
|
|
269
|
+
# 4) Verify the canonical URL routing
|
|
270
|
+
oed cve-sa-backend getSecurityNoticeByCveId --dry-run --cve-id 1 \
|
|
271
|
+
| jq '.url'
|
|
272
|
+
# → "https://apig.osinfra.cn/cve-security-notice-server/securitynotice/getByCveId"
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Cache debugging
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
oed cache show # path, size, age, TTL
|
|
279
|
+
oed cache refresh # force re-fetch the discovery feed
|
|
280
|
+
oed cache clear # delete the cache file
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Cache lives at `~/.cache/oed-cli/` (XDG) or
|
|
284
|
+
`C:\Users\<you>\AppData\Local\oed-cli\cache\` on Windows. Override with
|
|
285
|
+
`OED_CACHE_DIR=...`. Per-service OpenAPI specs are cached next to it
|
|
286
|
+
under `specs/<community>/<service>.json` with the same 10-minute TTL.
|
|
287
|
+
|
|
288
|
+
### Keeping in sync with the gateway
|
|
289
|
+
|
|
290
|
+
`oed` does **not** warm up a fresh discovery feed on every invocation — only
|
|
291
|
+
the first command in a 10-minute window actually hits the gateway, the rest
|
|
292
|
+
read from `~/.cache/oed-cli/discovery.json`. That keeps CI scripts that run
|
|
293
|
+
dozens of `oed` calls from hammering the gateway, and it means `oed --help`
|
|
294
|
+
and `oed --version` never touch the network.
|
|
295
|
+
|
|
296
|
+
When the gateway adds a new service or operation, refresh the cache by hand:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
# Fastest: drop the 10-minute TTL and re-pull the discovery feed
|
|
300
|
+
oed cache refresh
|
|
301
|
+
|
|
302
|
+
# Or, nuke and re-fetch from a known-clean state
|
|
303
|
+
oed cache clear && oed info
|
|
304
|
+
|
|
305
|
+
# Then confirm the new service is now visible
|
|
306
|
+
oed services | jq -r '.[] | .service_name'
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The first call to a brand-new service will additionally pull its OpenAPI
|
|
310
|
+
spec into `specs/<community>/<service>.json` (also 10-minute TTL); after
|
|
311
|
+
that it's reused like any other spec.
|
|
312
|
+
|
|
313
|
+
**Why this isn't automatic.** openEuler Infra services are added on a
|
|
314
|
+
weekly-to-quarterly cadence via review, not minute-to-minute. Auto-refreshing
|
|
315
|
+
on every `oed` invocation would burn a network round-trip per CLI call for
|
|
316
|
+
no practical benefit. The TTL exists to absorb CI bursts, not to delay
|
|
317
|
+
visibility of new endpoints.
|
|
318
|
+
|
|
319
|
+
**Future** — `oed whatsnew` (planned) will diff the freshly-pulled feed
|
|
320
|
+
against the previous cache and print only what changed, so you don't have
|
|
321
|
+
to eyeball `oed services` output every week.
|
|
322
|
+
|
|
323
|
+
### Common errors
|
|
324
|
+
|
|
325
|
+
| Symptom | Cause | Fix |
|
|
326
|
+
| -------------------------------------------------- | ------------------------------ | --------------------------------------- |
|
|
327
|
+
| `ModuleNotFoundError: oed_cli` | not installed in env | `pip install -e .` |
|
|
328
|
+
| `oed info` hangs or `waf_block` exit 2 | gateway unreachable / WAF | confirm `curl https://api-gateway.osinfra.cn`; see `context/discoverAPI.md` §6 |
|
|
329
|
+
| Chinese output garbled on Windows | console codepage not UTF-8 | `chcp 65001`, or pipe `\| python`, or `PYTHONIOENCODING=utf-8 oed …` |
|
|
330
|
+
| `error="spec_missing"` (exit 4) on a known service | upstream hasn't published the spec yet | wait for the gateway-side OpenAPI yaml; nothing to do on the oed side |
|
|
331
|
+
| A `cve-sa-backend` call exits 2 (`waf_block`) | spec points to a `.test.osinfra.cn` host | already handled — oed routes via `apig.osinfra.cn` regardless of the spec's `x-apigateway-backend.httpEndpoints.address` |
|
|
332
|
+
|
|
333
|
+
### Offline mode
|
|
334
|
+
|
|
335
|
+
`oed --help`, `oed info` (uses cached feed), `pytest`, and any
|
|
336
|
+
command against a service whose spec is in the local cache all work
|
|
337
|
+
without network. To run `oed` from source without installing:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
python -m oed_cli --help
|
|
341
|
+
# or
|
|
342
|
+
python -c "from oed_cli.main import main; sys.argv = ['oed','--help']; main()"
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Documentation
|
|
348
|
+
|
|
349
|
+
- [Design doc](docs/cli-design.md) — architecture, command contract, exit
|
|
350
|
+
codes, packaging, roadmap.
|
|
351
|
+
- [Local testing guide](docs/local-testing.md) — install, smoke test, every
|
|
352
|
+
per-parameter flag demo.
|
|
353
|
+
- [Discovery API reference](context/discoverAPI.md) — the data source `oed`
|
|
354
|
+
consumes, plus WAF caveats.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Contributing
|
|
359
|
+
|
|
360
|
+
Issues and patches welcome on
|
|
361
|
+
[gitee.com/openeuler/oed-cli](https://gitee.com/openeuler/oed-cli).
|
|
362
|
+
|
|
363
|
+
Dev install:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
pip install -e ".[dev]"
|
|
367
|
+
pytest
|
|
368
|
+
ruff check src tests
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## License
|
|
372
|
+
|
|
373
|
+
Apache-2.0. See [LICENSE](LICENSE).
|