oed — openEuler Infra command line. Auto-discovered, AI-friendly.
Project description
oed-cli
oed — one CLI for openEuler community services. Auto-discovered, JSON-first, AI-friendly. Built for humans and LLM agents.
oed doesn't ship a static list of commands. It reads the openEuler Infra
Discovery Service at runtime
and builds its entire command surface dynamically. When a new service ships,
oed picks it up automatically — no upgrade required.
Why oed?
oed exists to solve one specific problem: a CLI that talks to many
evolving services without multiplying that complexity. Shipping a
hand-written client per service per version is exactly the versioning
pressure that the
Zylos API versioning research
warns against — and for AI agent consumers that pressure is acute: a
renamed field silently breaks a tool call, a new required parameter
crashes an otherwise healthy workflow. oed sidesteps the whole problem
by being one client that discovers every service at runtime, so the
only thing that has to be versioned is the gateway's OpenAPI spec itself.
- Zero boilerplate. No copy-pasted OpenAPI clients, no per-service
SDKs, no
--datato escape, noUser-Agentheaders to remember. - Runtime discovery. The
oed cve-sa-backend --helplist you saw above is built fromhttps://api-gateway.osinfra.cn/discovery/apison every call. New services, new endpoints, and new schema fields show up without anoedupgrade. A 10-minute cache keeps CI bursts cheap;oed cache refreshforces an immediate re-fetch when you know the gateway just shipped. - AI-friendly output. Single JSON object on stdout, deterministic
exit codes (
0success ·1user ·2network ·3upstream ·4not found). Logs and progress go to stderr so| jqis always safe. - Claude / Cursor ready. Ships with
.claude/skills/oed-cli/SKILL.mdso agents know how to use it without a custom prompt.
Install
pip install oed-cli
Or in an isolated environment (recommended for CI):
pipx install oed-cli
From source:
git clone https://gitee.com/openeuler/oed-cli && cd oed-cli
pip install -e .
Verify:
oed --version # → oed, version 0.2.0
Quick start: install, then call
pip install oed-cli
oed cve-sa-backend getSecurityNoticeByCveId --cve-id CVE-2019-10082
That's it. oed discovers the service from the gateway, pulls its OpenAPI
spec, derives --cve-id from the declared query parameter, fills WAF-safe
browser headers, and ships the request through the production gateway:
{
"ok": true,
"status": 200,
"url": "https://apig.osinfra.cn/cve-security-notice-server/securitynotice/getByCveId",
"response": {
"code": 0,
"result": [
{
"cveId": "CVE-2019-10082",
"affectedProduct": "openEuler-20.03-LTS",
"affectedComponent": "httpd-2.4.34-18",
"announcementTime": "2020-05-13",
...
}
]
}
}
Output is plain JSON on stdout — pipe straight into jq:
oed cve-sa-backend getSecurityNoticeByCveId --cve-id CVE-2019-10082 \
| jq '.response.result[0] | {cveId, affectedProduct, affectedComponent}'
{
"cveId": "CVE-2019-10082",
"affectedProduct": "openEuler-20.03-LTS",
"affectedComponent": "httpd-2.4.34-18"
}
Want to look around first? (optional)
oed info # gateway snapshot: services_total, cache status, community
oed services # full service list — pick one to call next
oed <service> --help # list that service's operations
oed <service> <operation> --help # see every flag for one operation
oed <service> <operation> --dry-run --… # preview the request, no network
The first oed <service> <operation> call after install will pull a fresh
discovery feed + that service's OpenAPI spec; subsequent calls within
10 minutes reuse the local cache. Run oed cache refresh to force a
re-fetch when you know the gateway just shipped something new.
"What flags does this operation take?" → --help
Don't guess. Each operation auto-derives its own flags from the OpenAPI schema:
oed cve-sa-backend getSecurityNoticeByCveId --help
{
"help_for": "getSecurityNoticeByCveId",
"operation_id_raw": "getSecurityNoticeByCveId",
"method": "GET",
"path": "/cve-security-notice-server/securitynotice/getByCveId",
"url": "https://apig.osinfra.cn/cve-security-notice-server/securitynotice/getByCveId",
"parameters": [
{
"name": "cveId",
"in": "query",
"required": true,
"flag": "--cve-id",
"alt_flag": "--cveId",
"type": "string",
"description": "CVE ID"
}
],
"usage": "oed <service> getSecurityNoticeByCveId --cve-id <value> [--dry-run]"
}
Same idea at the service level — list every operation with one flag each:
oed cve-sa-backend --help | jq '.operations | length'
# → 37
More examples
# Dry-run — preview the request without hitting the network
oed cve-sa-backend getSecurityNoticeByCveId --cve-id 1 --dry-run
# Path placeholder — `--id` is a path param, auto-substituted into the URL
oed software-package-server getSoftwarePackage --id 12345 --language zh_CN
# Integer / number flags auto-coerce from string
oed software-package-server listSoftwarePackages --page-num 1 --count-per-page 5
# POST with JSON body — Content-Type auto-set
oed software-package-server applyNewSoftwarePackage \
--json '{"pkg_name":"demo","version":"1.0.0"}'
# Bulk JSON for scripts — `--params` is the escape hatch when you have many fields
oed cve-sa-backend getSecurityNoticeByCveId --params '{"cveId":"1"}'
# Raw OpenAPI spec, for debugging
oed schema cve-sa-backend | jq '.paths | keys'
Local development
Clone and install (editable)
git clone https://gitee.com/openeuler/oed-cli && cd oed-cli
pip install -e ".[dev]"
pip install -e . makes source edits take effect on the next oed
invocation. Drop it with pip uninstall oed-cli when you're done.
Run the tests (~0.3 s, fully offline)
python -m pytest -q
41 tests cover v0.1 + v0.2 dispatch, the operation-help cheatsheet,
per-parameter flag coercion, the API_-prefix alias, and every exit
code path. They monkeypatch the discovery layer so no gateway access
is needed.
Smoke-test against the live gateway
# 1) Health check
oed info
# → {"ok": true, "services_total": 8, "community": "openeuler", ...}
# 2) Real CVE query (canonical end-to-end test)
oed cve-sa-backend getSecurityNoticeByCveId --cve-id CVE-2019-10082
# 3) Dry-run to inspect URL + body without hitting the network
oed cve-sa-backend getSecurityNoticeByCveId --cve-id 1 --dry-run
# 4) Verify the canonical URL routing
oed cve-sa-backend getSecurityNoticeByCveId --dry-run --cve-id 1 \
| jq '.url'
# → "https://apig.osinfra.cn/cve-security-notice-server/securitynotice/getByCveId"
Cache debugging
oed cache show # path, size, age, TTL
oed cache refresh # force re-fetch the discovery feed
oed cache clear # delete the cache file
Cache lives at ~/.cache/oed-cli/ (XDG) or
C:\Users\<you>\AppData\Local\oed-cli\cache\ on Windows. Override with
OED_CACHE_DIR=.... Per-service OpenAPI specs are cached next to it
under specs/<community>/<service>.json with the same 10-minute TTL.
Keeping in sync with the gateway
oed does not warm up a fresh discovery feed on every invocation — only
the first command in a 10-minute window actually hits the gateway, the rest
read from ~/.cache/oed-cli/discovery.json. That keeps CI scripts that run
dozens of oed calls from hammering the gateway, and it means oed --help
and oed --version never touch the network.
When the gateway adds a new service or operation, refresh the cache by hand:
# Fastest: drop the 10-minute TTL and re-pull the discovery feed
oed cache refresh
# Or, nuke and re-fetch from a known-clean state
oed cache clear && oed info
# Then confirm the new service is now visible
oed services | jq -r '.[] | .service_name'
The first call to a brand-new service will additionally pull its OpenAPI
spec into specs/<community>/<service>.json (also 10-minute TTL); after
that it's reused like any other spec.
Why this isn't automatic. openEuler Infra services are added on a
weekly-to-quarterly cadence via review, not minute-to-minute. Auto-refreshing
on every oed invocation would burn a network round-trip per CLI call for
no practical benefit. The TTL exists to absorb CI bursts, not to delay
visibility of new endpoints.
Future — oed whatsnew (planned) will diff the freshly-pulled feed
against the previous cache and print only what changed, so you don't have
to eyeball oed services output every week.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
ModuleNotFoundError: oed_cli |
not installed in env | pip install -e . |
oed info hangs or waf_block exit 2 |
gateway unreachable / WAF | confirm curl https://api-gateway.osinfra.cn; see context/discoverAPI.md §6 |
| Chinese output garbled on Windows | console codepage not UTF-8 | chcp 65001, or pipe | python, or PYTHONIOENCODING=utf-8 oed … |
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 |
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 |
Offline mode
oed --help, oed info (uses cached feed), pytest, and any
command against a service whose spec is in the local cache all work
without network. To run oed from source without installing:
python -m oed_cli --help
# or
python -c "from oed_cli.main import main; sys.argv = ['oed','--help']; main()"
Documentation
- Design doc — architecture, command contract, exit codes, packaging, roadmap.
- Local testing guide — install, smoke test, every per-parameter flag demo.
- Discovery API reference — the data source
oedconsumes, plus WAF caveats.
Contributing
Issues and patches welcome on gitee.com/openeuler/oed-cli.
Dev install:
pip install -e ".[dev]"
pytest
ruff check src tests
License
Apache-2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oed_cli-0.2.1.tar.gz.
File metadata
- Download URL: oed_cli-0.2.1.tar.gz
- Upload date:
- Size: 37.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c9fbdf05f46bc5b254011c5c5d725144d7bf02d9c5b59905f657b1831217435b
|
|
| MD5 |
8c3c17b775dbecb471d612646d1ff091
|
|
| BLAKE2b-256 |
e5369c1810ed4ac95a75aac33b51373dcb00b487f82792f5053069bb60692ddc
|
File details
Details for the file oed_cli-0.2.1-py3-none-any.whl.
File metadata
- Download URL: oed_cli-0.2.1-py3-none-any.whl
- Upload date:
- Size: 29.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c377413506853c07591119f43655f30cc72fe9fdc653d07783f09ffb09c3b9b9
|
|
| MD5 |
3e8a60f983bfeb7f833ad35d88764f38
|
|
| BLAKE2b-256 |
3a8eb86ccc0ec822e2fa4f16b4e10f6875614cd4863d04843311ac3f8e6057a2
|