README
¶
webda-cli
A dynamic CLI client for Webda applications. The binary name determines which remote host it connects to, configured via ~/.webdacli/config.yaml.
Installation
Release installer (no Go SDK)
For macOS and Linux on Intel/AMD or ARM64, run:
curl -fsSL https://github.com/loopingz/webda.cli/releases/latest/download/install.sh | sh
Install and configure a client for a server in one step:
curl -fsSL https://github.com/loopingz/webda.cli/releases/latest/download/install.sh | sh -s -- --server https://app.example.com --name myapp
myapp explore
The installer verifies the binary's SHA-256 checksum and installs it to
~/.local/bin without requiring root access. Add this directory to your PATH
if it is not already there. --name sets both the executable and profile name;
--server saves the server URL through the CLI's setup command. Authentication
happens when you first use the client, so installation does not open a browser.
Additional options:
curl -fsSL https://github.com/loopingz/webda.cli/releases/latest/download/install.sh -o install.sh
sh install.sh --version v1.2.0 --install-dir "$HOME/bin"
sh install.sh --server https://staging.example.com --name staging --install-completion
Shell completion installation is opt-in. The script requires curl and either
sha256sum (Linux) or shasum (macOS), and supports --help.
The installer and checksums.txt will be included in releases containing this
change; older releases such as v1.1.0 do not provide them.
Windows (no Go SDK)
In Windows PowerShell 5.1 or PowerShell 7, run:
& ([scriptblock]::Create((irm https://github.com/loopingz/webda.cli/releases/latest/download/install.ps1)))
Install and configure a named client in one command:
& ([scriptblock]::Create((irm https://github.com/loopingz/webda.cli/releases/latest/download/install.ps1))) -Server https://app.example.com -Name myapp
The installer selects the native AMD64 or ARM64 binary, verifies its SHA-256
checksum, and installs it to %LOCALAPPDATA%\WebdaCLI without administrator
access. It adds the directory to PATH for the current session. For future
terminals, open Edit environment variables for your account, edit the user
Path, and add %LOCALAPPDATA%\WebdaCLI.
Options include -Version v1.2.0, -InstallDir "$HOME\bin", and
-InstallCompletion (requires -Server). Authentication happens when you first
use the client, for example myapp explore. The one-liner invokes the downloaded
script as a script block so these parameters can be passed directly.
The PowerShell installer will be available in the next release containing this
change. You can also download webda-cli-windows-amd64.exe or
webda-cli-windows-arm64.exe manually from the
latest release.
Install with Go
Requires Go 1.25 or newer. Once a release containing the corrected module path is published, install with:
go install github.com/loopingz/webda.cli@latest
This installs the command as webda.cli in GOBIN, or $(go env GOPATH)/bin when
GOBIN is unset. Add that directory to your PATH, then run webda.cli setup.
The v1.1.0 release cannot be installed with this command: it declares
github.com/loopingz/webda-cli as its module path, which differs from the actual
repository path. Use a source build until a corrected release is available:
git clone https://github.com/loopingz/webda.cli.git
cd webda.cli
go build -o webda-cli .
./webda-cli setup
To install the source-built binary under a custom command name:
mkdir -p "$HOME/.local/bin"
install -m 755 webda-cli "$HOME/.local/bin/myapp"
Add $HOME/.local/bin to your PATH. The binary name is the default profile name;
configure it with myapp setup --name myapp --url https://app.example.com.
You can also select an existing profile with myapp --profile staging explore.
Optional aliases retain the binary-name profile behavior:
ln -s "$HOME/.local/bin/myapp" "$HOME/.local/bin/mystaging"
Configuration
Configure a named profile using the setup wizard, or supply values directly:
webda.cli setup
webda.cli setup --name staging --url https://staging.example.com
webda.cli profiles
webda.cli --profile staging explore
Host mappings are stored in ~/.webdacli/config.yaml:
myapp: https://app.example.com
staging: https://staging.example.com
wlocal: http://localhost:18080
lc:
url: https://my.loopingz.com
client_id: webda-cli
scope: "* offline_access"
update: prompt
model_groups: singular
A profile is either a URL string or an object with url and optional client_id
(default webda-cli) and scope (default "* offline_access"). Set them with
setup:
myapp setup --name lc --url https://my.loopingz.com --client-id webda-cli --scope "* offline_access"
Running setup again for an existing profile keeps its client_id and scope unless
you pass them. Changing the URL of a profile deletes its stored sign-in and its cached
OAuth metadata and operations, so a token is never sent to another server.
The binary name remains the default profile, so existing symlinks work. Use
--profile to select another host without creating a symlink. Setup changes shell
configuration only when you choose completion installation or pass
--install-completion.
Help, version, setup, profiles, completion, and cached input skeletons work without
signing in or contacting the server. Operation help uses the last cached schema;
run refresh-operations to update it.
For Bash and Zsh, help recognizes literal aliases in the standard shell startup
files (including .bash_aliases and Zsh's ZDOTDIR):
alias myalias='webda.cli --profile staging'
myalias --help
myalias mind-node get --help
Usage displays myalias in place of the binary and the global options supplied
by the alias. The selected profile and command execution stay the same. Detection
matches the command and its leading arguments, preferring the most specific alias.
Shells do not pass the original alias name to programs, so a direct invocation
with those same arguments also displays the matching alias. Ambiguous matches,
session-only aliases, sourced custom files, expansions, and aliases containing
subcommands fall back to the binary name. Startup files are read without executing
shell code.
Authentication
Sign-in uses OAuth 2.0 with PKCE. On first use, or with myapp auth, the CLI opens
your browser so you can sign in and choose which operations it may call. Tokens are
refreshed automatically (refresh tokens rotate, and concurrent requests share a
single refresh, also across CLI processes through ~/.webdacli/<profile>.lock). The
access token is only ever sent to the profile's server: a redirect to another
origin (scheme, host or port) drops it, and a token saved for another server is
discarded.
myapp auth # sign in (or sign in again)
myapp auth --scope "Task.* offline_access" # request a narrower scope
myapp auth --device # sign in with a code instead of a browser
myapp auth --prompt consent # browser flow only: login, consent or none
myapp whoami # show current user info
myapp logout # revoke the sign-in and forget it
--scopeoverrides the profile scope for this sign-in.- The device flow shows a verification URL, a code and a terminal QR code; approve it
on any device. It is used automatically over SSH (
SSH_CONNECTION), on Linux withoutDISPLAYorWAYLAND_DISPLAY, and when no browser can be opened.--promptapplies to the browser flow only. - Without a terminal (scripts, CI), a first use that needs a sign-in fails fast with
run myapp auth; run it once interactively. - A failed re-authentication keeps your previous sign-in. A successful one revokes the previous refresh token.
logoutrevokes the refresh token on the server, then deletes it locally.- Sign-ins saved by older versions of the CLI are discarded once; sign in again.
- Connected apps can also be revoked from the web app.
- When a token does not cover an operation, the CLI prints
this token does not cover <operation>; run myapp auth --scope "<current scope> <operation>"so you can request it without losing what the token already grants. - The CLI prints
Sign in at: <url>before opening the browser; open it yourself if no browser appears.
Token storage
Tokens go to the OS keychain (service webda-cli, one entry per profile). Without a
keychain they are stored in an encrypted file, ~/.webdacli/<profile>.tok, keyed with
the SSH agent or, failing that, the machine (with a random salt in
~/.webdacli/<profile>.tok.salt). Each token records the server it was issued for.
OAuth server metadata is cached in ~/.webdacli/<profile>.oauth.json. It is checked
to be same-origin with the profile URL and is discovered again when invalid. A
webda_result_uri on another origin is ignored and the CLI shows its own page after
the browser sign-in.
Server requirements
OAuth sign-in needs a Webda server running @webda/oauth (OAuthServer); against
other servers (discovery answers 404) auth, and any command that contacts the server
while no sign-in is stored, stops with this server does not support OAuth sign-in
(without a terminal, commands ask you to run myapp auth instead).
Operations come from the advertised webda_operations_uri (default /operations,
Webda/OperationsTransport) or, when that answers 404, from the REST OpenAPI
document served when exposeOpenAPI is enabled.
Operations
The CLI fetches available operations from the remote host (GET <operations URL>: the
webda_operations_uri advertised by the OAuth metadata, /operations by default) and creates nested subcommands automatically. Operation names are split by . and converted to kebab-case:
| Operation | Command |
|---|---|
AuthorizerService.testOperations |
myapp authorizer-service test-operations |
MFA.SMS |
myapp mfa sms |
Sync.AWS |
myapp sync aws |
Model operations use both the singular and the plural (MindNode.Get, MindNodes.Query). When the
listing has an aliases map ({"MindNodes": "MindNode"}, sent by Webda servers), they share one group
reachable by both names, like kubectl get pod/pods: myapp mind-node query or myapp mind-nodes get.
model_groups in ~/.webdacli/config.yaml picks the name shell completion offers:
model_groups |
Completed | Also accepted |
|---|---|---|
singular (default) |
mind-node |
mind-nodes (alias) |
plural |
mind-nodes |
mind-node (alias) |
both |
mind-node, mind-nodes |
Operations are invoked via POST <operations URL>/<operationId>, against the URL they
were listed from (also when loaded from the cache). Operations from the OpenAPI
document use their own method and path.
Flags from JSON Schema
If an operation defines an input JSON schema, flags are generated automatically:
myapp authorizer-service test-operations --user alice
Interactive TUI
Run myapp explore, or run myapp without arguments in a terminal, to browse
operations. The explorer provides fuzzy search across names and descriptions,
operation details, favorites, and recent operation names. Wide terminals show
list and details side by side; narrow terminals stack them.
/: search operations;↑/↓: navigate;Enter: configure.f: toggle a favorite;c: copy a shell command;q: quit.- Forms show required fields first, optional fields under More options, schema defaults/examples, validation, and a redacted request review before execution.
- In results, use
↑/↓or page keys to scroll,/to search lines,tfor tables,jfor JSON,eto edit retained inputs, andEscto go back. - During execution,
Esccancels the local request. Cancellation does not undo work that the server may already have started. Failed mutations are not automatically retried.
When required flags are missing, direct commands also offer a form if stdin and
stderr are terminals. Force it with --interactive / -i, or disable prompting
with --no-interactive. Missing input in scripts produces an error with the
required flag names. --accessible uses plain prompts suitable for screen
readers; NO_COLOR=1 disables color.
myapp authorizer-service test-operations -i
myapp authorizer-service test-operations --user alice --no-interactive
myapp explore --accessible
Forms preserve numeric and enum types, support JSON arrays/objects, and omit untouched optional fields. Optional booleans offer Leave unset, Yes, and No, preserving the distinction between omission and explicit false. Local validation covers inline types, required fields, enums, numeric ranges, string length/pattern, array sizes/items, and additionalProperties=false; the server validates schema references and other constraints.
Operations with destructive: true metadata require a review in interactive mode
or an explicit --yes on a direct command. Request previews and copied commands
redact password/writeOnly fields and names containing password, token, or secret.
History contains operation names and favorites only, never request inputs.
Output and automation
myapp sync aws --input input.json --output json --timeout 15s
cat input.json | myapp sync aws --input - --no-interactive --output raw
myapp users list --output table
Output formats are json, table, raw, and the default pretty. Tables accept
an object or an array of objects; pretty formats JSON and passes other responses
through. HTTP failures return a nonzero exit status and diagnostics go to stderr.
Authentication refresh messages do not pollute stdout. --timeout bounds operation
requests and operation discovery.
Array and object flags accept JSON. Schema fields that conflict with built-in
flags use a field- prefix, for example --field-input or --field-output.
Query response fields
Keep --output / -o for the format; use --query / -q for jq selection,
filtering, and projection before rendering. No external jq binary is required.
myapp users list -o json -q '.items[0].email'
myapp users list -o raw -q '.items[].name'
myapp users list -o table -q '.items[] | {name, email, team: .team.name}'
myapp users list -o table -q '.items[] | select(.team.name == "Engineering") | {name, email}'
Queries use gojq v0.12.17, a Go implementation of jq with some differences from jq (including sorted object keys). Modules, extra input streams, and host environment variables are unavailable. Invalid queries fail before the operation runs; evaluation errors fail after the response arrives, with no partial stdout. The operation timeout also bounds query evaluation; Ctrl-C cancels it. Results are buffered in memory.
Each query result is printed separately: json and pretty emit a sequence of
indented JSON values, while raw emits strings without JSON quotes and other
values as compact JSON, with a newline after each value. Multiple results are a
JSON stream, not one JSON document; use [.items[].name] to collect an array.
No results produce no output; null is printed as null.
With -o table, object results become rows and array results contribute their
objects as rows. Columns are the alphabetically sorted union of object keys,
including aliases such as team above. Missing cells are blank; nested values
are JSON. Scalars, nulls, or arrays containing non-objects produce an error.
Empty result streams and empty arrays print nothing. Without --query, existing
output behavior is unchanged.
Tab completion uses the operation's cached output schema, without signing in,
contacting the server, or running the operation. It supports nested properties,
[], numeric indices, simple path pipes, and object projection keys/value paths.
Available local schema references and union branches are followed. Missing or
unknown schemas yield no suggestions. Complex transformations (including map,
select, variables, and custom functions) still evaluate normally, but completion
after them is not inferred.
In bash, zsh, and fish, put the query in single quotes to protect jq punctuation. Press Tab inside those quotes, at the end of the query being edited:
-q '.items[].em<TAB> suggests email
-q '.items[] | .team.na<TAB> suggests name
-q '.items[] | {name, email: .em<TAB> suggests email
-q '.items[].team["postal-<TAB> suggests ["postal-code"]
<TAB> means a key press, not literal text; close the outer quote before running
the command. Use bracket-quoted paths for special keys, e.g. .["a.b"].
Completion in the middle of an expression is best effort; move the cursor to the
end of the query for reliable suggestions. Regenerate/reinstall shell completion
with myapp completion install after upgrading, especially for bash projection
completion (which needs updated handling of colons).
JSON Skeleton and File Input
Generate a JSON skeleton for an operation's input schema, fill it in, then pass it back:
# Generate skeleton
myapp authorizer-service test-operations --generate-cli-skeleton > input.json
# Edit the file with your values
cat input.json
{
"user": ""
}
# Run the operation with the file
myapp authorizer-service test-operations --input input.json
Flags override values from the file. The TUI form can fill remaining gaps:
# File provides some values, --user overrides, TUI fills the rest
myapp authorizer-service test-operations --input partial.json --user alice -i
Refresh operations
myapp refresh-operations # re-fetch operations from the server
Shell Completion
Install completion explicitly:
myapp completion install
# Or opt in during setup:
myapp setup --name myapp --url https://app.example.com --install-completion
This detects zsh, bash, or fish, writes completion scripts, and configures shell startup. Restart your shell to activate it. For manual installation:
myapp completion zsh > ~/.webdacli/completions/_myapp
myapp completion bash > ~/.webdacli/completions/myapp.bash
myapp completion fish > ~/.config/fish/completions/myapp.fish
Updates
By default the CLI reports incompatible server version requirements and suggests
myapp update. Set update: prompt in config to ask before updating in an
interactive terminal, or update: silent to explicitly enable automatic updates.
Local help and version commands never trigger updates.
Logo
If the server provides a logo URL in the operations response, the CLI displays it inline in terminals that support it (iTerm2, Kitty, WezTerm). The logo appears in help output and interactive TUI forms.
Development
After cloning, enable the pre-push hook to run linting and tests before each push:
git config core.hooksPath .githooks
This runs go vet, golangci-lint, and go test -race automatically.
Files
| Path | Purpose |
|---|---|
~/.webdacli/config.yaml |
Host mappings |
~/.webdacli/<name>.tok |
Encrypted tokens (only when no OS keychain is available) |
~/.webdacli/<name>.tok.salt |
Salt of the machine-bound token encryption (no keychain nor SSH agent) |
~/.webdacli/<name>.lock |
Lock held while a token is refreshed, shared by CLI processes |
~/.webdacli/<name>.oauth.json |
Cached OAuth server metadata |
~/.webdacli/<name>.operations |
Cached operations spec and the URL they run against |
~/.webdacli/<name>.logo |
Cached logo image |
~/.webdacli/<profile>.history.json |
Favorite and recent operation names |
~/.webdacli/completions/ |
Shell completion scripts |
Documentation
¶
There is no documentation for this package.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package filelock provides an advisory, cross-process exclusive lock on a file.
|
Package filelock provides an advisory, cross-process exclusive lock on a file. |
|
Package oauth implements the client side of the Webda OAuth 2.1 authorization server.
|
Package oauth implements the client side of the Webda OAuth 2.1 authorization server. |
|
oauthtest
Package oauthtest provides an in-memory OAuth 2.1 authorization server for tests.
|
Package oauthtest provides an in-memory OAuth 2.1 authorization server for tests. |
|
Package webdaclient is the authenticated HTTP client of the CLI: it attaches the OAuth access token and refreshes it (with refresh token rotation) through the authorization server token endpoint.
|
Package webdaclient is the authenticated HTTP client of the CLI: it attaches the OAuth access token and refreshes it (with refresh token rotation) through the authorization server token endpoint. |