# CLI 参考

`xq` 是 xiangqin 的客户端。装好跑 `xq --help` 看完整子命令。

## 全局选项

```
xq --help
xq --version
xq --endpoint <URL>       # 覆盖默认服务端（或 XIANGQIN_ENDPOINT 环境变量）
```

默认 endpoint：`http://112.124.27.213`（0.1 期生产）。session 里记住的 endpoint 优先。

## 自动版本检查

任何 `xq` 命令执行时，若 PyPI 上有新版本，**stderr** 打印：

```
[xq] 新版本 0.4.0 可用（当前 0.3.0）。跑 `pip install -U acong-tech-xiangqin` 更新。
```

机制：本地缓存 `~/.xiangqin/version-cache.json`（24h TTL）；缓存过期时后台子进程异步查 PyPI 写缓存，**不阻塞当前命令**。

关闭：

```bash
export XIANGQIN_NO_UPDATE_CHECK=1   # 一次性或永久
```

CI 环境（`CI=true`）自动关。

## 基础

### `xq health`

健康检查。

```bash
xq health
# → {"status":"ok","version":"0.3.1"}
```

## 认证

### `xq register <phone>`

发送注册 / 登录验证码。返回 `request_id`（ULID）。60s 内重复发同号不重复扣码（返回原 request_id）。

```bash
xq register 13800001111
```

### `xq verify <code> --request-id <ULID>`

换 session。`session` 写 `~/.xiangqin/session.json`（权限 600）。

```bash
xq verify 123456 --request-id 01JF...
```

### `xq logout`

删本机 session + 服务端 session 行。

### `xq me`

查当前登录态。

## Profile

### `xq profile show`

### `xq profile set <field> <value>`

字段：`gender / age / city / tags / bio`

- `age` 必须 18-99
- `city` ≤30 字
- `tags` 逗号分隔（`xq profile set tags 程序,登山,做饭`）
- `bio` ≤500 字

### `xq profile clear <tags|bio>`

只允许清可空字段。`gender / age / city` 必填，不让清。

## Query

### `xq query <WHERE> [--limit N] [--json]`

```bash
xq query 'gender=f AND city=hangzhou AND age>=25'
xq query 'tags CONTAINS 登山' --limit 20
xq query 'gender IN (f,nb)' --json
```

- DSL 规则见 [设计 — 受限 WHERE DSL](design.md#受限-where-dsl)
- 默认 `--limit 50`，最大 100
- `--json` 出原始 JSON 供脚本解析

## DM 私信（虚拟信封）

### `xq dm send <user_id> <body>`

主动给对方发私信。消耗 1 个信封。投递失败（对方拉黑 / 对方不存在）不扣。

```bash
xq dm send 01JG... "你好，我是杭州的..."
# → ✓ 已投递（免费额度）。msg=01JH...
#   通知：pushed
```

### `xq dm sent [--limit N]`

查我发出去的私信。

```bash
xq dm sent --limit 10
# 2026-04-23 10:15  → 01JG...  通知=pushed 阅读=已读 已回复  01JH...
```

### `xq dm show <message_id>`

看单条详情（5 维状态）。

```bash
xq dm show 01JH...
```

## Inbox 收件箱

### `xq inbox list [--limit N]`

按时间倒序平铺（不折叠）。`●` = 未读。

```bash
xq inbox list
# ● 2026-04-23 10:15  ← 01JG...  01JH...
#     你好，我是杭州的...
```

### `xq inbox show <message_id>`

看 + 标已读。

### `xq inbox reply <message_id> <body>`

回信（消耗 1 信封）。

### `xq inbox delete <message_id>`

软删（服务端仍保留 30 天）。

### `xq inbox report <message_id> [--reason <text>]`

举报，永久留档。

## 信封

### `xq envelope state`

```bash
xq envelope state
# 今日免费剩余: 2 / 3
# 付费信封余量: 5
# 历史累计购买: 10
# 信封单价:    ¥1.00
```

### `xq envelope buy --count N --mock`

`--mock` 必带（0.1 期真付未接入）。

## 拉黑

### `xq block <user_id>` / `xq unblock <user_id>` / `xq blocklist`

拉黑单向；对方后续发给我的信被拦截（钱退）。

## Profile 新字段（M4）

```bash
xq profile set height 178                # 140-220 cm
xq profile set education bachelor        # highschool/associate/bachelor/master/phd/other
xq profile set agent_gateway_url https://...    # 可选，启用通知需配
xq profile set agent_hooks_token <token>        # 可选
xq profile set notify_on_new_mail on            # on|off
```

## 退出码

| 码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 任意错误（网络 / 业务 / 鉴权失败） |

错误分类靠 stderr 消息文本：

- `未登录` → 过了 session 有效期
- `register failed: 429` → rate limited
- `verify failed: 400` → 验证码错 / 过期
- `query failed: 400` → DSL 不合规
- `buy failed: 400` → count 超限
- `insufficient_envelopes` → 今日免费用完且付费余额 0 → `xq envelope buy`
- `blocked_by_recipient` → 对方已拉黑你（信封退）
- `recipient_not_found` → 对方不存在或已注销
