wallaby-cli
v0.5.0
Published
Wallaby developer CLI — drive the Wallaby API and SDK from the terminal
Downloads
836
Readme
wallaby-cli
The Wallaby developer CLI. Talk to any Wallaby environment from your terminal — no more hand-rolled curl scripts or Postman collections. Create wallets, send transfers, test the full custody recovery path, and point it at any environment with one flag.
It is also built to be driven by AI agents and CI: every command speaks JSON and uses meaningful exit codes (more under For agents & CI).
Install
npm install -g wallaby-cli # puts `wallaby` and `wallaby-mcp` on your PATHRequires Node 20+. macOS keychain is used for secrets out of the box.
Five-minute setup
First generate an API key in the Wallaby portal — the portal mints a signing keypair alongside it, so keep the private key it shows you handy. Then:
wallaby initThe wizard first asks which profile you're setting up — the active one, another existing one, a brand-new one, or a duplicate of an existing profile (the quick way to add a second test user). Then it walks through everything the profile needs:
- API key — paste the one from the portal.
- Your user id (
externalUserId) — accept the generated UUID if you're starting fresh. - Signing keypair — paste the portal's private key (the default), or generate one locally and register the printed public key back in the portal ("update public key"). Either way both halves are stored in your OS keychain, and client JWTs are minted from them on every request — you never handle tokens manually.
- Sign-up / sign-in — the wizard looks your user id up: a new one is created with the wallet password you choose; an existing one signs in with its current password.
Then prove the whole thing end to end:
wallaby flow smoke # read-only sanity checkGreen across the board? You're done. Add --save-password on wallaby auth login (or say
yes in the wizard) and no command will ever prompt you again.
Everyday commands
wallaby whoami # where am I pointed, as whom, and does it all work?
wallaby system chains # what chains does this env support?
wallaby system tokens --search usdc # find a token
wallaby system tokens --page 2 --limit 100 # list endpoints return 50 items/page — page through, or --all for everything
wallaby user wallets # your wallets
wallaby wallet create # new wallet (2-of-3 key shares, keychain-stored)
wallaby wallet balances [walletId] # current balance of every asset (live read)
wallaby wallet assets [walletId] # assets configured on the wallet (with configIds)
wallaby wallet add-token <assetConfigId> --wallet <walletId> # track a new token (see `system tokens`)
wallaby wallet add-token --chain SUI --sui-object-id 0x…::module::COIN --name Coin --symbol COIN --decimals 9 # custom token
wallaby wallet update-asset <configId> --hide --wallet <walletId> # show/hide or rename an asset
wallaby wallet shares # what recovery material does THIS machine hold?
wallaby wallet shares --verify # …and prove it actually decrypts (flags stale material)
wallaby wallet forget [walletId] --force # delete one wallet's material from this device
wallaby wallet export-seed [walletId] # print the decrypted seed phrase (confirms first — it controls the funds)
wallaby wallet import --wallet <walletId> # recover a wallet onto this device from its seed phrase
wallaby wallet change-password # rotate the wallet password (re-wraps this device's material)
wallaby tx transfer --from 0x… --to 0x… --amount 0.01 --asset-id <id> # --wallet optional
wallaby tx status --hash 0x… --reference <ref> --chain ETHEREUM
wallaby discover # what can I do? every command, plus the API endpoint groups
wallaby discover send # task search: matching commands first, then raw endpoints
wallaby discover create-wallet --schema # …and show the request body shape for `api -d`
wallaby api GET /api/v1/chains # raw escape hatch — any endpoint, gh-api style
pbpaste | wallaby api POST /api/v1/wallets/create-with-shares -d @- # paste/pipe a JSON body
wallaby dd # Datadog logs link for the last command's requests
wallaby dd --last-error --web # …or the last failure, opened in the browserEvery command takes --profile <name> to target another environment and --json for
machine-readable output. --verbose turns on the SDK's internal logging for debugging
(warning: it prints tokens and request payloads — keep it off normally).
Mistype a command and you get a did-you-mean suggestion; wallaby autocomplete prints
setup instructions for shell tab-completion.
Some API areas (Merchants, Admin-Transactions, Query) intentionally have no dedicated
commands — reach them with wallaby api. discover marks results that already have a
first-class command, and warns about endpoints that can't be called raw because their
request bodies need material only the SDK's signing session can produce.
The wallet/tx commands above take a wallet id; omit it to use the profile's default
(wallaby config set defaultWalletId <id>, or the WALLABY_WALLET_ID env var).
Server-side debugging: wallaby dd
Every command sends one x-client-request-id (a uuid) on all its API calls and records
them to ~/.config/wallaby/history.jsonl — timestamps, status, duration, and the server's
requestId (no bodies, no credentials). When a command fails, the requestId is printed
right there; wallaby dd then works retroactively:
wallaby dd # request details + Log Explorer link for the last command
wallaby dd --web # …and open it in the browser (-w)
wallaby dd --last-error # the last command with a failed request
wallaby dd --list # recent commands, numbered
wallaby dd 3 # the 3rd most recent
wallaby dd --endpoint /transfers --since 2h # facet search instead of history
wallaby dd --user <externalUserId> --since 1dLinks query @clientRequestId / @requestId in Datadog Log Explorer. Point them at a
different Datadog site with wallaby config set ddSite <host> (or WALLABY_DD_SITE).
Profiles: environments and test users
A profile = one environment + one set of credentials. All seven deployed environments
are pre-seeded (local, dev-testnet, dev-mainnet, staging-testnet, staging-mainnet,
testnet, mainnet); dev-testnet starts active.
wallaby config # list profiles (active one starred)
wallaby config use staging-testnet # switch the default
wallaby config rename dev-testnet josh-dev
wallaby config remove old-profile --forceNeed a second test user? One command — it clones the active profile (the signing keypair is bound to your API key, not the user), generates a fresh identity, signs it up, and switches to it:
wallaby user new alice --save-password # clone + signup + switch, done
wallaby wallet create # you're alice now
wallaby whoami # …see?--from <profile> clones a different source, --no-use keeps your current profile active.
(The longhand still works: config clone src dest --generate-user then
auth login --signup --profile dest.)
Switching between users is just --profile alice on any command, or config use alice to
make it sticky. wallaby whoami always tells you where you stand: profile, environment,
user, credential and session state, and what this machine holds.
Config lives at ~/.config/wallaby/config.json (no secrets except the API key);
field resolution is always flag > environment variable > config file.
Flows: one-command end-to-end tests
wallaby flow # list scenarios
wallaby flow smoke # read-only sweep: reachability, chains, user, wallets
wallaby flow wallet-lifecycle # sign in → create wallet → verify listed → balances
wallaby flow recovery-roundtrip # the big one — see below
wallaby flow <name> --sandbox # same flow, throwaway user — your account stays cleanWallet-creating flows permanently add wallets to whatever account they run against
(there is no server-side wallet delete). Run them bare when you mean to test your own
account; otherwise add --sandbox: the CLI mints credentials for a fresh throwaway user
(same API key and signing key — both are key-bound, not user-bound), signs it up as an
extra first step in the report, and runs the flow as that user with a generated password
(no prompt, --password ignored). Everything the run stores — device identity, session,
key shares — lives in process memory only, so nothing lands in your keychain and nothing
outlives the run. The report names the throwaway externalUserId if you ever want to
inspect what it created.
recovery-roundtrip proves the custody story end to end: it creates a throwaway wallet,
exports the seed phrase (forcing share + MPC reconstruction, since no seed is stored),
fully wipes this device's state, signs back in as a new device, re-imports from the
seed, and verifies the recovered wallet has the identical id and addresses. Decrypted
material never leaves flow memory. If it fails between the wipe and the import, run
wallaby auth login followed by wallaby wallet import --wallet <id> to finish the
recovery by hand — the wallet stays recoverable server-side.
The same recovery path is available as standalone commands for real wallets:
wallet export-seed reconstructs and prints the seed phrase (share + MPC, confirms
before printing), and wallet import --wallet <id> re-attaches a wallet to this
device from its seed — after a device wipe, a stale share (wallet shares --verify
flags those), or on a brand-new machine.
Each step reports pass/fail, timing, and a structured error naming exactly what diverged.
A failed flow exits 1. New scenarios are a single file in src/flows/.
For agents & CI
Every command supports --json and emits one stable envelope:
{ "ok": true, "command": "api", "profile": "dev-testnet",
"baseUrl": "https://dev-testnet.wallaby.cash/api/v1", "durationMs": 312, "data": { } }On failure, ok is false and error: { status, errorCode, message } is present.
Exit codes: 0 success, 1 request/API failure, 2 usage or config error, 127 unknown
command (with a did-you-mean suggestion on a TTY). Warnings go to stderr; stdout is always
clean JSON under --json.
Headless configuration (CI, containers):
| Setting | Env var |
|---|---|
| profile | WALLABY_PROFILE |
| base URL | WALLABY_BASE_URL |
| API key | WALLABY_API_KEY |
| user id | WALLABY_EXTERNAL_USER_ID |
| wallet password | WALLABY_PASSWORD |
| new password (wallet change-password) | WALLABY_NEW_PASSWORD |
| seed phrase (wallet import) | WALLABY_SEED_PHRASE |
| secret store | WALLABY_STORE=file + WALLABY_PASSPHRASE=… |
With WALLABY_STORE=file, secrets live in an encrypted file (scrypt + AES-256-GCM) instead
of the OS keychain, so no interactive keychain access is needed.
MCP server
The same operations are exposed to AI agents over the Model Context Protocol.
The package ships a second binary, wallaby-mcp, that serves every operation as an MCP tool
over stdio (system_chains, wallet_balances, tx_transfer, swap_quote, api_request,
flow_run, … — the CLI command paths in snake_case). CLI and MCP share one core logic
layer (src/core/), so a tool returns the identical JSON envelope its --json command does.
To find a capability instead of guessing, call the discover tool — it searches the
first-class tools and a catalog of every Wallaby API endpoint (method, path, summary)
for use with api_request, parsed from the installed SDK so it never drifts. Endpoint
results say when a dedicated tool covers them (coveredBy) or when a raw call can't work
because the request needs SDK-signed material (clientCrypto). The server also exposes read-only resources:
wallaby://docs/agents (the agent guide), wallaby://docs/readme, and
wallaby://api/endpoints (the full catalog as JSON).
Installing the package puts both wallaby and wallaby-mcp on your PATH —
no separate install needed.
Automated (recommended)
wallaby init asks at the end whether to register with Claude Code and/or the Desktop app.
Or run it standalone at any time:
wallaby mcp install # prompts for Claude Code and Claude Desktop in one goClaude Code (CLI) — manual
claude mcp add wallaby -- wallaby-mcpClaude Desktop app — manual
Open ~/Library/Application Support/Claude/claude_desktop_config.json (create it if it doesn't
exist) and add the mcpServers block:
{
"mcpServers": {
"wallaby": {
"command": "wallaby-mcp"
}
}
}Restart Claude Desktop after saving. To target a specific environment, add an env key:
{
"mcpServers": {
"wallaby": {
"command": "wallaby-mcp",
"env": { "WALLABY_PROFILE": "dev-testnet" }
}
}
}The server has no TTY, so it never prompts. It reads the active profile from
WALLABY_PROFILE / WALLABY_BASE_URL (falling back to the config file's active profile),
and resolves the wallet password — needed by wallet_create, tx_transfer,
wallet_export_seed, wallet_import, and wallet_shares --verify — from
WALLABY_PASSWORD or the keychain-saved password only (never as a tool argument;
likewise wallet_change_password reads the new password from WALLABY_NEW_PASSWORD
only). If it's missing, the tool returns an actionable error instead of hanging. Read-only tools (chains, balances, quotes, status, whoami, …) need only the
profile credentials. Tools are annotated with readOnlyHint / destructiveHint so clients
can gate the fund-moving ones.
⚠️ The full surface is exposed, including
tx_transfer,wallet_create, andwallet_export_seed(which returns decrypted seed phrases). Point the server at a non-production profile when wiring it to an agent.
How secrets are handled
Everything sensitive lives in your OS keychain (service wallaby-cli):
- the client-JWT signing keypair (
keys init/ theinitwizard) — short-lived RS256 tokens are minted from it per command; the private key never leaves the keychain - the SDK's device key and encrypted wallet shares (created by login / wallet create)
- your wallet password, only if you opt in with
auth login --save-password(convenient on a personal machine; skip it on shared ones —auth logoutdeletes it)
The plaintext config file holds only the API key and non-secret settings. The SDK's
internal logging is silenced by default because its debug output can include secrets;
--verbose re-enables it deliberately.
Development
git clone [email protected]:WALLABY-WaaS/wallaby-cli.git
cd wallaby-cli && npm install
npm run dev -- <command> # run from src/ without rebuilding (ts-node)
npm run build # compile to dist/
npm run typechecknpm run dev -- <command> runs straight from src/ — no npm link needed. If you do want
the built wallaby/wallaby-mcp binaries on your PATH from a local checkout, npm link
after a build.
The package has one core logic layer and two entry points:
src/core/— framework-agnostic operations (operations/<topic>.ts) plus the execution context. Each operation is a pure function(ctx, input) => datawith a zod input schema; the registry insrc/core/operations/index.tsis the source of truth the MCP server reads.src/cli/— the oclif CLI. Commands insrc/cli/commands/<topic>/are thin wrappers that map flags to a core operation and format its result. oclif discovers them from the build output.src/mcp/— the MCP server (server.ts), launched bybin/mcp.js.src/lib/— shared low-level plumbing (config, client, secret store, output envelope); e2e scenarios live insrc/flows/.
Add a capability by writing the operation in src/core/operations/, registering it in
index.ts (which exposes it over MCP), and dropping a thin CLI command in the matching
src/cli/commands/ topic folder.
