Skip to main content

odsbox-cli

An interactive command-line tool for inspecting ASAM ODS servers with odsbox, JAQuel queries, model-aware autocomplete, and real pandas DataFrames. Python 3.14 or newer is required.

Install and start

Development checkout:

uv sync --locked --all-groups
uv run odsbox-cli

Equivalent launchers are uv run python -m odsbox_cli. Every Python/tool command in this project runs through uv.

Build and install an independent command:

uv build --no-sources
uv tool install .\dist\odsbox_cli-0.1.0-py3-none-any.whl
odsbox-cli --help
odsbox-cli --version

Use the wheel filename produced by your build, not a stale version. After the project is published, uv tool install odsbox-cli installs from PyPI. There is no claim that publication has already occurred.

Local Windows executable

On Windows, build a standalone, single-file console executable:

uv run --locked --group executable python scripts\build_exe.py
.\dist\odsbox-cli.exe --help
.\dist\odsbox-cli.exe --version

The build requires uv and Python 3.14+, but the resulting executable does not require Python or uv on the destination machine. Build on Windows for the target architecture; cross-compiling a Windows .exe from Linux/macOS is not supported. Run it from PowerShell or another terminal for the interactive console.

To bundle optional plotting/table previews, include the existing extra:

uv run --locked --group executable --extra plot python scripts\build_exe.py

PyInstaller bundles dependencies installed in the build environment; use a clean environment without plot for a base-only build. The executable contains a fixed set of Python modules, not a general-purpose Python installation: arbitrary imports in console/startup scripts are limited to bundled modules. Single-file startup extracts support files to a temporary directory.

The process writes dist\odsbox-cli.exe (replacing a previous local executable), keeps generated build files under build\executable, and smoke-tests version, help, connection templates, pandas/protobuf, the Windows keyring backend import, startup success/failure, and bundled plotting offline outside the checkout. It does not read credentials or connect to a server. This is a local, unsigned build only: it does not change versions, tag, publish, or alter wheel/sdist builds. Windows may warn about running an unsigned executable.

Prepared startup commands

uv run odsbox-cli --script ".\explore.ods"
uv run odsbox-cli -s ".\explore.ods" --no-history

The UTF-8 file (optional BOM) uses CLI syntax, for example:

use My server
selected_id = 42
runs = db.find({
    AoMeasurement: {id: selected_id},
    $options: {$rowlimit: 20}
})
runs.head()

On success, a terminal opens the usual prompt with the same db, variables and last DataFrame _. The first error stops the script and reports its filename and line; with terminal stdin, the prompt opens for debugging without discarding successful setup. There is no rollback or automatic retry, including for partial side effects inside a failed statement. Final normal exit returns 1 after a script error, even if debugging succeeds; startup Ctrl-C returns 130. Without terminal stdin, the process exits after the script (0 on success) and never consumes additional piped input. File/encoding errors follow the same rules.

Multiline bracketed expressions work. End compound Python blocks (for, if, def, etc.) with an unindented blank line or EOF. Keep internal blank lines indented, and put CLI commands on their own at top level:

names = []
for name in ["Speed", "Time"]:
    names.append(name)
    print(name)

help bulk

Relative paths use the process working directory, not the script's directory. exit/quit intentionally end the entire session, skipping the remaining file and prompt; explicit Python SystemExit is honored. Cleanup runs on final exit. Script contents bypass persistent history; commands entered in the prompt still follow normal history settings. GUI previews require an interactive backend and terminal; they update once the prompt is idle. Use explicit save= without a terminal. Synchronous commands can temporarily pause GUI interaction.

Only run files you trust: this executes Python with your permissions, not a sandbox. --script cannot be combined with the process-level connections subcommand. Existing piped input without --script still continues after errors and returns a nonzero status. See help script. This is separate from db.script_starter(), which generates a standalone Python/uv project.

Connections: reuse odsbox-pilot settings

The CLI discovers ~/.ods-pilot/servers.json read-only. Names can be used immediately; no import is necessary for local pilot connections.

ods> connections list
ods> use My server
ods> disconnect

To copy portable connections or a saved pilot registry into CLI-owned storage:

uv run odsbox-cli connections import ".\lab.ods-pilot.con.json"
uv run odsbox-cli connections import ".\lab.ods-pilot.con.toml" --name "Test server"
uv run odsbox-cli connections import-pilot --path "C:\config\servers.json"

The same connections ... commands work inside the shell. Quoted Windows paths retain their backslashes. Imports are saved atomically to ~/.odsbox-cli/servers.json; pilot files are never modified. Repeated identical imports report no changes. Conflicting names/IDs/settings are rejected, not silently overwritten. Rename a portable import with --name.

Portable files use pilot's JSON/TOML format, without secrets:

{
  "name": "Lab",
  "url": "https://ods.example.invalid/api",
  "auth_type": "basic",
  "username": "alice",
  "verify_certificate": true,
  "context_variables": {"LANGUAGE": "en"}
}

Generate a connection template

uv run odsbox-cli --generate-connection-template oidc "C:\temp\my_server.ods-pilot.con.toml"
# Edit the generated file, then import it:
uv run odsbox-cli connections import "C:\temp\my_server.ods-pilot.con.toml"
uv run odsbox-cli

In the prompt, select the connection with use my_server (or the name you configured). The generated name is the filename before its first dot, stripping all extensions: my_server.ods-pilot.con.toml becomes my_server. The derived name must not be empty and can be edited. Template modes are basic, m2m and oidc. Generation supports TOML only: the destination must end in .ods-pilot.con.toml (case-insensitive). The parent directory must already exist; an existing file, directory or symlink is never overwritten. Choose a new path to regenerate.

The file includes comments and the fields for just that authentication mode. Replace the example.invalid server/token URLs and YOUR_USERNAME / YOUR_CLIENT_ID as applicable. Optional context variables are a string-to-string table; M2M scopes are a string list, initially empty. Certificate verification stays enabled. OIDC uses the default redirect URI http://127.0.0.1:12345 with insecure redirects disabled; register the URI with your identity provider and adjust optional webfinger settings only if needed.

Do not add passwords or client secrets to the file. Basic/M2M retrieve secrets at connection time from the pilot-compatible OS keyring, or a hidden terminal prompt. OIDC opens browser login only when connecting. Template generation itself does not read registries, history or keyring, connect, open a browser, import the file or start the interactive shell. It exits after printing the file location and edit/import instructions. Do not combine it with --script or the process-level connections subcommand. ATFX templates are not offered because this CLI cannot open ATFX. See help connection_template and help connections in the prompt.

Supported settings match pilot:

Authentication Settings
basic username; password in keyring or hidden prompt
m2m token_endpoint, client_id, optional scope string list; client secret in keyring or hidden prompt
oidc client_id, redirect_uri, optional webfinger_path_prefix, redirect_url_allow_insecure; browser login
All server modes verify_certificate (default true), context_variables (string-to-string map)
atfx Entries can be imported/listed; opening local ATFX files is not supported

OIDC defaults match pilot: redirect URI http://127.0.0.1:12345, insecure redirects disabled. Configure insecure loopback redirects explicitly only when your provider requires them. TLS verification is never disabled automatically.

Passwords/client secrets reuse OS keyring service ods-pilot, account URL::username-or-client_id. Missing credentials prompt without echo and are not saved automatically. If a secure terminal is unavailable, the CLI refuses an echoed-input fallback. Keyring backend problems are reported. Listing/importing/help do not require credentials or connect to a server.

A failed server switch leaves the old connection usable. Successful switches and disconnect/exit clean up sessions; logout problems produce warnings.

Recover from an expired session

Lab> db.reconnect()

This explicitly opens a new session for the selected server, using its current registry settings and the normal authentication flow. Keyring credentials are reused when available; a hidden prompt or OIDC login may be needed again. No plaintext credentials are retained solely for reconnect.

The cached model is refreshed. The existing db object, local variables, DataFrames and _ are preserved. The method returns None and does not print a result. On connection/authentication, registry or model-loading failure, the error is reported and the old session reference/model remain intact, although that session may still be expired. Logout errors from the old session produce warnings without undoing a successful replacement.

There is no automatic reconnect or retry: after reconnecting, explicitly rerun the failed operation. Server-side transient state is not restored. Before selecting a server, or after disconnect, use use <server> first.

Inline help

At startup, type help. Topics work without a connection:

help queries
help model
help results
help connections
help completion
help bulk
help history
help reconnect
help script_starter

Process commands also provide --help, including uv run odsbox-cli connections import --help. Python calls such as help(pd.DataFrame) remain ordinary Python.

Generate a standalone exploration project

folder = db.script_starter(r"C:\temp")
# No application launch:
folder = db.script_starter(r"C:\temp", open_folder=False)

The argument is a parent directory. Each call creates a new connection-named, timestamped subfolder containing pyproject.toml, script.py, README.md and .vscode/mcp.json, following odsbox-pilot's starter layout. It returns the created Path; existing projects are never overwritten. By default the folder opens in VS Code, or the file explorer if unavailable. Opening failures warn without discarding the generated project.

The project is independent of odsbox-cli:

Set-Location <generated-folder>
uv sync
uv run python script.py

It uses the current successful connection's settings snapshot (including TLS, context variables and authentication options), not subsequent registry edits. Basic/M2M secrets are retrieved only when running the generated script, using pilot's ods-pilot OS keyring service/account. OIDC opens browser login. Follow the generated README to set up credentials. Generation never reads credentials, installs dependencies, runs the script, contacts the server, or starts MCP.

The last successful db.find query is captured as detached, evaluated JSON data; later mutation of the original dict does not change it. Failed reads leave the previous captured query intact. Model/bulk reads do not replace it. A successful use, reconnect, or disconnect clears it. Without a saved query, the script lists model entities instead. Non-JSON-compatible Python values cause an explicit generation error, without changing whether the original query succeeds.

When run, the script replays that query exactly: fetch limits are preserved, not silently inserted. Its 20-row output preview is not a network limit. Review the query before execution. Generated settings and query literals may contain sensitive application data even though authentication secrets are excluded; inspect all files before sharing.

The MCP file matches pilot's nonsecret environment mapping. It does not carry context variables or M2M scope (the Python script does). MCP installation/login/ launch remain separate explicit steps. The starter does not capture bulk reads, DataFrames, local Python variables or live session tokens. db, _, and existing results are unchanged. See help script_starter.

Inspect the model first

ods> use Lab
Lab> db.model.find()
Lab> db.model.find("AoMeasurement")
Lab> db.model.relations("AoMeasurement")

The first table lists entity IDs, application names and base names. Entity details list attributes, types, enum names, unit IDs and flags. Relations list targets and cardinalities (range_max == -1 means unbounded). An empty result preserves its table columns.

Application schemas differ. Prefer a specific application entity name if several entities derive from AoMeasurement; autocomplete annotates ambiguous base names rather than guessing their attributes. Manually entered base-name queries retain odsbox's resolution behavior. The application model is cached; call db.reconnect() to see external schema changes.

Write JAQuel queries

db.find({AoMeasurement: {}})
db.find({AoMeasurement: {}, $attributes: {id: 1, name: 1}, $options: {$rowlimit: 20}})
db.find({AoMeasurement: {name: {$like: "*test*"}}})

These are schema-dependent examples; discover the real names through the model. The JAQuel examples explain projections, filters, ordering, grouping, and options.

Inline syntax is relaxed Python, not full JSON5:

  • Identifier keys, $ operator keys and dotted keys need no quotes.
  • String values and keys with spaces/special characters must be quoted.
  • true, false, null work alongside True, False, None.
  • Python expressions are allowed as values; expressions execute once.
  • Use # comments. // is always floor division, not a comment.
  • Trailing commas and multiline queries work; JavaScript block comments do not.
  • For computed dictionary keys, create an ordinary Python dict first.
selected_id = 42
db.find({AoMeasurement: {id: selected_id}})
db.find({AoMeasurement: {}, $attributes: {test.name: 1}})
query = {"AoMeasurement": {"id": selected_id}}
db.find(query)

The dotted projection works only if test is a relation on that entity. The CLI delegates JAQuel validation/execution to odsbox instead of implementing a second query engine.

Autocomplete and editing

Suggestions appear while typing:

use Tes<Tab>                                  registered server names
db.find({AoMea<Tab>                           application/base entities
db.find({AoMeasurement: {na<Tab>               attributes and relations
db.find({AoMeasurement: {test.na<Tab>          relation-target attributes
db.find({AoMeasurement: {}, $att<Tab>          query clauses
db.find({AoMeasurement: {}, $options: {$row<Tab>
db.model.find("AoMea<Tab>                     model arguments
runs["na<Tab>                                actual DataFrame column names

Tab selects/cycles candidates; Shift-Tab cycles backward. Enter accepts a selected candidate without running the command; press Enter again to submit. Escape cancels completion. Incomplete input continues onto another line. Ctrl-C cancels input/execution, Ctrl-D on an empty buffer exits.

Typing an opening (, [, {, ' or " inserts its matching closer and leaves the cursor inside. Pairing is conservative: only at end-of-input or before whitespace/closing punctuation, not inside existing strings or comments, or for escaped quotes. Quotes after ordinary words remain literal, while Python string prefixes such as r and f are supported. Formatted/triple strings are treated conservatively, without special interpolation editing.

This is insertion only: typing a closer inserts it normally even if one is already ahead, and Backspace deletes one character, not the whole pair. Use Right Arrow to move past the auto-inserted closer. There is no selection wrapping or automatic indentation. Completion insertion and bracketed paste remain verbatim; Ctrl-R search text is not paired. Terminals without bracketed paste cannot reliably distinguish pasted keystrokes from typing.

Completion understands nested projections, filters, ordering/grouping paths, enumeration labels on known attributes, direct DataFrame column selections, and simple sort_values("...") column arguments. It does not evaluate arbitrary Python expressions or complete pandas query-string syntax. It uses cached model/local metadata only: no server queries, credential lookups or distinct value fetching while typing. db.bulk( also completes unused keyword parameters, without consulting the server model.

Interactive input has basic syntax highlighting for commands, relaxed query keys, strings, numbers, keywords, punctuation and comments. Set NO_COLOR (any value) to disable input colors. Tables, exports and piped output are unchanged.

The latest 1000 commands are stored in plaintext at ~/.odsbox-cli/history.sqlite3, shared across sessions. Up/Down recalls commands at multiline boundaries. Ctrl-R starts case-insensitive reverse search; repeat Ctrl-R for older matches. Enter recalls the match for editing, without executing it; Enter again submits. Escape or Ctrl-C restores your original draft and cursor. A failed search displays No history match.

Blank commands, consecutive duplicates and commands whose first character is a space are excluded. Search text and cancelled drafts are not recorded. Hidden credential prompts do not enter command history. Queries and Python expressions may contain sensitive data: prefix them with a space, or start with uv run odsbox-cli --no-history to prevent all disk-history reads and writes. Disabled history remains bounded in memory for that process. Noninteractive input, help and connection-management commands do not open history storage. Storage failures warn once and fall back to memory. Delete the history file with shells closed to clear stored commands.

Read bulk measurement data

db.bulk(...) delegates to con_i.bulk.data_read(...) with its full signature:

db.bulk(
    submatrix_iid,
    column_patterns=None,
    column_patterns_case_insensitive=False,
    date_as_timestamp=True,
    set_independent_as_index=True,
    values_start=0,
    values_limit=0,
    *,
    raise_on_partial_result=False,
    valid_flag=None,
)

Discover the application entity derived from AoSubmatrix using db.model.find(), then use a bounded db.find(...) query to obtain a real ID. For example (replace 42 with that ID):

samples = db.bulk(42, column_patterns=["Time", "*speed*"], values_start=0, values_limit=100)
samples.head()
samples.attrs["unit_names"]
samples.to_csv("samples.csv")

Patterns support * and ?; case-insensitive matching is opt-in. Sample offsets are zero-based. values_limit=0 is unlimited, so provide a limit for exploratory reads. Dates become timestamps by default, and a single independent column becomes the index. The original DataFrame is returned, preserving index, dtypes and attrs, including unit_names and partial_result; successful calls update _, even when assigned.

Partial results produce a warning. Set raise_on_partial_result=True to propagate odsbox's completeness error instead. Other upstream errors propagate without replacing the prior result. valid_flag masks invalid values, not rows: None disables masking; integers specify a quality mask. Upstream currently maps both True and False to mask 15; the CLI forwards them unchanged. Bulk calls use normal Python syntax, not relaxed JAQuel keys.

Sample limiting relies on odsbox and the server's sequence handling. On the demo server, some stored columns return full sequences despite a limit while implicit columns honor it; reading those together can raise ValueError: All arrays must be of the same length. This upstream behavior is not silently sliced or hidden. Select individual column patterns to inspect affected data, and verify returned lengths before assuming the server honored a limit.

Work with complete DataFrames

Optional graphical previews

Install the single optional matplotlib extra:

uv sync --locked --extra plot
# Independent installation:
uv tool install "odsbox-cli[plot]"

The base CLI does not require matplotlib. Preview tools live under gui, not top-level plot or table names:

gui.plot()  # last DataFrame (_)
gui.plot(samples, columns=["Speed", "Torque"])
gui.plot(samples, x="Time", columns=["Speed"], max_points=2000)
gui.table(samples)  # original values, paged
gui.table(samples, columns=["Time", "Speed", "Torque"])
gui.plot(samples, columns=["Speed"], save="speed.png")
gui.table(samples, save="samples-page.png")

These are schema-dependent column examples. Both methods return None and leave the DataFrame, index, attrs and _ untouched. They work entirely locally: no additional ODS reads. help gui, help plot, and help table work offline.

Plot: by default, display the first 8 real numeric signal columns (excluding an explicit x column); select others with columns=. Boolean, complex and text signals are not plotted. Each signal has its own stacked axes with shared x and available unit metadata. The index supplies x, or use a numeric/datetime/timedelta column. Unsupported index types use row positions with a notice. Original row order is retained, not sorted; timedelta axes use seconds.

Large signals use a min/max envelope capped at 2,000 points per series by default, including endpoints. This preserves isolated spikes in finite-data bins better than every-Nth sampling. Missing/nonfinite samples conservatively break their entire bin; extrema in those bins can be omitted. This is not a lossless analysis/resampling result. max_points must be an integer >=6. Zoom/pan uses the fixed reduced data, not a full-resolution reload.

Reduction scans each selected signal once: startup still depends on row count and hardware, but rendering is bounded. It does not construct a melted or copied version of the entire frame. Partial-result metadata is displayed prominently.

Table: render original values, not plot-reduced samples, in 50-row by 8-column pages, plus the original index. Previous/Next controls navigate row and column pages, with disabled boundary buttons. Only visible cells are formatted; long/multiline values are shortened/escaped for display (up to 40 characters, further bounded by column width). Floats display 6 significant digits, without changing stored precision. No sorting, filtering or editing widgets: use pandas first. Pages reference the existing frame, not an immutable full-data snapshot. Saving a table writes only its first page; pandas exports remain the full-data export mechanism.

Windows and headless use: interactive previews require a working matplotlib desktop GUI backend, such as TkAgg with tkinter supplied by your Python installation. No Qt/PySide dependency is added. Windows are nonblocking and their events are pumped while the terminal prompt is idle, including while editing/searching. Long synchronous reads or Python commands can temporarily pause GUI interaction. Exit closes only helper-owned windows. Existing backend selection is respected; notebook/browser backends are not supported by this terminal integration.

Scripts/piped input should use save=. In headless environments select Agg (for example, $env:MPLBACKEND = "Agg" in PowerShell) and save an image. Missing dependencies, unsupported backends and save errors are reported, not silently treated as successful displays. No windows open automatically for bulk reads or other results.

Pandas operations and terminal output

runs = db.find({AoMeasurement: {}, $attributes: {id: 1, name: 1}})
runs.head(5)
runs.tail(5)
runs[["id", "name"]].sort_values("name")
runs[runs["id"] > 100]
runs.query("id > 100")
runs.to_csv("runs.csv", index=False)
runs.to_json("runs.json", orient="records", date_format="iso")

pd is pandas. Named variables last for this shell session. _ holds the last successful query/model result, even after assignment. Displaying another DataFrame expression updates _; scalar output does not. Assignments do not print. Failed queries leave the previous result intact. If a query succeeds but a chained local operation fails, the successful query remains available.

Tables preview at most 20 rows, with terminal-aware column and cell-width limits, full shape and truncation notices. All fetched data is retained. There is no implicit server row limit: use $options: {$rowlimit: N} to bound network transfers and memory. Export calls use pandas directly and errors are not hidden; choose paths deliberately because pandas can overwrite files.

This is a trusted local Python console, not a sandbox. There are no CLI commands for modifying server data/models, but arbitrary Python has your normal permissions. Use server credentials with read-only permissions if enforcement is needed. No automatic reconnect/retry replays queries.

Development and verification

uv sync --locked --all-groups
uv run --locked ruff check .
uv run --locked ruff format --check .
uv run --locked mypy src scripts tests
uv run --locked pytest --cov=odsbox_cli --cov-report=term-missing --cov-report=xml --cov-fail-under=85
uv run --locked python scripts\package_smoke.py

Mypy checks application code and scripts strictly. Tests allow unannotated functions and calls to untyped helpers, but their bodies and all other strict checks remain enabled. uv run --locked mypy defaults to src scripts tests; uv run --locked mypy tests checks tests using the local source tree. Ruff lint and formatting cover the entire repository, including tests.

Offline tests enforce at least 85% total package line coverage, including unimported modules. Linux/Windows CI both enforce this gate. There is no per-file or branch-percentage requirement. Missing-line and XML reports are produced. Clean wheel and sdist installations are tested outside the checkout. Semantic-release stamping is tested in a disposable Git repository without pushing or publishing.

Explicit live tests (local-only)

Normal tests exclude the integration marker. To run read-only integration tests against the demo server, set process-local variables:

$env:ODSBOX_TEST_URL = "https://docker.peak-solution.de:10032/api"
$env:ODSBOX_TEST_USERNAME = "<demo username>"
$credential = Read-Host "Demo password" -AsSecureString
$env:ODSBOX_TEST_PASSWORD = [System.Net.NetworkCredential]::new("", $credential).Password
uv run --locked pytest -m integration -q
Remove-Item Env:\ODSBOX_TEST_PASSWORD

Do not commit passwords or save them in command history/configuration files. Explicit live runs fail on missing settings, authentication/network errors, or assertions; they never quietly skip/fall back to mocks. They open/close ODS sessions, read the model, issue explicitly bounded reads, and export only to temporary local files. They do not modify server data/models or your pilot registry/keyring. Session DELETE/logout is necessary lifecycle cleanup. Basic authentication is live-tested; M2M/OIDC are covered with mocked factories. Live tests run separately and do not inflate offline coverage.

CI and releases

The pipeline adapts odsbox-diff: Ruff, mypy (strict source/scripts, relaxed body-checked tests), offline pytest-cov, Linux/Windows build/install checks, dependency audit, and weekly uv/GitHub Actions Dependabot updates.

On main, after all checks pass, python-semantic-release uses Conventional Commits (feat:, fix:, breaking changes) to stamp project.version, update uv.lock without upgrading unrelated dependencies, create a changelog/version commit and v{version} tag, and build distributions once. The installed version comes from package metadata. Commits requiring no new version do not publish. The release job publishes the exact new distributions with uv publish --trusted-publishing always.

Maintainers must configure the real GitHub remote, main-branch rules and Actions write permissions, a protected pypi environment, PyPI package-name ownership and a trusted publisher matching the repository/workflow/environment. No long-lived PyPI token is required. This setup cannot be completed just by adding YAML.

If publication fails after a version/tag has been created, do not blindly bump another version. Inspect the failed job and PyPI for partial uploads, then recover the same tagged artifacts through an approved trusted-publisher job. Never overwrite an existing PyPI distribution. Actual publishing is not performed by local verification.

License

Apache-2.0. See LICENSE.

Metadata

Release files for odsbox-cli 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for odsbox-cli 1.0.0
File Size Uploaded Provenance Actions
odsbox_cli-1.0.0.tar.gz 61.7 kB Without provenance Details for odsbox_cli-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for odsbox-cli 1.0.0
File Interpreter ABI Platform Actions
odsbox_cli-1.0.0-py3-none-any.whl Python 3 none any Details for odsbox_cli-1.0.0-py3-none-any.whl

Total release size: 117.1 kB

Release files / odsbox_cli-1.0.0.tar.gz

Download URL odsbox_cli-1.0.0.tar.gz
Size 61.7 kB
Tags Source
SHA-256 checksum
How to use checksums
13c2fe86a85d4a63c09a179891fc3d277089ed91cb5dae0de6fdbabefbabf228
BLAKE2b-256 checksum
How to use checksums
fffce1b89cc6111e45fc973e77007a4ba1c93e45fa1b28ba0c003c1472aac6bf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / odsbox_cli-1.0.0-py3-none-any.whl

Download URL odsbox_cli-1.0.0-py3-none-any.whl
Size 55.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e223cb8e01a4fec434ce888bebcfa128565eca081ba01f6a05632b3ccea93c3d
BLAKE2b-256 checksum
How to use checksums
cd631f77b728f99379bd5ba622c8c80ed9a1e850efa074eb80137f02517440ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for odsbox-cli 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page