Skip to content

Releases: mpenet/ezu

v1.0.0-beta10

v1.0.0-beta10 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Oct 20:25
Immutable release. Only release title and notes can be modified.

Changed

  • Field options take their value after a space too, as other options do: --q.limit 10 as well as --q.limit=10 (--h., --b., --server., --ssl. alike); --q.limit alone said unknown option.
  • The Exoscale example's compute instance-pool evict takes several instances, repeated: instances=ID instances=ID.

v1.0.0-beta9

v1.0.0-beta9 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 19 Oct 21:44
Immutable release. Only release title and notes can be modified.

Added

  • Exoscale with ezu: setting up the Exoscale example as an exo command, with the signing plugin, where to keep the key and secret, completion, questions, and MCP.
  • Secrets from where they are kept: an auth parameter, or a profile's headers or query value, can be {"env": NAME}, {"file": PATH} (a file of yours no one else can read) or {"cmd": [COMMAND, ARG…]} (a keychain, a password manager), read once per command when needed, never for a dry run; errors never show a value, and headers and query values read this way count as credentials.
  • x-ezu-complete on a workflow input names a workflow listing its values (and a selector picking them): completion runs it with the document's profile, keeps the values a minute, and offers them, exo compute instance stop <Tab> offering your instances. The Exoscale example completes instances, security groups, clusters, nodepools, DNS domains, database services and more.
  • ezu mcp --arazzo offers the command words of a document and of the documents it mounts as tools (workflow.compute_instance_create), each run in its own document; workflows that ask first (x-ezu-confirm) only with --allow-writes. --trust-sources applies to mcp too. A document's mcp.workflows in the configuration lists which to offer ("compute instance *"), writes included.
  • x-ezu-confirm: "Delete instance {$inputs.instance}?" on a workflow asks before it runs; only yes goes ahead, --yes answers for scripts, and without a terminal ezu stops. The Exoscale example asks before deleting, resetting, reverting, evicting, stopping and rebooting.
  • --select works on a workflow's outputs (ezu az exo compute instance list --select=.instances[].name), and as a table (csv, tsv), outputs that are one list print a row per item.
  • arazzo-output on a named workflow document in the configuration sets how its runs print (progress, say); --arazzo-output still wins.
  • More options can be set in the configuration: timing (top level or profile), no-paginate and check-response (profile), and max-gotos (named workflow document). The command line still wins; the configuration documents which options stay on it, and why.

Fixed

  • az fetched the OpenAPI specs of a document's sources on every run, never using the spec cache op uses: with the Exoscale example, that was about 0.6 s of each command's 0.7 s. They are now cached for an hour, as op's are, and --reload and --cache-ttl work on az.

v1.0.0-beta8

v1.0.0-beta8 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 19 Oct 11:33
Immutable release. Only release title and notes can be modified.

Changed

  • A JSONPath Selector Object's result has a type that depends on the query, not on the data: a singular query (names and indexes only, RFC 9535) gives a value or nothing, and any other query, a filter say, always gives an array, empty when nothing matches. A filter matching one node used to give that node's value: take it with #/0 ($steps.find.outputs.id#/0).
  • Workflow payloads leave out array items that resolve to nothing (an input that isn't given), and an array whose items all do, as they already did for object members: security-groups: [{id: $steps.sg.outputs.id}] is left out rather than sent as [{}]. A null written as an item is still sent.
  • A field of an array property (--b.tags=a, tags=a for a workflow input) adds an item, typed by the items schema, so repeating it builds a list; a JSON array ([…]) is still the whole list.
  • Workflow inputs are name=value arguments (ezu az vm create name=web1 size=medium), typed from the inputs schema; -i, --i./--input., and -d/--b. for workflows are gone (they say so). Arguments starting with - are always ezu's options and name=value ones always inputs, so neither can shadow the other; an unknown input name stops with a suggestion. Positional inputs are those a workflow declares in x-ezu-args, no longer guessed from the path parameters of its steps. Completion offers NAME= and allowed values; az --check checks x-ezu-args.
  • atlas is now ezu (絵図, a pictorial map): the command, the Lua modules (require "ezu"), the rock and the Homebrew formula. Configuration and cache are in ~/.config/ezu and ~/.cache/ezu; copy ~/.config/atlas there to keep your profiles.

Added

  • After --, a workflow's arguments are values whatever their shape, so a positional value like a base64 string ending with = can be given as is (vm decrypt -- aGVsbG8=).
  • Command words: x-ezu-command: instance create on a workflow runs it as vm instance create …, and on an Arazzo source mounts that document's commands under its words (exo compute instance create web1), loading it only when a command line reaches it. Groups list their commands, unknown words get a suggestion, completion and --help follow the words, and az --check checks them. Workflows without words still run by id.
  • Workflows of other Arazzo documents: a source of type: arazzo and $sourceDescriptions.NAME.ID wherever a workflowId goes (steps, goto and retry, dependsOn). The workflow runs in its own document with the run's profile and options; documents and OpenAPI sources are loaded once per run, and cycles across documents are stopped. Only sources next to the document (relative, on the same origin) are loaded, unless --trust-sources or trust-sources in the document's configuration. az --check checks those documents too.
  • doc/examples/exoscale.arazzo.yaml: Exoscale's exo command line as workflows, to use as a command of its own: about 400 workflows over 360 of the API's 374 operations, in documents mounted as exo compute (with exo compute sks), exo dbaas, exo dns, exo iam, exo kms, exo ai and exo networking. They take resources by name or id and wait for each job (exo compute instance create web1), with shared steps in a document of their own.
  • Plugins: Lua or Fennel files listed in the configuration's plugins provide authentication methods, which profiles use by name. A method is a function signing each request in-process, with helpers for SHA-256, HMAC-SHA256, base64, hex, JSON and URLs; a plugin's path is absolute (or starts with ~/), and its file and directories must belong to the user (or root, for directories) and be writable only by them. The binary carries the Fennel compiler (compressed, loaded only for a Fennel plugin), and doc/examples/plugins/exoscale.fnl signs Exoscale's API requests.
  • Releases include .deb and .rpm packages of the Linux binaries (x86_64 and arm64), with bash, zsh and fish completion and the licenses.
  • A document named in the configuration can be a command of its own: a link to ezu named after it (ezu az vm --link prints how to make one) runs its workflows (vm create name=web1), with its own completion (vm completion fish).
  • --check-response on op reports how the response differs from the spec (an undeclared status or content type, a body the schema doesn't allow, checked as a response) and exits 1 when it does.
  • ezu --licenses prints the licenses of the software the binaries contain (Lua, libcurl, Mbed TLS, LuaSocket, lyaml, LibYAML, lunajson, Fennel, and zlib and musl on Linux, and the Unicode data); they are also in THIRD-PARTY.md, attached to each release.
  • ezu az FILE [WORKFLOW] --check checks a document against its sources without running it: operations and workflows that don't exist, parameters and bodies, references to steps, outputs, inputs, workflows and components, conditions, regexes and JSONPath queries, and actions. It exits 1 on an error.
  • --until CONDITION on op and http sends the request again, every --every seconds, until the condition holds (written as in workflows: $response.body#/state == 'ready', $statusCode == 404), then prints the last response and exits 0. After --max-time seconds (default 600) it gives up saying what the condition found. The condition is checked before anything is sent; a POST or PATCH is only sent again with --retry-unsafe.
  • --timing logs a line per request: method, URL, status and how long each phase took (dns, tcp, tls, server, download, total, and the redirects that led there). -v shows the same timings for each exchange, and the library's responses carry them as :timings.

Fixed

  • A replacement's JSON Pointer one past the end of an array (/items/2 with two items) appended silently; only - appends now, and any other index past the end is an error.
  • A {$expression} embedded in a JSONPath query was pasted as text: a value with a quote broke the query, and a crafted one could change what it selects. Inside a string literal, the value is now escaped for its quotes; elsewhere a string goes in as text, as before, and other values as JSON literals.
  • Keywords next to a $ref in a schema were ignored: a description beside it now replaces the target's (in help too), and other keywords apply along with the target's (nullable: true beside a $ref, a common OpenAPI 3.0 pattern, included).
  • On macOS, a plugin file with extended attributes (@ in ls -l) and an access list was loaded: ls shows @ in place of the + that marks an access list. ezu now reads the access list itself (ls -le) and refuses such a plugin.
  • A workflow failing on a regex or JSONPath condition shows the condition with its {$expressions} replaced, and says it doesn't match or selects nothing in its context; a failure inside a workflow run by a step names that workflow (step 'find' (instance-id) › step 'list' failed: …) rather than repeating failed:.
  • On a terminal that doesn't report its width, the lines of workflow progress were cut down to …; they now fit 80 columns.

Removed

  • $find(…), an ezu-only runtime expression: Arazzo has no such expression, so documents using it ran nowhere else. A JSONPath Selector Object does the same and more: {context: $response.body, selector: "$.users[?@.name == '{$inputs.name}'].id", type: jsonpath}.

v1.0.0-beta7

v1.0.0-beta7 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Oct 15:24
Immutable release. Only release title and notes can be modified.

Changed

  • --arazzo-output progress prints a line per step once its success criteria are checked: ✓ or ✗, its position (2/5), what it calls, its time and the response's status. A step polled with goto or retry stays on one ↻ line with the wait and the criterion that failed, then ends with the number of attempts and the total time; the steps of a workflow run by another one are indented. On a terminal, lines are updated in place and fit its width. Step outputs are no longer printed there; steps and full print them.
  • A failed workflow says which criterion failed and the values it compared: step 'find' failed: $statusCode == 200 ($statusCode is 404); HTTP 404 {…}, the body shortened to 500 characters. Warnings no longer land on a step's line while it is updated in place.
  • The library's on-step events: :workflow when a workflow starts, :running, :done after the step's criteria are checked (with :passed, :failure and :error) and :action; each carries the workflow, its depth, and the step's index, count and attempt.
  • In an Arazzo payload, an object member whose own members all resolve to nothing (inputs that aren't given) is left out, like a single member, rather than sent empty. The payload itself, array elements and objects written as {} stay objects.

Fixed

  • Progress output marked a step ✓ when its response arrived, before its success criteria were checked, so a failing step showed ✓.

v1.0.0-beta6

v1.0.0-beta6 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Oct 12:14
Immutable release. Only release title and notes can be modified.

Added

  • Arazzo 1.1 documents: Selector Objects (JSONPath or JSON pointer, with runtime expressions embedded in {}) wherever values are evaluated; jsonpath criteria and Expression Type Objects; {$expression} in regex and JSONPath conditions; replacements with targetSelectorType: jsonpath; in: querystring parameters; action parameters as the inputs of the workflow an action runs; step timeout and dependsOn; $self, $outputs.NAME, $workflows.ID.inputs.NAME and source description fields. AsyncAPI steps and XPath are refused.
  • JSONPath (RFC 9535), passing its compliance test suite, with I-Regexp (RFC 9485) patterns for match() and search(), matched in linear time.
  • Operations take :querystring, the whole query string (OpenAPI 3.2).
  • An Arazzo example that creates an Exoscale instance from names.

Changed

  • An Arazzo condition that can't be evaluated fails, with a warning on stderr saying why, instead of failing the workflow.
  • Arazzo regex conditions are ECMAScript regular expressions, as in the specification's examples (^2\d{2}$), rather than Lua patterns; they run in linear time, so back-references and lookaround are refused.
  • The binaries are about a quarter smaller (1.5 MB on macOS, 1.9 MB on Linux): the program is embedded as stripped Lua bytecode, the C libraries are optimized for size, and the linker drops the code nothing calls.
  • An Arazzo step's timeout is in milliseconds, as Arazzo 1.1 defines it, rather than seconds.

Fixed

  • Ctrl-C ends the binary at once, with the usual signal status; it could be taken for an error, caught, and atlas would go on, then exit 0.

v1.0.0-beta5

v1.0.0-beta5 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Oct 06:55
Immutable release. Only release title and notes can be modified.

Added

  • atlas http [METHOD] URL-or-path: a request without a spec, with a profile's headers, authentication and TLS settings, retries, pagination, -v, --dry-run/--curl and the usual output and exit codes. A path is appended to the profile's API URL; a profile's credentials are only sent to its API's origin.
  • atlas.base-url: the URL a client of a schema sends requests to.
  • Spec discovery: a profile with base-url and no schema finds its spec through the base URL's Link header (service-desc, service-doc, describedby), openapi.json and openapi.yaml under it, or the base URL itself; the location is cached. Restish profiles without spec_files import without a warning.
  • Streamed responses: server-sent events and JSON Lines are printed record by record as they arrive (with --select applied to each), and with --output=raw and no --select, a body of a type other than JSON or YAML is written as it comes. Requests accepting text/event-stream may stay idle between events. The transport takes a :stream function for this.
  • --output=table aligns wide characters (CJK, most emoji) and combining marks by their display width; csv and tsv escape control characters on a terminal.
  • --select filters: [?state==running], [?size>10], [?tags.env=="prod"], [?tags]; negative indexes ([-1]); ..key for a field at any depth.
  • --output=table, csv and tsv for arrays of objects, with the columns a {a,b} selection picks.
  • atlas mcp PROFILE: an MCP server over stdio (protocol 2026-07-28, and the earlier ones that open with initialize). The operations become tools, authenticated with the profile: the read-only ones by default, all with --allow-writes, or those the profile's mcp.operations lists (names, or patterns like list-*). --arazzo=NAME adds the workflows of an Arazzo context as tools.
  • YAML responses are decoded, and JSON Lines responses (application/x-ndjson, application/jsonl) become an array of their lines' values.

Changed

  • --output=raw prints a selected array of strings, numbers or booleans one value per line.
  • Output escapes C1 control characters and the bidirectional overrides (U+202A–202E, U+2066–2069) as well as ASCII control characters, so text from an API can neither drive nor reorder what the terminal shows.
  • The documentation is split into a page per topic, with an index in doc/README.md; make test checks its links.

Fixed

  • Content types that only start like JSON (application/json-seq, application/jsonl) are no longer decoded as JSON.
  • YAML with an alias inside its own anchor, or whose aliases expand past a million nodes, is refused instead of crashing atlas or making it spin; this covers YAML specs and Arazzo documents too.
  • OpenAPI 3.0 nullable allows null whatever else the schema says (enum, allOf…), and read-only properties declared in allOf members are not required in requests.
  • External auth tools run with stdin from /dev/null (unless given the request to sign), and the browser of OAuth flows with stdin and stdout on /dev/null.

v1.0.0-beta4

v1.0.0-beta4 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Oct 20:41
Immutable release. Only release title and notes can be modified.

Changed

  • The binaries use mbedTLS (3.6 LTS) for TLS instead of OpenSSL: about 2 MB instead of 5.5 MB. Certificate verification, client certificates (certificate/key, RSA or EC) and the CA file and directory work as before; TLS 1.0 and 1.1 are refused. The Linux binaries no longer contain OpenSSL 3.3, which is out of support; they are built on Alpine 3.24, as static position-independent executables.

v1.0.0-beta3

v1.0.0-beta3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Oct 09:31
Immutable release. Only release title and notes can be modified.

Added

  • Homebrew: brew install mpenet/tap/atlas (macOS and Linux, with shell completion); releases update the tap.

Fixed

  • A spec behind an auth proxy is fetched again with credentials when the first request is redirected to a sign-in page on another origin, as after a 401; -v shows where a redirected request ended.

v1.0.0-beta2

v1.0.0-beta2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Oct 08:33
Immutable release. Only release title and notes can be modified.

The first beta with release binaries: 1.0.0-beta1 was published without them, and its release can no longer take files. The code is that of 1.0.0-beta1: see its section of CHANGELOG.md for the changes since 1.0.0-alpha14.

Fixed

  • The release workflow publishes a release only once all its files are uploaded, and builds on Intel macOS runners.

v1.0.0-beta1

v1.0.0-beta1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Oct 08:15
Immutable release. Only release title and notes can be modified.

Security

  • TLS certificates are now verified. Up to and including 1.0.0-alpha14, HTTPS requests did not verify the server's certificate or host name (luasec's default), so credentials sent by atlas — bearer tokens, API keys, Basic credentials, OAuth client secrets and refresh tokens — could be intercepted by anyone able to sit between atlas and the API. Upgrade, and consider rotating credentials used over untrusted networks. Verification uses the system CA bundle (SSL_CERT_FILE, the usual bundle locations, SSL_CERT_DIR) and fails closed when none is found; --ssl.verify=none or a profile's "insecure": true opt out.
  • Redirects are followed by atlas itself: GET and HEAD only, at most 5, never from https to http, and credential headers are dropped when the origin changes.
  • Header names and values containing CR, LF or NUL are rejected.
  • -v and --dry-run redact secret query parameters; az sends credentials to source schemas only after a 401/403.
  • external-tool signing and query api-key auth no longer modify the caller's request table.
  • Same-origin checks for redirects and pagination parse URLs as libcurl does: a crafted Location or Link such as https://evil.example?@api.example.com/ was taken for the API's own origin, so credentials followed it to another host.
  • Documents named by a spec's external $refs are fetched without the profile's headers or credentials unless they are on the spec's own origin.
  • The bash completion script treats the candidates it gets (operation names, enum values from the spec…) as data: compgen -W expanded $(...) in them, so a spec could run commands when completing.
  • On a redirect to another origin, the headers authentication added are dropped whatever their name, and logs and dry runs redact them and a query API key whatever their name; user info in URLs is redacted too.
  • The schema cache directory is private to the user (0700).
  • The config file written by profile import-restish, its .bak copy and OAuth tokens are created readable by the user only (0600), with no moment when they are readable by others.
  • A query API key no longer appears in the error printed when a request fails in transport.
  • The profile's headers and credentials are not sent to a spec hosted on another origin than the profile's base-url.
  • --dry-run doesn't run an external signing tool, and doesn't send placeholder credentials when fetching a spec.
  • Bash completion inserts values quoted: an enum value from a spec containing spaces or shell syntax ($(...)) stays one argument and never runs.
  • Text from a spec (summaries, descriptions, server descriptions, completion help) is printed with its control characters escaped, so a spec cannot send terminal escape sequences; so is an OAuth provider's error description.
  • The OAuth callback checks the state before anything else: a forged callback without it can no longer pose as the provider's error.
  • -v redacts credentials in response headers (set-cookie, ...token) as it does in request headers.
  • Error messages, warnings, Arazzo step ids and the OAuth device prompt escape control characters too: they quote specs and servers.
  • op fetches the documents a spec's $refs name like the spec itself: through the same transport (so -v shows them), with the profile's headers and credentials only when the spec is on the API's origin; they used to get the profile's headers whatever the origin.
  • -v, --dry-run, --curl, logged response headers and text error bodies escape control characters too.
  • A same-origin redirect keeps a query API key; another origin still never gets it.
  • A query API key is read when a request is sent, so an unset env: variable no longer breaks --list, --help, --servers or --dry-run; a spec answering 401 is fetched again with the query key or signature, as with header credentials; -v logs spec fetches too.
  • Headers added by external-tool signing are marked as credentials: logs and dry runs redact them, and they are dropped on a redirect to another origin.
  • The libcurl binding rejects URLs, header names and values and options that contain a NUL byte, rather than cutting them short.
  • A URL whose host part holds a backslash or a control character (such as a NUL in a $ref) matches no origin, so credentials never follow it (libcurl reads http://a\@b/ as host b); retried schema fetches and next-page requests keep the marking of credential headers, so a later cross-origin redirect drops them; $ref errors redact the document's URL.

Added

  • HTTP goes through libcurl: proxies from https_proxy/http_proxy/all_proxy/no_proxy, connection reuse, transparent gzip, per-request timeouts.
  • atlas --version, also reporting the libcurl/OpenSSL build; atlas.version in the library.
  • atlas profile import-restish [FILE] [--dry-run] imports Restish configs (v1 apis.json, v2 restish.json) as profiles.
  • Profiles take query (parameters on every request) and tls.ca_cert.
  • az WORKFLOW --help documents a workflow's inputs and steps.
  • --servers, --server-index and --server.NAME=VAL to list and choose servers; atlas.servers and atlas.server-url in the library.
  • Short field prefixes --q., --h., --b., --i..
  • Request checks before sending: missing required parameters and invalid JSON bodies are errors, undeclared query names warn; --no-validate sends anyway.
  • Shell completion of operations, parameters, body fields, enum values, server variables, Arazzo contexts, workflows and inputs.
  • Operations without operationId are available under a name made of their method and path (GET /pets/{petId} → get-pets-pet-id); every operation exposes :name.
  • atlas.json (decode, encode, array, array?): JSON that keeps empty arrays apart from empty objects; use json.array to send an empty array from a body built in Lua.
  • atlas.parse-schema parses fetched schema text, rejecting documents that are not a mapping.
  • Release binaries for macOS x86_64 and Linux arm64, a SHA256SUMS file and build provenance attestations.

Changed

  • Breaking (library): operations take a request table, op.invoke {:path :query :headers :body :timeout}; positional calls remain a shorthand.
  • Breaking (library): atlas.load-schema and the :load-schema client option take {:ssl :headers} instead of positional arguments.
  • Breaking (library): the operation field :has-body? is now :has-body; :n-path is gone (use (length op.path-params)).
  • Breaking (library): every $ref to the same place resolves to the same table, so recursive schemas are cyclic tables (node.properties.next is node) instead of being cut to {}. Code that walks op.spec or op.body-schema must not assume a tree, and should not serialise them as is. This makes resolution linear for any spec (it was exponential on heavily recursive ones such as Stripe's) and lets validation check recursive data at every depth.
  • Breaking (library): JSON null is json.null (from atlas.json) in decoded response bodies, both in arrays and as object members, so a body shows and re-encodes as received; it used to drop members and corrupt arrays ([1,null,3] came out as {"1":1,"3":3}). Code testing (= nil body.x) for a null x must test (= json.null body.x).
  • Breaking: an option that doesn't apply to the command is an error: op --profile p used to send the request without the profile, az --select was ignored.
  • Breaking: Arazzo runs fail when a source description cannot be loaded, and a plain operationId defined by several sources is an error; qualify it as $sourceDescriptions.NAME.ID.
  • Operation names are lowercase words joined by dashes whatever the operationId's style: projects/list-cards, get_folders_id#get_shared_link and bookmarks-(-deprecated) become projects-list-cards, get-folders-id-get-shared-link and bookmarks-deprecated. When two operationIds yield the same name, the first by path keeps it and the others are named after their method and path, the same way on every run.
  • Query parameters are serialized in their declared style and explode (form, spaceDelimited, pipeDelimited, deepObject); array path parameters use the simple style.
  • Arazzo: string comparisons in conditions are case-insensitive and '' escapes a quote, as the spec says; a backslash is now an ordinary character. A misspelled expression ($statuscode) is an error rather than null, and payload text that merely starts with $ is kept as is.
  • Arazzo: a request that fails in transport is a step failure, so onFailure and retry handle it; retry runs the step named by stepId first; a response's Retry-After overrules retryAfter; requestBody.contentType and replacements are supported; $request.query.NAME and $request.path.NAME work.
  • --output and --arazzo-output reject unknown values.
  • Building a client is linear in the size of the spec: Stripe's went from not finishing to 0.07 s, Kubernetes' from 1 s to 0.01 s.
  • The ssl options are cafile, capath, certificate, key and verify; other luasec options are no longer used.
  • The macOS binaries run on macOS 12 or later: Lua, OpenSSL (3.5 LTS) and libyaml are built from pinned sources for that target, where the previous ones only started on the macOS version of the machine that built them.
  • The binaries link OpenSSL, libyaml, zlib and libcurl statically. The Linux binaries are fully static, built against musl: they run on any distribution (the previous ones needed glibc 2.34 or later, libssl 3 and libyaml).
  • YAML is read with YAML 1.2 core schema semantics; mapping keys keep their source text.
  • $ref resolution handles JSON Pointer escapes, references into other files and URLs, and path items; an unresolved reference is an error.
  • Authentication happens only when a request is sent, never for --help, --list, --servers or --dry-run.

Fixed

  • Base...
Read more