wallet-cli-usage

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

wallet-cli

wallet-cli

USB-based CLI for Ledger wallet flows. Networks: bitcoin, ethereum, solana (mainnet + testnets).
Install globally with a user-preferred package manager —
npm i -g @ledgerhq/wallet-cli
,
pnpm add -g @ledgerhq/wallet-cli
,
yarn global add @ledgerhq/wallet-cli
, or
bun add -g @ledgerhq/wallet-cli
. Run:
wallet-cli [flags]
.
Concepts & rationale: for why a command behaves the way it does, or to surface a safety rule that this skill states tersely (genuine check, receive-address verification, sessions, sandbox, device contention), read
references/business-logic.md
.
Session first: When invoked without a specific task, immediately run
session view
— do not ask the user what to do first. Show the result, then ask what to do next. If labels exist, skip
account discover
.
Sandbox:
account discover
,
receive
,
send
,
genuine-check
,
swap execute
,
ring encrypt
,
ring decrypt
,
ring keys
,
ring destroy
must use
dangerouslyDisableSandbox: true
— the first group is blocked by USB restrictions; the ring commands are blocked by OS keychain access restrictions.
Device contention: Never run two device commands in parallel — they fail with
[object Object]
or garbled APDU. Run sequentially.
Device readiness: Before running a device command, briefly describe what you're about to do. The CLI prompts for device interaction itself — don't time out or kill the command. Exception:
genuine-check
exits immediately (
[✖] Wrong app. Open Ledger dashboard.
, exit code 4) if any currency app is open — unlike the other device commands, it targets the dashboard and has no auto-launch path. Ensure the device is on the dashboard before running; if it exits, ask the user to back out to the dashboard and re-run.
Ambiguous requests — ask, don't guess. If a required parameter is missing or unclear (no recipient for
send
, no network for
account discover
, an amount with no ticker), stop and ask. A wrong guess on a hardware wallet flow can mean irreversible fund loss.

基于USB的Ledger钱包流程CLI。支持网络:bitcoin、ethereum、solana(主网 + 测试网)。
使用你偏好的包管理器全局安装 ——
npm i -g @ledgerhq/wallet-cli
、
pnpm add -g @ledgerhq/wallet-cli
、
yarn global add @ledgerhq/wallet-cli
或
bun add -g @ledgerhq/wallet-cli
。运行方式:
wallet-cli [flags]
。
概念与设计逻辑: 若想了解命令行为的原因,或查看本技能简要提及的安全规则(正品校验、接收地址验证、会话、沙箱、设备争用),请阅读
references/business-logic.md
。
会话优先: 当调用时没有指定具体任务,立即运行
session view
—— 不要先询问用户要做什么。先展示结果,再询问下一步操作。如果已有账户标签,跳过
account discover
。
沙箱限制:
account discover
、
receive
、
send
、
genuine-check
、
swap execute
、
ring encrypt
、
ring decrypt
、
ring keys
、
ring destroy
必须使用
dangerouslyDisableSandbox: true
—— 前一组命令受USB限制被沙箱拦截;ring系列命令受操作系统钥匙串访问限制被拦截。
设备争用: 严禁并行运行两个设备相关命令 —— 否则会报错
[object Object]
或出现APDU乱码。请按顺序执行。
设备就绪检查: 运行设备命令前,请简要说明即将执行的操作。CLI会自行提示设备交互 —— 不要超时或终止命令。例外情况:如果打开了任何币种应用,
genuine-check
会立即退出(报错
[✖] Wrong app. Open Ledger dashboard.
,退出码4)—— 与其他设备命令不同,该校验针对的是设备主界面,没有自动启动路径。运行前请确保设备处于主界面;如果命令退出,请让用户返回设备主界面后重新运行。
请求不明确时 —— 询问,不要猜测。 如果缺少必填参数或参数不明确(
send
没有收款方、
account discover
没有指定网络、金额没有币种代码),请停下来询问用户。硬件钱包流程中的错误猜测可能导致不可逆的资金损失。

Intent map

意图映射

Map informal phrasings to commands. Account references use a session label (e.g.
ethereum-1
).
User saysCommand
"show me my wallet", "what do I have", "let's get started", no specific task
session view
(run immediately, before asking anything)
"find my accounts", "scan my wallet", "import my wallet", "set up Ethereum/Bitcoin"
account discover <network>
"where do I send funds to", "give me my address", "deposit address"
receive <account>
"how much do I have", "balance", "what's my ETH balance"
balances <account>
"what did I send", "transaction history", "recent activity"
operations <account>
"send X to Y", "transfer", "pay", "withdraw to an exchange"
send <account> --to <address> --amount '<amount> <ticker>'
"swap A to B", "convert", "trade ETH for BTC", "exchange"
swap quote
->
swap execute
->
swap status
"where can I earn", "staking rates", "yield/APY", "best return on my ETH/SOL"
earn yields [-n <network>]
"what am I staking", "my staking positions", "earn balance"
earn positions <account>
"stake my SOL", "deposit into a vault", "earn yield on my USDC", "delegate"
earn deposit <account> --product <id> --amount '<amount>'
"unstake", "withdraw my stake", "redeem from vault", "stop earning"
earn withdraw <account> …
"is this Ledger real", "verify authenticity", "I bought this off eBay"
genuine-check
"encrypt this file / these env vars / publish tokens", "GPG alternative", "secret manager", "decrypt anywhere with my Ledger"
ring init
->
ring encrypt --key <name>
/
ring decrypt --key <name>
"what keys do I have on my ring", "list domains/projects I've encrypted under"
ring keys
"wipe my key ring", "destroy the ring", "tear down LKRP membership"
ring destroy
"start over", "clear my session", "I switched devices"
session reset

将非正式表述映射到对应命令。账户引用使用会话标签(例如
ethereum-1
)。
用户说法对应命令
"给我看看我的钱包"、"我有什么资产"、"开始吧"、无具体任务
session view
(立即运行,先于任何询问)
"查找我的账户"、"扫描我的钱包"、"导入我的钱包"、"设置以太坊/比特币"
account discover <network>
"我要把钱转到哪里"、"给我我的地址"、"充值地址"
receive <account>
"我有多少钱"、"余额"、"我的ETH余额是多少"
balances <account>
"我发送了什么"、"交易历史"、"近期活动"
operations <account>
"把X转给Y"、"转账"、"付款"、"提现到交易所"
send <account> --to <address> --amount '<amount> <ticker>'
"把A换成B"、"兑换"、"用ETH换BTC"、"交易"
swap quote
->
swap execute
->
swap status
"我在哪里可以赚收益"、"质押利率"、"收益/年化收益率"、"我的ETH/SOL最佳收益"
earn yields [-n <network>]
"我质押了什么"、"我的质押仓位"、"收益余额"
earn positions <account>
"质押我的SOL"、"存入金库"、"用我的USDC赚收益"、"委托"
earn deposit <account> --product <id> --amount '<amount>'
"解除质押"、"提取我的质押"、"从金库赎回"、"停止赚收益"
earn withdraw <account> …
"这个Ledger是真的吗"、"验证真伪"、"我在eBay上买的这个"
genuine-check
"加密这个文件/这些环境变量/发布令牌"、"GPG替代方案"、"密钥管理器"、"用我的Ledger在任何地方解密"
ring init
->
ring encrypt --key <name>
/
ring decrypt --key <name>
"我的密钥环上有哪些密钥"、"列出我加密过的域名/项目"
ring keys
"清空我的密钥环"、"销毁密钥环"、"取消LKRP成员身份"
ring destroy
"重新开始"、"清除我的会话"、"我换了设备"
session reset

Out of scope — say no, don't improvise

超出范围 —— 直接拒绝,不要临时发挥

If the user asks for any of the following, surface that wallet-cli does not support it yet rather than constructing a command:
  • NFTs (mint, transfer, view).
  • OpenPGP-compatible output or key-share / multi-recipient encryption (the
    ring
    commands encrypt with a per-user Ledger Key Ring, not a sharable key).
  • send
    ,
    receive
    ,
    operations
    , or
    swap execute
    on testnets and layer 2s (e.g. Base).
  • Custom chains not listed in the Networks line above.

如果用户询问以下任何内容,请说明wallet-cli暂不支持,不要自行构造命令:
  • NFT(铸造、转账、查看)。
  • OpenPGP兼容输出或密钥共享/多接收方加密(
    ring
    命令使用用户专属的Ledger Key Ring加密,而非可共享的密钥)。
  • 测试网和二层网络(例如Base)上的
    send
    、
    receive
    、
    operations
    或
    swap execute
    。
  • 上方支持网络列表中未列出的自定义链。

Session & labels

会话与标签

account discover
persists accounts. Each gets a label:
<network>[-derivation][-env]-<n>
(e.g.
ethereum-1
,
bitcoin-native-1
,
ethereum-sepolia-1
).
All
--account
flags accept a session label (e.g.
ethereum-1
). Run
account discover
first to populate the session.

account discover
会持久化账户信息。每个账户会获得一个标签:
<network>[-derivation][-env]-<n>
(例如
ethereum-1
、
bitcoin-native-1
、
ethereum-sepolia-1
)。
所有
--account
参数都接受会话标签(例如
ethereum-1
)。请先运行
account discover
来填充会话数据。

Commands

命令

CommandDeviceSandboxTTY†Network
session view
NoNoNoNo
session reset
NoNoNoNo
account discover
YesRequiredNoYes
receive
YesRequiredNoNo
send
Yes*RequiredNoYes
genuine-check
YesRequiredNoYes
balances
NoNoNoYes
operations
NoNoNoYes
swap quote
NoNoNoYes
swap execute
YesRequiredNoYes
swap status
NoNoNoYes
assets token
NoNoNoNo
assets token-by-id
NoNoNoNo
earn yields
NoNoNoYes
earn positions
NoNoNoYes
earn deposit
Yes*RequiredNoYes
earn withdraw
Yes*RequiredNoYes
ring init
YesRequiredRequired‡Yes
ring encrypt
NoRequiredNoYes
ring decrypt
NoRequiredNoYes
ring keys
NoRequiredNoNo
ring destroy
NoRequiredRequired‡‡Yes
*
send
,
earn deposit
, and
earn withdraw
with
--dry-run
need no device and no sandbox bypass.
†TTY: whether the command requires an interactive terminal for user input.
‡
ring init
requires a password to protect the ring.
WALLET_PASS
must already be provided in the environment by the developer/user before the command runs — the agent never sets or injects it (see Non-TTY password injection).
‡‡
ring destroy
prompts for typed confirmation (
"destroy"
). Pipe it in non-interactive shells:
echo "destroy" | wallet-cli ring destroy
. If a password was set,
WALLET_PASS
must already be present in the environment (provided by the developer, not the agent).
命令需设备需绕过沙箱需TTY†需指定网络
session view
否否否否
session reset
否否否否
account discover
是必须否是
receive
是必须否否
send
是*必须否是
genuine-check
是必须否是
balances
否否否是
operations
否否否是
swap quote
否否否是
swap execute
是必须否是
swap status
否否否是
assets token
否否否否
assets token-by-id
否否否否
earn yields
否否否是
earn positions
否否否是
earn deposit
是*必须否是
earn withdraw
是*必须否是
ring init
是必须是‡是
ring encrypt
否必须否是
ring decrypt
否必须否是
ring keys
否必须否否
ring destroy
否必须是‡‡是
* 带
--dry-run
参数的
send
、
earn deposit
和
earn withdraw
不需要设备,也不需要绕过沙箱。
†TTY:命令是否需要交互式终端供用户输入。
‡
ring init
需要密码来保护密钥环。命令运行前,开发者/用户必须已在环境变量中提供
WALLET_PASS
—— Agent绝不会设置或注入该密码(参见非TTY密码注入)。
‡‡
ring destroy
会要求输入确认词(
"destroy"
)。在非交互式shell中可以通过管道传入:
echo "destroy" | wallet-cli ring destroy
。如果设置了密码,环境变量中必须已存在
WALLET_PASS
(由开发者提供,而非Agent)。

session view / reset

session view / reset

bash
wallet-cli session view
wallet-cli session reset
bash
wallet-cli session view
wallet-cli session reset

account discover

account discover

bash
wallet-cli account discover ethereum
wallet-cli account discover bitcoin
wallet-cli account discover ethereum:sepolia
Networks:
bitcoin
(mainnet),
ethereum
,
solana
,
ethereum:sepolia
,
bitcoin:testnet
,
solana:devnet
.
bash
wallet-cli account discover ethereum
wallet-cli account discover bitcoin
wallet-cli account discover ethereum:sepolia
支持网络:
bitcoin
(主网)、
ethereum
、
solana
、
ethereum:sepolia
、
bitcoin:testnet
、
solana:devnet
。

receive

receive

bash
wallet-cli receive ethereum-1
wallet-cli receive ethereum-1 --no-verify  # skip device confirmation
If the on-screen address differs from the terminal address: do not share or use the address. Have the user disconnect the device and run
genuine-check
before retrying. See
references/business-logic.md
§ Receive-address verification for context.
bash
wallet-cli receive ethereum-1
wallet-cli receive ethereum-1 --no-verify  # 跳过设备确认
如果设备屏幕上的地址与终端显示的地址不一致: 不要分享或使用该地址。请让用户断开设备连接,运行
genuine-check
后再重试。相关说明请参见
references/business-logic.md
§ 接收地址验证。

genuine-check

genuine-check

bash
wallet-cli genuine-check
wallet-cli genuine-check --output json  # only if a downstream caller needs to parse the result
Preconditions: device unlocked and on the dashboard (exit any open app); host has internet access (the secure channel reaches Ledger's backend — offline runs fail).
bash
wallet-cli genuine-check
wallet-cli genuine-check --output json  # 仅当下游调用方需要解析结果时使用
前置条件: 设备已解锁并处于主界面(退出所有打开的应用);主机有互联网连接(安全通道需要连接Ledger后端 —— 离线运行会失败)。

balances

balances

bash
wallet-cli balances ethereum-1
wallet-cli balances ethereum-1 --output json
bash
wallet-cli balances ethereum-1
wallet-cli balances ethereum-1 --output json

operations

operations

bash
wallet-cli operations ethereum-1
wallet-cli operations ethereum-1 --limit 20 --cursor <cursor>
Pagination: next cursor on stderr (human) or
nextCursor
in JSON.
bash
wallet-cli operations ethereum-1
wallet-cli operations ethereum-1 --limit 20 --cursor <cursor>
分页:下一页游标会输出到stderr(人类可读格式)或JSON中的
nextCursor
字段。

send

send

bash
wallet-cli send ethereum-1 --to 0xDEF... --amount '0.5 ETH'
wallet-cli send ethereum-1 --to 0xDEF... --amount '100 USDT'  # ERC-20
wallet-cli send bitcoin-native-1 --to bc1q... --amount '0.001 BTC' --fee-per-byte 15 --rbf
wallet-cli send ethereum-1 --to 0xDEF... --amount '0.5 ETH' --dry-run
Ticker is mandatory in
--amount
. No
--token
flag — ticker drives asset resolution.
Bitcoin flags:
--fee-per-byte <sats>
,
--rbf
Solana flags:
--mode send|stake.createAccount|stake.delegate|stake.undelegate|stake.withdraw
,
--validator <addr>
,
--stake-account <addr>
,
--memo <text>
bash
wallet-cli send ethereum-1 --to 0xDEF... --amount '0.5 ETH'
wallet-cli send ethereum-1 --to 0xDEF... --amount '100 USDT'  # ERC-20
wallet-cli send bitcoin-native-1 --to bc1q... --amount '0.001 BTC' --fee-per-byte 15 --rbf
wallet-cli send ethereum-1 --to 0xDEF... --amount '0.5 ETH' --dry-run
--amount
参数中必须包含币种代码(ticker)。没有
--token
参数 —— 资产解析由币种代码驱动。
Bitcoin专属参数:
--fee-per-byte <sats>
、
--rbf
Solana专属参数:
--mode send|stake.createAccount|stake.delegate|stake.undelegate|stake.withdraw
、
--validator <addr>
、
--stake-account <addr>
、
--memo <text>

swap quote

swap quote

Fetches quotes in parallel from the built-in provider list (no device required; addresses are resolved from session accounts).
Currencies:
--from
/
-f
and
--to
/
-t
are Ledger currency IDs — native assets (e.g.
ethereum
,
bitcoin
,
solana
) or token IDs when the token’s parent chain is a supported native swap currency (same IDs the CLI allows for swap). They are not session account labels — use
--from-account
/
--to-account
for accounts.
Default providers queried by
swap quote
and usable by
swap execute
:
changelly
,
changelly_v2
,
cic
,
cic_v2
,
exodus
,
lifi
,
nearintents
,
okx
,
oneinch
,
swapsxyz
,
uniswap
,
velora
. Some are CEX/aggregators run through the legacy Exchange-app pipeline; the DEX providers (
uniswap
,
oneinch
,
velora
,
okx
) execute in the partner's embedded coin app — see swap execute — DEX providers.
Accounts:
--from-account
and
--to-account
accept a session label only; the CLI resolves a fresh receive address from the account like
receive
.
bash
wallet-cli swap quote --from ethereum --to bitcoin --amount 0.1 --from-account ethereum-1 --to-account bitcoin-native-1
wallet-cli swap quote -f ethereum -t bitcoin --amount 0.1 --from-account ethereum-1 --to-account bitcoin-native-1 --output json
Required:
--from
,
--to
,
--from-account
,
--to-account
,
--amount
.
从内置提供商列表并行获取报价(不需要设备;地址从会话账户中解析)。
币种:
--from
/
-f
和
--to
/
-t
是Ledger 币种ID —— 原生资产(例如
ethereum
、
bitcoin
、
solana
)或代币ID(当代币的父链是支持的原生兑换币种时,与CLI允许的兑换ID相同)。它们不是会话账户标签 —— 账户请使用
--from-account
/
--to-account
参数。
swap quote
默认查询且
swap execute
可使用的提供商:
changelly
、
changelly_v2
、
cic
、
cic_v2
、
exodus
、
lifi
、
nearintents
、
okx
、
oneinch
、
swapsxyz
、
uniswap
、
velora
。部分是CEX/聚合商,通过旧版Exchange应用流程运行;DEX提供商(
uniswap
、
oneinch
、
velora
、
okx
)在合作伙伴的嵌入式币种应用中执行 —— 参见swap execute — DEX提供商。
账户:
--from-account
和
--to-account
仅接受会话标签;CLI会像
receive
命令一样从账户解析一个新的接收地址。
bash
wallet-cli swap quote --from ethereum --to bitcoin --amount 0.1 --from-account ethereum-1 --to-account bitcoin-native-1
wallet-cli swap quote -f ethereum -t bitcoin --amount 0.1 --from-account ethereum-1 --to-account bitcoin-native-1 --output json
必填参数:
--from
、
--to
、
--from-account
、
--to-account
、
--amount
。

swap execute

swap execute

Currencies:
--from
/
-f
and
--to
/
-t
are Ledger currency IDs (same as
swap quote
): native assets or tokens on an allowed parent chain. They must match the asset of the source
--account
and of
--to-account
respectively.
Providers: Valid
--provider
values are
changelly
,
changelly_v2
,
cic
,
cic_v2
,
exodus
,
lifi
,
nearintents
,
okx
,
oneinch
,
swapsxyz
,
uniswap
,
velora
. Aliases:
changelly
→
changelly_v2
,
1inch
→
oneinch
. Use the provider id shown on the quote line you pick from
swap quote
.
DEX providers (
uniswap
,
oneinch
,
velora
,
okx
):
these run end-to-end in the partner's embedded coin app on the device (via the Device Intent Executor), not the legacy Exchange app. The flow re-fetches a quote for the chosen provider, then drives an on-device approval + swap sequence (
sign-approval
/
sign-permit2
/
sign-swap
/ broadcast), switching device apps as needed — confirm each
Open <app>
and signing prompt on the device.
  • EVM only. DEX execution requires an EVM source account (e.g.
    ethereum
    ); a non-EVM
    --account
    falls through to the legacy pipeline.
  • RFQ quotes are not supported in the CLI. If the picked quote resolves to an RFQ plan (
    rfq-order
    /
    approval-then-rfq-order
    ), the embedded flow is skipped and execution falls back to the legacy Exchange-app pipeline (you'll see a
    falling back to legacy Exchange-app pipeline
    progress line).
All other providers (
changelly
,
cic
,
exodus
,
nearintents
,
swapsxyz
,
lifi
, …) run the legacy Exchange-app pipeline (nonce → payload → complete exchange → sign/broadcast).
Fee strategy:
--fee-strategy
accepts
slow
,
medium
(default), or
fast
. On the legacy pipeline it sets the refund-chain transaction fee.
bash
wallet-cli swap execute --from ethereum --to bitcoin --account ethereum-1 --to-account bitcoin-native-1 --provider changelly --amount 0.1
wallet-cli swap execute -f ethereum -t bitcoin --account ethereum-1 --to-account bitcoin-native-1 --provider changelly --amount 0.1 --fee-strategy fast
wallet-cli swap execute --from ethereum --to bitcoin --account ethereum-1 --to-account bitcoin-native-1 --provider changelly --amount 0.1 --output json
币种:
--from
/
-f
和
--to
/
-t
是Ledger 币种ID(与
swap quote
相同):允许的父链上的原生资产或代币。它们必须分别与源
--account
和
--to-account
的资产匹配。
提供商: 有效的
--provider
值包括
changelly
、
changelly_v2
、
cic
、
cic_v2
、
exodus
、
lifi
、
nearintents
、
okx
、
oneinch
、
swapsxyz
、
uniswap
、
velora
。别名:
changelly
→
changelly_v2
、
1inch
→
oneinch
。请使用你从
swap quote
结果中选择的报价行上的提供商ID。
DEX提供商(
uniswap
、
oneinch
、
velora
、
okx
):
这些提供商的兑换端到端运行在设备上的合作伙伴嵌入式币种应用中(通过设备意图执行器),而非旧版Exchange应用。该流程会为选定的提供商重新获取报价,然后驱动设备上的授权+兑换序列(
sign-approval
/
sign-permit2
/
sign-swap
/ 广播),根据需要切换设备应用 —— 请在设备上确认每个
Open <app>
和签名提示。
  • 仅支持EVM。 DEX执行需要EVM源账户(例如
    ethereum
    );非EVM的
    --account
    会回退到旧版流程。
  • CLI不支持RFQ报价。 如果选择的报价对应RFQ方案(
    rfq-order
    /
    approval-then-rfq-order
    ),会跳过嵌入式流程,执行回退到旧版Exchange应用流程(你会看到
    falling back to legacy Exchange-app pipeline
    的进度提示行)。
所有其他提供商(
changelly
、
cic
、
exodus
、
nearintents
、
swapsxyz
、
lifi
等)运行旧版Exchange应用流程(nonce → payload → 完成兑换 → 签名/广播)。
手续费策略:
--fee-strategy
接受
slow
、
medium
(默认)或
fast
。在旧版流程中,它设置退款链的交易手续费。
bash
wallet-cli swap execute --from ethereum --to bitcoin --account ethereum-1 --to-account bitcoin-native-1 --provider changelly --amount 0.1
wallet-cli swap execute -f ethereum -t bitcoin --account ethereum-1 --to-account bitcoin-native-1 --provider changelly --amount 0.1 --fee-strategy fast
wallet-cli swap execute --from ethereum --to bitcoin --account ethereum-1 --to-account bitcoin-native-1 --provider changelly --amount 0.1 --output json

DEX (embedded coin app): EVM-only, source and destination on an EVM chain

DEX(嵌入式币种应用):仅支持EVM,源和目标都在EVM链上

wallet-cli swap execute --from ethereum --to ethereum/erc20/usd_tether__erc20_ --account ethereum-1 --to-account ethereum-1 --provider uniswap --amount 0.1

Required flags: `--from`, `--to`, `--account`, `--to-account`, `--provider`, `--amount`. Use a `--provider` value that matches the provider id on the quote line you pick from `swap quote`.
wallet-cli swap execute --from ethereum --to ethereum/erc20/usd_tether__erc20_ --account ethereum-1 --to-account ethereum-1 --provider uniswap --amount 0.1

必填参数:`--from`、`--to`、`--account`、`--to-account`、`--provider`、`--amount`。请使用与你从`swap quote`结果中选择的报价行上的提供商ID匹配的`--provider`值。

swap status

swap status

bash
wallet-cli swap status --swap-id <swapId> --provider changelly
wallet-cli swap status --swap-id <swapId> --provider changelly --output json
Required flags:
--swap-id
,
--provider
bash
wallet-cli swap status --swap-id <swapId> --provider changelly
wallet-cli swap status --swap-id <swapId> --provider changelly --output json
必填参数:
--swap-id
、
--provider

assets token / token-by-id

assets token / token-by-id

Resolve token metadata from the cryptoassets store. No device, no session.
bash
wallet-cli assets token ethereum 0xdac17f958d2ee523a2206206994597c13d831ec7
wallet-cli assets token-by-id ethereum/erc20/usd_tether__erc20_
Use
token
when you have the contract address; use
token-by-id
when you have the id. Exits non-zero if not found.
For non-EVM chains pass
--identifier
.
The
id
printed here is the same id accepted by
swap quote --from
/
--to
and
swap execute --from
/
--to
.
从加密资产库中解析代币元数据。不需要设备,不需要会话。
bash
wallet-cli assets token ethereum 0xdac17f958d2ee523a2206206994597c13d831ec7
wallet-cli assets token-by-id ethereum/erc20/usd_tether__erc20_
如果你有合约地址,使用
token
;如果你有ID,使用
token-by-id
。如果未找到,命令以非零状态码退出。
对于非EVM链,请传入
--identifier
参数。
这里输出的
id
与
swap quote --from
/
--to
和
swap execute --from
/
--to
接受的ID是同一个。

ring — Ledger Key Ring (LKRP)

ring — Ledger Key Ring (LKRP)

Trustless, hardware-rooted encryption for files and text. The key ring is provisioned once on your Ledger via the Ledger Sync app; afterwards
encrypt
/
decrypt
run without the device — keys derive deterministically via HKDF-SHA256 from the LKRP-shared root and never leave AES-256-GCM.
encrypt
/
decrypt
still call the LKRP backend to restore the trustchain on each invocation, so network access is required. The ring is recoverable from your seed on any new machine.
bash
undefined
无需信任的、硬件根保护的文件和文本加密。密钥环通过Ledger Sync应用在你的Ledger设备上一次性配置;之后
encrypt
/
decrypt
运行不需要设备 —— 密钥通过HKDF-SHA256从LKRP共享根密钥确定性派生,永远不会离开AES-256-GCM加密环境。
encrypt
/
decrypt
每次调用时仍会调用LKRP后端来恢复信任链,因此需要网络连接。密钥环可以在任何新机器上通过你的助记词恢复。
bash
undefined

One-time provisioning (device required). Password comes from WALLET_PASS in the environment (see below); name the machine with --name.

一次性配置(需要设备)。密码来自环境变量中的WALLET_PASS(见下文);用--name为机器命名

wallet-cli ring init wallet-cli ring init --name my-laptop
wallet-cli ring init wallet-cli ring init --name my-laptop

File round-trip (no device after init):

文件加解密往返(初始化后不需要设备):

wallet-cli ring encrypt --key my-oss-project -i .publish-tokens -o .publish-tokens.enc wallet-cli ring decrypt --key my-oss-project -i .publish-tokens.enc -o .publish-tokens
wallet-cli ring encrypt --key my-oss-project -i .publish-tokens -o .publish-tokens.enc wallet-cli ring decrypt --key my-oss-project -i .publish-tokens.enc -o .publish-tokens

Text via stdin/stdout (clipboard pattern):

通过标准输入输出处理文本(剪贴板模式):

pbpaste | wallet-cli ring encrypt --key personal-notes | pbcopy pbpaste | wallet-cli ring decrypt --key personal-notes | pbcopy
pbpaste | wallet-cli ring encrypt --key personal-notes | pbcopy pbpaste | wallet-cli ring decrypt --key personal-notes | pbcopy

List the keys this machine has used; tear down the ring:

列出本机已使用的密钥;销毁密钥环:

wallet-cli ring keys wallet-cli ring destroy

> **Always provision with a password.** The ring must be protected by a password. The user provides it via `WALLET_PASS` in the environment before running `ring init` (see [Non-TTY password injection](#ring--ledger-key-ring-lkrp)) — the agent never provisions a ring without one.

> **Decrypted output is sensitive.** `ring decrypt` emits secrets — never print them to the terminal, `cat` a decrypted file, or otherwise surface the decrypted contents, since they land in the agent transcript, logs, and scrollback. Pipe `decrypt` straight to its destination (a file via `-o`, another process, or the clipboard as shown above) or capture it into an env var; avoid `--output`/logging sinks that could echo it back.

`--key <name>` derives a per-name AES-256-GCM key; matching name at decrypt time is mandatory. Names are free-form (max 253 chars, no whitespace) — common patterns: project slugs (`my-oss-project`), env tags (`openClaw-prod`), notebooks (`personal-notes`).

**Non-TTY (CI / agentic) password injection:** the `ring` commands read the password from the `WALLET_PASS` env var when there is no TTY. **The password itself must be provisioned by the developer/user (exported in the environment or stored in the OS keychain) — the agent never chooses, types, or otherwise handles the secret value; it only references what the user has already provisioned.**

- **Never write the password literally** into a command (e.g. `WALLET_PASS=hunter2 wallet-cli …`, or via a flag). A literal leaks into shell history, `ps` output, CI logs, and — when an agent runs the command — the **agent transcript**. This applies to throwaway/test passwords too: make it a habit, because the same command shape is reused with a real secret.
- **Always inject via command substitution** so the secret never appears in the command text you type:
  - macOS : `WALLET_PASS=$(security find-generic-password -a default -s ledger-wallet-cli -w) wallet-cli ring encrypt …`
  - Linux : `WALLET_PASS=$(secret-tool lookup service ledger-wallet-cli account default) wallet-cli ring encrypt …`
- **Agents must not handle the secret at all.** Ask the user to store the password once in their OS keychain, then reference only the `$(…)` substitution. If a test or `ring init` needs a password, store a throwaway value in the keychain first (`security add-generic-password -a default -s ledger-wallet-cli -w`) and inject it the same way — never type the literal into a tool call.
- Even via substitution the value lives in the child process environment (readable via `ps eww` by the same user) — acceptable, but prefer the keychain form and avoid `--output json` sinks or logs that could echo it back.

**Rotation limitation:** the domain key derives from the ring's wallet-sync encryption key, which the LKRP protocol **rotates when a ring member is removed**. After a rotation, data encrypted before it can no longer be decrypted (decrypt fails with a "wrong key name, corrupted data, or the Ledger Key Ring rotated" error, and the CLI prints a `⚠ Ledger Key Ring rotated` warning). Re-encrypt the affected data under the new ring after a member is removed. `ring destroy` aborts (no changes) if you enter a wrong password, and also if `WALLET_PASS` is set but empty (a failed keychain lookup) — this is treated as a mistake, not a skip, so it never orphans the remote ring. To intentionally skip the remote teardown and wipe only local credentials, press Enter at the interactive password prompt.

---
wallet-cli ring keys wallet-cli ring destroy

> **务必配置密码。** 密钥环必须受密码保护。用户在运行`ring init`前通过环境变量`WALLET_PASS`提供密码(参见[非TTY密码注入](#ring--ledger-key-ring-lkrp))—— Agent绝不会在没有密码的情况下配置密钥环。

> **解密输出属于敏感信息。** `ring decrypt`会输出密钥 —— 绝不要将它们打印到终端、用`cat`查看解密文件或以其他方式暴露解密内容,因为这些内容会进入Agent对话记录、日志和滚动缓存。请将`decrypt`的输出直接管道传输到目的地(通过`-o`写入文件、传给另一个进程,或如上所示传到剪贴板),或存入环境变量;避免使用可能回显内容的`--output`或日志输出方式。

`--key <name>` 会派生一个对应名称的AES-256-GCM密钥;解密时必须使用相同的名称。名称是自由格式的(最多253个字符,无空格)—— 常见用法:项目标识(`my-oss-project`)、环境标签(`openClaw-prod`)、笔记(`personal-notes`)。

**非TTY(CI / Agent模式)密码注入:** 当没有TTY时,`ring`系列命令会从`WALLET_PASS`环境变量读取密码。**密码本身必须由开发者/用户配置(导出到环境变量或存储在操作系统钥匙串中)—— Agent绝不会选择、输入或以其他方式处理密钥值;它仅引用用户已配置的内容。**

- **绝不要在命令中直接写密码明文**(例如`WALLET_PASS=hunter2 wallet-cli …`,或通过参数传入)。明文会泄露到shell历史、`ps`输出、CI日志中,并且当Agent运行命令时,会泄露到**Agent对话记录**中。这也适用于临时/测试密码:养成习惯,因为相同的命令格式会在使用真实密钥时复用。
- **始终通过命令替换注入**,这样密钥永远不会出现在你输入的命令文本中:
  - macOS : `WALLET_PASS=$(security find-generic-password -a default -s ledger-wallet-cli -w) wallet-cli ring encrypt …`
  - Linux : `WALLET_PASS=$(secret-tool lookup service ledger-wallet-cli account default) wallet-cli ring encrypt …`
- **Agent绝不能处理密钥。** 请让用户将密码一次性存储在他们的操作系统钥匙串中,然后仅引用`$(…)`替换。如果测试或`ring init`需要密码,请先在钥匙串中存储一个临时值(`security add-generic-password -a default -s ledger-wallet-cli -w`),然后用同样的方式注入 —— 绝不要在工具调用中输入明文。
- 即使通过替换,密钥值仍存在于子进程环境中(同一用户可通过`ps eww`读取)—— 这是可接受的,但优先使用钥匙串方式,并避免使用可能回显内容的`--output json`或日志输出。

**轮换限制:** 域密钥派生密钥环的wallet-sync加密密钥,而LKRP协议会在**移除密钥环成员时轮换**该密钥。轮换后,之前加密的数据将无法解密(解密会失败并报错"wrong key name, corrupted data, or the Ledger Key Ring rotated",CLI会打印`⚠ Ledger Key Ring rotated`警告)。移除成员后,请使用新密钥环重新加密受影响的数据。`ring destroy`会中止(不做任何更改)如果输入错误的密码,或者`WALLET_PASS`已设置但为空(钥匙串查找失败)—— 这被视为错误操作而非跳过,因此绝不会导致远程密钥环成为孤儿。如果要故意跳过远程清理,仅擦除本地凭据,请在交互式密码提示时按Enter键。

---

earn (staking & DeFi yield)

earn(质押与DeFi收益)

Earn covers two flows: Ethereum ERC-4626 DeFi vaults (deposit/redeem) and Solana native staking (delegate/undelegate).
yields
and
positions
are read-only (no device);
deposit
and
withdraw
sign on the device.
Only ethereum & solana support
deposit
/
withdraw
. Other networks appear in
earn yields
(informational) but cannot be deposited to via the CLI.
Earn包含两种流程:Ethereum ERC-4626 DeFi金库(存款/赎回)和Solana原生质押(委托/解除委托)。
yields
和
positions
是只读的(不需要设备);
deposit
和
withdraw
需要在设备上签名。
仅ethereum和solana支持
deposit
/
withdraw
。其他网络会出现在
earn yields
中(仅供参考),但无法通过CLI存入资金。

earn yields

earn yields

Lists yield opportunities (no device). Without
--network
it prints every network's headline rate. With
-n ethereum
or
-n solana
it also prints the concrete deposit targets
, each ending with the exact
→ --product <id>
value to pass to
earn deposit
:
  • ethereum → ERC-4626 vault ids (e.g.
    1_0x7daeba3f217614e409f85d3014d33923a6b03630
    ).
  • solana → validator vote accounts. The CLI surfaces the Ledger-operated validators ("Ledger by Figment", "Ledger by Bitwise") as the recommended targets; any other valid vote account also works as
    --product
    .
bash
wallet-cli earn yields
wallet-cli earn yields -n solana
wallet-cli earn yields -n ethereum --output json
There is no separate "list validators / vaults" command —
earn yields -n <network>
is how you discover a valid
--product
. In JSON, the value is the
vaultId
(ETH) or
validator
(SOL) field on each row.
列出收益机会(不需要设备)。不带
--network
参数时,它会打印每个网络的头条利率。使用
-n ethereum
或
-n solana
时,它还会打印具体的存款目标
,每个目标的末尾是可传给
earn deposit
的确切
→ --product <id>
值:
  • ethereum → ERC-4626金库ID(例如
    1_0x7daeba3f217614e409f85d3014d33923a6b03630
    )。
  • solana → 验证者投票账户。CLI会列出Ledger运营的验证者("Ledger by Figment"、"Ledger by Bitwise")作为推荐目标;任何其他有效的投票账户也可以作为
    --product
    使用。
bash
wallet-cli earn yields
wallet-cli earn yields -n solana
wallet-cli earn yields -n ethereum --output json
没有单独的"列出验证者/金库"命令 ——
earn yields -n <network>
就是你查找有效
--product
的方式。在JSON格式中,该值是每行的
vaultId
(ETH)或
validator
(SOL)字段。

earn positions

earn positions

Lists active earn positions for an account (no device). Account-based networks only (solana, ethereum).
bash
wallet-cli earn positions solana-1
wallet-cli earn positions solana-1 --fresh   # request a background refresh
--fresh
flags stale rows for an async backend refresh; the refreshed data shows up on a re-run, not in the same response. Watch for the
(stale)
marker.
Solana stake accounts: for Solana accounts the command also reads on-chain stake accounts and prints each one's
→ --stake-account <address>
, its
state
(active / inactive / activating / deactivating), balance, and validator. This is where you get the
--stake-account
value for
earn withdraw
.
In JSON they're a top-level
stakes[]
array alongside
positions
(each entry:
stakeAccount
,
validator
,
state
,
stakeBalance
,
withdrawable
); the
stakes
key is omitted entirely when there are none. Stake accounts show up here right after a deposit even if the backend snapshot is still empty. (Requires a chain sync; if it can't be reached the backend snapshot still prints, with a warning.)
列出一个账户的活跃收益仓位(不需要设备)。仅支持基于账户的网络(solana、ethereum)。
bash
wallet-cli earn positions solana-1
wallet-cli earn positions solana-1 --fresh   # 请求后台刷新
--fresh
会标记过期行,供后端异步刷新;刷新后的数据会在重新运行时显示,而非在同一次响应中。请注意
(stale)
标记。
Solana质押账户: 对于Solana账户,该命令还会读取链上质押账户,并打印每个账户的
→ --stake-account <address>
、
state
(active / inactive / activating / deactivating)、余额和验证者。这就是你获取
earn withdraw
所需的
--stake-account
值的地方。
在JSON格式中,它们是与
positions
并列的**顶层
stakes[]
**数组(每个条目包含:
stakeAccount
、
validator
、
state
、
stakeBalance
、
withdrawable
);如果没有质押账户,
stakes
键会被完全省略。即使后端快照仍然为空,质押账户在存款后也会立即显示在这里。(需要链同步;如果无法连接链,仍会打印后端快照,但附带警告。)

earn deposit

earn deposit

Stakes (Solana) or deposits into a vault (Ethereum). Touches the device to sign — bypass the sandbox.
--product
comes from
earn yields -n <network>
(see above).
--amount
requires a ticker.
bash
undefined
质押(Solana)或存入金库(Ethereum)。需要连接设备签名 —— 请绕过沙箱。
--product
来自
earn yields -n <network>
(见上文)。
--amount
需要包含币种代码。
bash
undefined

Solana: --product is a validator vote account

Solana:--product是验证者投票账户

wallet-cli earn deposit solana-1 --product 26pV97Ce83ZQ6Kz9XT4td8tdoUFPTng8Fb8gPyc53dJx --amount '1.5 SOL'
wallet-cli earn deposit solana-1 --product 26pV97Ce83ZQ6Kz9XT4td8tdoUFPTng8Fb8gPyc53dJx --amount '1.5 SOL'

Ethereum: --product is a vault id

Ethereum:--product是金库ID

wallet-cli earn deposit ethereum-1 --product 1_0x7daeba3f217614e409f85d3014d33923a6b03630 --amount '100 USDC'
wallet-cli earn deposit ethereum-1 --product 1_0x7daeba3f217614e409f85d3014d33923a6b03630 --amount '100 USDC'

Validate without signing (no device, no sandbox bypass)

仅验证不签名(不需要设备,无需绕过沙箱)

wallet-cli earn deposit solana-1 --product 26pV97… --amount '1.5 SOL' --dry-run

Solana `stake.createAccount` creates **and** delegates the stake account in one transaction. Ethereum deposits may run two transactions (ERC-20 `approve` then `deposit`).

**First-time ETH vault deposit — dry-run can't validate the deposit leg.** A first deposit into a vault you've never used is `approve` → `deposit`, and the deposit can only be built once a non-zero allowance exists on-chain. In `--dry-run` nothing is broadcast, so when an approve is still required the CLI validates the approve and **skips** the deposit build (status `not-simulated …`, overall `dry-run: approve validated; deposit needs an on-chain allowance to simulate`) rather than surfacing the backend's opaque 500. This is expected — **not** a balance error. The only way to validate the deposit leg is the real run (broadcast `approve`, wait for confirmation, then `deposit`). Treat a clean dry-run here as "approve is fine"; **confirm with the user before the live run** since it's an irreversible on-device signature. Once the allowance exists, a re-run of `--dry-run` will simulate the deposit normally.
wallet-cli earn deposit solana-1 --product 26pV97… --amount '1.5 SOL' --dry-run

Solana的`stake.createAccount`会在一笔交易中创建**并**委托质押账户。Ethereum存款可能会运行两笔交易(ERC-20 `approve` 然后 `deposit`)。

**首次ETH金库存款 —— dry-run无法验证存款环节。** 首次存入你从未使用过的金库的流程是`approve` → `deposit`,而存款交易只有在链上存在非零授权时才能构建。在`--dry-run`模式下不会广播任何内容,因此当仍需要授权时,CLI会验证授权并**跳过**存款构建(状态为`not-simulated …`,整体结果为`dry-run: approve validated; deposit needs an on-chain allowance to simulate`),而不是返回后端不透明的500错误。这是预期行为 —— **不是**余额错误。验证存款环节的唯一方法是真实运行(广播`approve`,等待确认,然后`deposit`)。这里dry-run通过就意味着"授权没问题";**在真实运行前请与用户确认**,因为这是不可逆的设备上签名。一旦授权存在,重新运行`--dry-run`就会正常模拟存款。

earn withdraw

earn withdraw

Unstakes (Solana) or redeems from a vault (Ethereum). Touches the device — bypass the sandbox.
  • Ethereum:
    --product <vault-id>
    required;
    --amount
    optional. The amount is in the vault's asset units (e.g.
    '50 USDC'
    ); if a ticker is given it must match the vault asset. Omit
    --amount
    for a full exit:
    the CLI sends
    amount:"max"
    and the backend redeems the entire share balance, leaving no dust (don't compute the asset amount yourself for a full exit — the share→asset rate drifts).
  • Solana:
    --stake-account <address>
    (required). Two-phase: run once to
    undelegate
    (deactivate), wait for the deactivation epoch boundary (~2–3 days), then re-run with
    --finalize
    to withdraw the now-inactive lamports back to the main account. coin-solana computes the withdrawable amount on-chain, so
    --amount
    is ignored on finalize.
bash
undefined
解除质押(Solana)或从金库赎回(Ethereum)。需要连接设备 —— 请绕过沙箱。
  • Ethereum: 必填
    --product <vault-id>
    ;
    --amount
    可选。金额以金库的资产单位计(例如
    '50 USDC'
    );如果提供了币种代码,必须与金库资产匹配。全额退出请省略
    --amount
    :
    CLI会发送
    amount:"max"
    ,后端会赎回全部份额余额,不留零头(全额退出时不要自己计算资产金额 —— 份额兑资产的汇率会波动)。
  • Solana: 必填
    --stake-account <address>
    。分两阶段: 第一次运行执行
    undelegate
    (解除委托/停用),等待停用周期边界(约2-3天),然后使用
    --finalize
    重新运行,将已停用的lamports提取回主账户。coin-solana会在链上计算可提取金额,因此finalize时会忽略
    --amount
    参数。
bash
undefined

Ethereum vault redeem

Ethereum金库赎回

wallet-cli earn withdraw ethereum-1 --product 1_0x7daeba3f… --amount '50 USDC'
wallet-cli earn withdraw ethereum-1 --product 1_0x7daeba3f… --amount '50 USDC'

Solana phase 1: deactivate

Solana阶段1:解除委托

wallet-cli earn withdraw solana-1 --stake-account <stakeAccountAddr>
wallet-cli earn withdraw solana-1 --stake-account <stakeAccountAddr>

Solana phase 2 (after ~2–3 days): withdraw

Solana阶段2(约2-3天后):提取

wallet-cli earn withdraw solana-1 --stake-account <stakeAccountAddr> --finalize

Get the Solana `--stake-account` address from `earn positions <account>` (its `stakes[]` / `→ --stake-account` lines) — that's the stake account created by your earlier `earn deposit`.

---
wallet-cli earn withdraw solana-1 --stake-account <stakeAccountAddr> --finalize

从`earn positions <account>`中获取Solana的`--stake-account`地址(其`stakes[]` / `→ --stake-account`行)—— 这是你之前通过`earn deposit`创建的质押账户。

---

Common errors

常见错误

ErrorCauseFix
Amount must include a ticker
--amount
missing ticker
Ask the user which asset they mean — do not guess. Then pass the ticker inline, e.g.
--amount '0.5 ETH'
.
Ticker UNKN not found in account
ticker not in account balancesRun
balances <account>
and show the user the tickers held by this account. Ask the user which ticker to use, or whether they meant a different account — do not silently substitute another ticker.
[✖] Wrong app. Open Ledger dashboard.
(exit code 4)
genuine-check
invoked while a currency app is open. Unlike other device commands,
genuine-check
targets the dashboard and has no auto-launch path.
Ask the user to exit the foreground app on the device (short-press both buttons on the app's main screen until
Quit
shows, then confirm), then re-run
genuine-check
. Other device commands (
account discover
,
receive
,
send
,
swap execute
) don't hit this — they auto-prompt the correct app launch.
[✖] Rejected on device. No action taken.
user rejected a sign request on deviceThe rejection was deliberate. Ask the user whether to retry or abort — do not auto-retry. If they retry, have them review amount, recipient, and fees on the device screen before approving.
[✖] Rejected on device. App was not opened.
user rejected the app-open prompt on deviceAsk the user to confirm the app-open prompt on the device and re-run the command.
[✖] Timed out talking to the Ledger over USB. The device may be busy or locked. Retry the command.
sandbox blocking USB, or device busy/lockedSurface to the user that the command needs
dangerouslyDisableSandbox: true
and ask for confirmation before re-running with the bypass. The bypass is expected for device commands (
account discover
,
receive
,
send
,
genuine-check
,
swap execute
); if this error fires on any other command, investigate before bypassing rather than disabling the sandbox by reflex.
[object Object]
or garbled APDU output
two device commands running in parallel (contention)Run device-touching commands sequentially — never in parallel tool calls.
[✖] Ledger not detected. Plug in, unlock, retry.
(exit code 3)
device powered off or unpluggedAsk the user to power on the device, unlock it, and connect via USB, then re-run the command.
device-state … awaiting_approval … reason: unlock
(JSON stream)
device lockedKeep the command running — the CLI resumes automatically once unlocked. Ask the user to unlock the device with their PIN.
错误信息原因解决方法
Amount must include a ticker
--amount
缺少币种代码
询问用户指的是哪种资产 —— 不要猜测。然后将币种代码内联传入,例如
--amount '0.5 ETH'
。
Ticker UNKN not found in account
币种代码不在账户余额中运行
balances <account>
,向用户展示该账户持有的币种代码。询问用户要使用哪个币种,或者他们是否指的是另一个账户 —— 不要静默替换为其他币种。
[✖] Wrong app. Open Ledger dashboard.
(退出码4)
打开了币种应用时调用了
genuine-check
。与其他设备命令不同,
genuine-check
针对的是设备主界面,没有自动启动路径。
请让用户退出设备上的前台应用(在应用主界面短按两个按钮,直到显示
Quit
,然后确认),然后重新运行
genuine-check
。其他设备命令(
account discover
、
receive
、
send
、
swap execute
)不会遇到这个问题 —— 它们会自动提示启动正确的应用。
[✖] Rejected on device. No action taken.
用户在设备上拒绝了签名请求拒绝是用户故意的。询问用户是重试还是中止 —— 不要自动重试。如果他们重试,请让他们在设备屏幕上确认金额、收款方和手续费后再批准。
[✖] Rejected on device. App was not opened.
用户在设备上拒绝了应用启动提示请让用户在设备上确认应用启动提示,然后重新运行命令。
[✖] Timed out talking to the Ledger over USB. The device may be busy or locked. Retry the command.
沙箱阻止了USB连接,或设备繁忙/已锁定向用户说明该命令需要
dangerouslyDisableSandbox: true
,并在重新运行前请求用户确认是否绕过沙箱。设备相关命令(
account discover
、
receive
、
send
、
genuine-check
、
swap execute
)需要绕过沙箱是正常的;如果其他命令出现此错误,请先调查原因再绕过,不要习惯性地禁用沙箱。
[object Object]
或 APDU输出乱码
两个设备命令并行运行(争用)按顺序运行涉及设备的命令 —— 绝不要在并行工具调用中运行。
[✖] Ledger not detected. Plug in, unlock, retry.
(退出码3)
设备关机或未连接请让用户开机、解锁设备并通过USB连接,然后重新运行命令。
device-state … awaiting_approval … reason: unlock
(JSON流)
设备已锁定保持命令运行 —— 设备解锁后CLI会自动恢复。请让用户用PIN码解锁设备。