mirror of
https://github.com/hanzoai/cli.git
synced 2026-10-30 15:00:27 +00:00
Integrate the native device flow into the HIP-0111 iam module so there is ONE login command, one way. `hanzo login` runs the browser Authorization-Code+PKCE flow by default and the RFC 8628 device flow when there is no browser to open — headless (no DISPLAY/WAYLAND_DISPLAY on linux), the browser fails to launch, or `--device` is passed. Endpoints resolve brand-aware via paths.rs; the device grant runs as the `<brand>-app` client (hanzo-app for the default brand). - src/iam/device.rs: device_auth + poll state machine (authorization_pending keeps polling, slow_down backs off +5s, expired_token/access_denied terminate) returning a keychain TokenSet; link + terminal QR + code display. - src/iam/jwt.rs: signature-free JWT claim decode (identity for the flat store). - src/iam/credentials.rs: the ecosystem-shared flat ~/.hanzo/credentials.json (0600, atomic temp+rename, unknown-key preserving). On every successful DEFAULT-brand login the keychain token set is ALSO mirrored here, so the Go hanzo CLI and hanzo-dev share one session. logout clears both stores; whoami falls back to this store when the keychain has no entry. - oauth.rs surfaces a browser-launch failure (BROWSER_OPEN_FAILED) instead of hanging, so login can fall back to the device flow. - paths.rs gains the RFC 8628 DEVICE endpoint; main.rs gains `login --device`. Deps pinned exact: base64 0.22.1 (tightened), qr2term 0.3.3 (new). Tests: 32 pass. New coverage — device client-id is <brand>-app, poll state machine + DeviceTokenResp→TokenSet against a mocked IAM server (pending→issued, denied, expiry), JWT claim decode, and the flat store (round-trip incl. unknown-key preservation, 0600, mirror-from-TokenSet, logout preserves other tools' keys and reports whether it cleared a session).
52 lines
2.2 KiB
TOML
52 lines
2.2 KiB
TOML
[package]
|
|
name = "hanzo-cli"
|
|
version = "1.0.0"
|
|
edition = "2021"
|
|
authors = ["Hanzo AI"]
|
|
description = "Unified CLI for Hanzo AI development tools"
|
|
license = "MIT"
|
|
repository = "https://github.com/hanzoai/cli"
|
|
|
|
[[bin]]
|
|
name = "hanzo"
|
|
path = "src/main.rs"
|
|
|
|
[dependencies]
|
|
clap = { version = "4.5", features = ["derive", "env"] }
|
|
tokio = { version = "1.36", features = ["full"] }
|
|
anyhow = "1.0"
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
serde_json = "1.0"
|
|
toml = "0.8"
|
|
dirs = "5.0"
|
|
colored = "2.1"
|
|
indicatif = "0.17"
|
|
dialoguer = "0.11"
|
|
reqwest = { version = "0.12", features = ["json", "gzip"] }
|
|
which = "6.0"
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
tempfile = "3.10"
|
|
subprocess = "0.2"
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
|
|
# Cloud auth (HIP-0111 OIDC PKCE S256 + RFC 8628 device flow).
|
|
sha2 = "0.10" # PKCE S256 challenge
|
|
base64 = "0.22.1" # base64url (no pad) — PKCE + JWT claim decode
|
|
rand = "0.8" # OsRng for PKCE verifier + state + local wallet entropy
|
|
keyring = { version = "3", features = ["apple-native", "windows-native", "sync-secret-service"] } # OS keychain — secrets NEVER hit disk
|
|
webbrowser = "1" # open the IAM authorize URL
|
|
qr2term = "0.3.3" # terminal QR of the device verification link
|
|
|
|
# Local / imported wallets — HD derivation + EVM address. Cloud-custody (KMS/MPC)
|
|
# wallets need NONE of this; these are only for offline `wallet create --local`
|
|
# and `wallet import`. Pure-Rust RustCrypto; secrets live in the OS keychain.
|
|
bip39 = { package = "tiny-bip39", version = "1" } # BIP-39 mnemonic (all word counts) -> seed
|
|
bip32 = "0.5" # secp256k1 BIP-32 HD derivation from a seed
|
|
k256 = "0.13" # secp256k1 (matches bip32) — public key -> EVM address
|
|
sha3 = "0.10" # Keccak-256 for EVM address
|
|
|
|
[dev-dependencies]
|
|
assert_cmd = "2.0"
|
|
predicates = "3.1"
|