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 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
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org)
41
+ [![License](https://img.shields.io/badge/license-Apache--2.0-green)](LICENSE)
42
+ [![PyPI](https://img.shields.io/pypi/v/oed-cli)](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).