Skip to content

feat(sdk/rust): add tinyplace CLI binary (#56)#189

Merged
senamakel merged 6 commits into
tinyhumansai:mainfrom
martian56:feat/rust-sdk-cli
Jun 23, 2026
Merged

feat(sdk/rust): add tinyplace CLI binary (#56)#189
senamakel merged 6 commits into
tinyhumansai:mainfrom
martian56:feat/rust-sdk-cli

Conversation

@martian56

@martian56 martian56 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a tinyplace CLI binary to the Rust SDK (a first slice of #56), built on the
existing tinyplace crate — clap for args, the SDK for everything else.

This cut ships a read-only command surface:

Command Purpose
whoami agent id + public key (needs a configured key)
version CLI version
debug / doctor non-secret diagnostics (paths, endpoint, identity)
lookup <handle> resolve a handle's identity (registry.get)
groups [--q --tag --limit] browse public groups (groups.list)
pricing {assets,pairs,networks,quote,gas} asset pricing

Output is JSON by default (--format md for Markdown), errors are JSON on stderr
with a machine-readable code + exit 1, and config resolves from
~/.tinyplace/config.json with $TINYPLACE_ENDPOINT / $TINYPLACE_API_URL,
$TINYPLACE_SECRET_KEY, and $TINYPLACE_CONFIG overrides — matching the
TypeScript CLI's contract.

Design notes

  • clap is optional, behind a cli feature with a required-features bin, so
    library consumers don't pull it in. The Rust CI job now runs --features cli so
    the binary is still fully built / linted / tested.
  • Supporting SDK fix: added Serialize to four response types that were
    Deserialize-only (PriceAsset/PriceAssets, TradePairs, SupportedNetworks,
    GroupListResponse) so the CLI can render them. This is consistent with their
    siblings (GroupMetadata, PriceQuote, GasEstimate already derive both).
  • Scope boundary: per sdk/rust/PORTING.md, the Rust SDK omits Signal E2E
    encryption and on-chain Solana execution, so the TS CLI's encrypted-messaging and
    on-chain commands are intentionally not in this slice. Mutating/auth'd and
    workflow commands can follow incrementally.

Testing

  • Unit tests for config/env resolution and output formatting (JSON/Markdown +
    secret redaction).
  • wiremock-backed command-dispatch tests (success, list response, and
    404 → sdk_error mapping) — the dispatch is split into run_api_command(client, …)
    so it runs against a mock with no live network.
  • Verified locally: cargo fmt --check, cargo clippy --all-targets --features cli -- -D warnings, cargo build --all-targets --features cli, cargo test --features cli (all green), plus a manual smoke test of the binary against the
    live API.

Refs #56

Summary by CodeRabbit

  • New Features
    • Added an optional Rust CLI (tinyplace) to the Rust SDK with commands for identity, lookup, groups, and pricing.
    • Supports JSON and Markdown output formats, with automatic redaction of sensitive fields.
    • Endpoint and authentication settings can be configured via ~/.tinyplace/config.json and environment variables (with CLI overrides).
  • Documentation
    • Updated the Rust SDK README with CLI setup, run examples, output behavior, and command reference.

Port a first slice of the TypeScript SDK CLI to a Rust `[[bin]]` built on the
existing `tinyplace` crate. Read-only command surface for this cut: whoami,
version, debug/doctor, lookup, groups, and pricing (assets/pairs/networks/
quote/gas). JSON output by default, `--format md` for Markdown; config from
`~/.tinyplace/config.json` + `$TINYPLACE_*` env overrides.

- clap is optional behind a `cli` feature with a `required-features` bin, so
  library consumers don't pull it in; the Rust CI job builds/lints/tests with
  `--features cli`.
- Add `Serialize` to a few response types that were `Deserialize`-only
  (PriceAsset(s), TradePairs, SupportedNetworks, GroupListResponse) so the CLI
  can render them — consistent with siblings (GroupMetadata, PriceQuote).
- Tests: pure unit tests for config/env resolution and output formatting, plus
  wiremock-backed command-dispatch tests.

Encrypted messaging and on-chain flows stay TypeScript-only (per PORTING.md).

Refs tinyhumansai#56
@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

@martian56 is attempting to deploy a commit to the Vezures Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 43dab2dd-0620-41cb-b405-cfacf360f66a

📥 Commits

Reviewing files that changed from the base of the PR and between 366402b and 278ba71.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • sdk/rust/src/cli/args.rs
  • sdk/rust/src/cli/commands.rs
  • sdk/rust/src/cli/config.rs
  • sdk/rust/src/cli/output.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • sdk/rust/src/cli/commands.rs
  • sdk/rust/src/cli/args.rs
  • sdk/rust/src/cli/output.rs
  • sdk/rust/src/cli/config.rs

📝 Walkthrough

Walkthrough

Adds an optional tinyplace CLI binary to the Rust SDK, gated behind a cli feature flag. The change introduces argument parsing, config/endpoint resolution with env var and file precedence, client construction with optional signing, JSON/Markdown output rendering with secret redaction, command dispatch for API calls, and updates CI to build and test with the new feature.

Changes

Rust SDK CLI Binary

Layer / File(s) Summary
Crate configuration, API type Serialize additions, and CI wiring
sdk/rust/Cargo.toml, .github/workflows/ci.yml, sdk/rust/src/api/groups.rs, sdk/rust/src/api/pricing.rs
Adds optional clap dependency and cli feature in Cargo.toml; defines [[bin]] target for tinyplace binary; updates CI clippy, build, and test steps to run twice—once without feature flags, then with --features cli; adds Serialize derives to GroupListResponse, PriceAsset, PriceAssets, TradePairs, and SupportedNetworks for JSON output support.
CLI argument types and subcommand surface
sdk/rust/src/cli/args.rs
Defines OutputFormat enum (Json, Md), Cli struct with global --format/--json/--md flags and optional --endpoint override, and Cli::output_format() precedence resolver; defines Command enum covering all subcommands and nested PricingCommand enum with typed arguments (e.g., Quote with base/quote/optional network; Gas with network).
Config file loading and endpoint/seed resolution
sdk/rust/src/cli/config.rs
Defines ConfigFile and Resolved structs; implements config path discovery via TINYPLACE_CONFIG env or ~/.tinyplace/config.json; implements endpoint precedence (CLI → TINYPLACE_ENDPOINTTINYPLACE_API_URL → config file → default); implements 64-hex-char seed validation and decoding to [u8; 32]; includes public resolve() function and unit tests for precedence and seed validation.
TinyPlaceClient and signer construction
sdk/rust/src/cli/context.rs
Implements build() to construct a TinyPlaceClient from Resolved config and optionally derive a LocalSigner from the seed, returning an error string on invalid key material; enables read-only command execution when no seed is configured.
JSON and Markdown output rendering with redaction
sdk/rust/src/cli/output.rs
Implements render() which recursively redacts string values under sensitive keys (containing secret or privatekey), then formats as pretty JSON or indented Markdown bullet lists; includes unit tests for formatting, nesting, and redaction behavior.
Command dispatch, error type, and integration tests
sdk/rust/src/cli/commands.rs
Defines CliError with to_stderr_json() and From<tinyplace::Error> conversion; implements local whoami/debug handlers and async run_pricing/run_api_command API dispatchers; exposes public run() entry point; adds wiremock-based async tests for lookup, pricing networks, and HTTP error conversion.
CLI entrypoint and README documentation
sdk/rust/src/cli/main.rs, sdk/rust/README.md
Wires async Tokio main that parses args, calls run(), and exits with SUCCESS/FAILURE code based on result; adds README CLI section documenting installation, config file, environment variable overrides, example commands, and read-only scope limitation.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant main as main.rs
    participant commands as commands::run
    participant config as config::resolve
    participant context as context::build
    participant sdk as TinyPlaceClient
    participant output as output::render

    User->>main: tinyplace [flags] <subcommand>
    main->>commands: run(cli)
    alt version / whoami / debug
        commands-->>commands: handle locally
    else API command (lookup / groups / pricing)
        commands->>config: resolve(cli_endpoint)
        config-->>commands: Resolved { endpoint, seed, config_path }
        commands->>context: build(&resolved)
        context-->>commands: (TinyPlaceClient, Option<Signer>)
        commands->>sdk: API call (lookup / groups / pricing/*)
        sdk-->>commands: deserialized response
    end
    commands->>output: render(value, format)
    output-->>commands: formatted String
    commands-->>main: Ok(String) or Err(CliError)
    alt success
        main->>User: stdout + ExitCode::SUCCESS
    else error
        main->>User: stderr JSON + ExitCode::FAILURE
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • tinyhumansai/tiny.place#35: Also modifies the Rust SDK CI job's cargo clippy, cargo build, and cargo test steps in .github/workflows/ci.yml, directly overlapping with the CI changes in this PR.

Poem

🐇 Hop hop, a new command appears,
tinyplace whoami — identity clears!
Secrets redacted, endpoints resolved,
JSON or Markdown, the output evolved.
From config file to env var chain,
The tiny rabbit CLI has arrived again! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(sdk/rust): add tinyplace CLI binary (#56)' accurately and specifically describes the main change: adding a CLI binary to the Rust SDK.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
sdk/rust/src/cli/args.rs (1)

21-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Disallow conflicting shorthand format flags.

--json and --md can currently be set together; output_format() silently prefers markdown. Add clap conflicts so invalid combinations fail fast.

Suggested change
     /// Shorthand for `--format json`.
-    #[arg(long, global = true)]
+    #[arg(long, global = true, conflicts_with_all = ["md", "format"])]
     pub json: bool,

     /// Shorthand for `--format md`.
-    #[arg(long, global = true)]
+    #[arg(long, global = true, conflicts_with_all = ["json", "format"])]
     pub md: bool,

Also applies to: 37-46

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/rust/src/cli/args.rs` around lines 21 - 27, The `json` and `md` fields in
the args struct allow conflicting format flags to be set simultaneously, which
should be prevented. Add a `conflicts_with` attribute to the clap `#[arg(...)]`
macro on both the `json` field and the `md` field to declare that they conflict
with each other. This will make clap reject the command if both flags are
provided together, rather than silently preferring one over the other in the
`output_format()` method. Apply the same conflict constraint to any other
format-related flags mentioned around lines 37-46.
sdk/rust/src/cli/commands.rs (1)

195-197: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wiremock matchers are too broad in two tests.

Line 195 and Line 215 match any GET, so path regressions can still pass. Add explicit path matchers to validate dispatch targets.

🔧 Proposed fix
     #[tokio::test]
     async fn pricing_networks_renders_array() {
         let server = MockServer::start().await;
         Mock::given(method("GET"))
+            .and(path("/pricing/networks"))
             .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "networks": [] })))
             .mount(&server)
             .await;
@@
     #[tokio::test]
     async fn sdk_http_error_maps_to_sdk_error_code() {
         let server = MockServer::start().await;
         Mock::given(method("GET"))
+            .and(path("/registry/names/ghost"))
             .respond_with(ResponseTemplate::new(404).set_body_json(json!({ "error": "nope" })))
             .mount(&server)
             .await;

Also applies to: 215-217

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/rust/src/cli/commands.rs` around lines 195 - 197, The Mock setup using
only Mock::given(method("GET")) without path validation allows tests to pass
even when calling incorrect endpoints. For both mock definitions (around line
195 and line 215), add explicit path matchers alongside the method matcher. Use
Mock::given with both method("GET") and an appropriate path matcher (such as
path matching the specific endpoint being tested) to ensure the mocks validate
the correct dispatch targets are being called, preventing path regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 181-189: The CI job currently only validates the `cli` feature
path in the Rust SDK, which leaves the default feature path untested and allows
non-CLI regressions to slip through. Add separate steps to run cargo clippy,
cargo build, and cargo test without the `--features cli` flag (in addition to
the existing steps) to ensure the default feature path is also covered. These
additional steps should run in the same `sdk/rust` working directory and
validate the SDK without any feature flags.

In `@sdk/rust/src/cli/config.rs`:
- Around line 93-97: The seed variable assignment does not filter out empty or
whitespace values from the TINYPLACE_SECRET_KEY environment variable, causing an
empty env var to block the fallback to file.secret_key. Modify the chain after
std::env::var("TINYPLACE_SECRET_KEY").ok() to check if the string is empty or
contains only whitespace, and only use the env var value if it's valid; if it's
empty or whitespace, allow the .or(file.secret_key) fallback to be used instead.

In `@sdk/rust/src/cli/output.rs`:
- Around line 31-35: The redaction logic in the condition starting with
is_sensitive_key check only fully redacts sensitive keys when the value is a
string, but when a sensitive key maps to an object or array, nested secrets
within those structures are not properly redacted and can leak. Fix this by
ensuring that whenever is_sensitive_key returns true for a key, the entire value
is redacted regardless of its type: if it is a string redact to "[redacted]",
and if it is an object or array, recursively redact all nested values within the
structure before returning it.

---

Nitpick comments:
In `@sdk/rust/src/cli/args.rs`:
- Around line 21-27: The `json` and `md` fields in the args struct allow
conflicting format flags to be set simultaneously, which should be prevented.
Add a `conflicts_with` attribute to the clap `#[arg(...)]` macro on both the
`json` field and the `md` field to declare that they conflict with each other.
This will make clap reject the command if both flags are provided together,
rather than silently preferring one over the other in the `output_format()`
method. Apply the same conflict constraint to any other format-related flags
mentioned around lines 37-46.

In `@sdk/rust/src/cli/commands.rs`:
- Around line 195-197: The Mock setup using only Mock::given(method("GET"))
without path validation allows tests to pass even when calling incorrect
endpoints. For both mock definitions (around line 195 and line 215), add
explicit path matchers alongside the method matcher. Use Mock::given with both
method("GET") and an appropriate path matcher (such as path matching the
specific endpoint being tested) to ensure the mocks validate the correct
dispatch targets are being called, preventing path regressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b1adc0f0-b894-410a-bd29-2f23b3d840bb

📥 Commits

Reviewing files that changed from the base of the PR and between 236a593 and 366402b.

⛔ Files ignored due to path filters (1)
  • sdk/rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • .github/workflows/ci.yml
  • sdk/rust/Cargo.toml
  • sdk/rust/README.md
  • sdk/rust/src/api/groups.rs
  • sdk/rust/src/api/pricing.rs
  • sdk/rust/src/cli/args.rs
  • sdk/rust/src/cli/commands.rs
  • sdk/rust/src/cli/config.rs
  • sdk/rust/src/cli/context.rs
  • sdk/rust/src/cli/main.rs
  • sdk/rust/src/cli/output.rs

Comment thread .github/workflows/ci.yml Outdated
Comment thread sdk/rust/src/cli/config.rs Outdated
Comment thread sdk/rust/src/cli/output.rs Outdated
@senamakel

Copy link
Copy Markdown
Member

thanks for the contribution @martian56 ! merging shortly.

@senamakel senamakel merged commit b34d00e into tinyhumansai:main Jun 23, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants