285 lines
12 KiB
Markdown
285 lines
12 KiB
Markdown
# shusub2
|
||
|
||
- 文档层级:客户端工具
|
||
- 应用性质:客户端 TUI
|
||
- 源码来源:repo 原生
|
||
- 运行关系:客户端工具
|
||
- 部署模型:无
|
||
- 对应服务:无
|
||
- 对应 stack:无
|
||
- Secret 边界:本地用户配置
|
||
- 分发方式:`uvx` / `uv tool install`。
|
||
- 包名:`shusub2`
|
||
- 命令名:`shusub2`
|
||
- 本地状态路径:`~/.config/shusub2/`
|
||
- 目标 API:`cliproxy-codex-quota` 的 `/api/tui/accounts`、可选 `sub2api-status` `/api/status`、可选 Pricing Monitor `/api/ui-data?view=accounts`,以及可选 Sub2API `/api/v1/admin/usage` 和 `/api/v1/admin/ops/errors`。
|
||
- 备注:账号数据无 secret,不需要 SSH、数据库访问、API key、OAuth credentials 或 plaintext env;请求与错误日志可选持有 Sub2API admin API key,仅存本机 `~/.config/shusub2/logs-token`(0600)或 `SHUSUB2_LOGS_TOKEN`。
|
||
|
||
Single-page terminal dashboard for the token-safe Sub2API account feed,
|
||
request logs, and merged cn/us error logs. Dedicated full-table views remain
|
||
available for each data set.
|
||
|
||
## Quick Start
|
||
|
||
Run once from a public Git repo with `uvx`:
|
||
|
||
```bash
|
||
uvx --from git+https://gitea.shujk.top/shujakuin/shusub2.git shusub2 \
|
||
--api-url https://codex.server2.shujk.top/1232131231313123/api/tui/accounts
|
||
```
|
||
|
||
Bootstrap a new machine from `uvx`: save the API URL, install `shusub2` as a
|
||
user command, then run it later as `shusub2`:
|
||
|
||
```bash
|
||
uvx --from git+https://gitea.shujk.top/shujakuin/shusub2.git shusub2 \
|
||
--api-url https://codex.server2.shujk.top/1232131231313123/api/tui/accounts \
|
||
--install
|
||
shusub2
|
||
```
|
||
|
||
Install as a user command:
|
||
|
||
```bash
|
||
uv tool install git+https://gitea.shujk.top/shujakuin/shusub2.git
|
||
shusub2 --api-url https://codex.server2.shujk.top/1232131231313123/api/tui/accounts --save-config
|
||
```
|
||
|
||
Configure the default public API URL for `shusub2`:
|
||
|
||
```bash
|
||
mkdir -p ~/.config/shusub2
|
||
chmod 700 ~/.config/shusub2
|
||
printf '%s\n' 'https://codex.server2.shujk.top/1232131231313123/api/tui/accounts' > ~/.config/shusub2/api-url
|
||
chmod 600 ~/.config/shusub2/api-url
|
||
shusub2
|
||
```
|
||
|
||
Environment variables override the config file:
|
||
|
||
- `SHUSUB2_API_URL`
|
||
- `SUB2API_QUOTA_TUI_API_URL`
|
||
- `SHUSUB2_API_URL_FILE`
|
||
- `SHUSUB2_STATUS_URL`
|
||
- `SHUSUB2_STATUS_URL_FILE`
|
||
- `SHUSUB2_PRICING_URL`
|
||
- `SHUSUB2_PRICING_URL_FILE`
|
||
- `SHUSUB2_LOGS_URL`
|
||
- `SHUSUB2_LOGS_URL_FILE`
|
||
- `SHUSUB2_LOGS_TOKEN`
|
||
- `SHUSUB2_LOGS_TOKEN_FILE`
|
||
- `SHUSUB2_LOGS_REFRESH_SECONDS`
|
||
- `SHUSUB2_LOGS_LIMIT`
|
||
- `SHUSUB2_ERRORS_CN_URL`
|
||
- `SHUSUB2_ERRORS_CN_URL_FILE`
|
||
- `SHUSUB2_ERRORS_US_URL`
|
||
- `SHUSUB2_ERRORS_US_URL_FILE`
|
||
- `SHUSUB2_ERRORS_REFRESH_SECONDS`
|
||
- `SHUSUB2_ERRORS_LIMIT`
|
||
- `SHUSUB2_ERRORS_TIME_RANGE`
|
||
- `SHUSUB2_VERSION_CHECK_URL`
|
||
- `SHUSUB2_VERSION_CHECK_TIMEOUT`
|
||
- `SHUSUB2_NO_VERSION_CHECK`
|
||
|
||
## Unified Dashboard
|
||
|
||
`shusub2` opens a compact dashboard for Accounts, Keys, Logs, and Errors. Its
|
||
summary also reports Pricing Monitor upstream health and CNY total. Press `a`,
|
||
`k`, `l`, or `e` to focus a dashboard table; press `p` to open the full
|
||
Upstreams source-balance page. `/` filters the active table and `r` refreshes
|
||
all data immediately. Automatic Accounts and Upstreams, Keys/Logs, and Errors refresh defaults
|
||
to every five minutes. The client requests
|
||
gzip-compressed JSON and transparently decodes it when the upstream supports it.
|
||
The two-line detail area follows the selected row. Accounts, Keys, Logs, and
|
||
Errors use green, yellow, magenta, and red section styling respectively.
|
||
|
||
The dashboard sizes every column from the fetched content so wide terminals show
|
||
complete keys, accounts, and models. On a narrow terminal, a table scrolls
|
||
horizontally instead of truncating a field:
|
||
|
||
```text
|
||
ACCOUNT | Group | Today | Daily | 5h | 7d | Avail
|
||
UPSTREAMS (p) | Source | Kind | Balance | CNY | Unit | Status | Updated | Age
|
||
KEY | Today | Tokens | Req
|
||
LOG KEY | Account | Model | First | Duration | Tok/s | Input | Output | Cache | Tokens | Cost | Time | Age
|
||
ERR | Status | Key | Account | Model | Time | Age
|
||
```
|
||
|
||
The Keys table shows today's usage sorted by actual cost, including key name,
|
||
tokens, and request count. It uses the same admin API key as Logs and Errors;
|
||
without that token the table is hidden and the summary remains compact.
|
||
|
||
Use `--accounts`, `--pricing`, `--logs`, or `--errors` to start a dedicated
|
||
full-table view. The corresponding `--once` form still prints only that data set.
|
||
|
||
## Request Logs Page
|
||
|
||
The middle dashboard table shows request logs, and `l` focuses it.
|
||
`shusub2 --logs` starts the dedicated logs page, while
|
||
`shusub2 --once --logs` prints one logs snapshot to stdout.
|
||
|
||
The logs page reads the latest requests (default 100, `--logs-limit`) from the
|
||
Sub2API admin usage API and refreshes every five minutes by default
|
||
(`--logs-refresh-seconds` / `SHUSUB2_LOGS_REFRESH_SECONDS`). Columns:
|
||
|
||
```text
|
||
Key | Account | Model | Effort | Type | Input | Output | Cache | Tokens | Cost | First | Duration | Tok/s | Time | Age
|
||
```
|
||
|
||
`Type` is the Sub2API `request_type` (`sync` / `stream` / `ws_v2` / `cyber`).
|
||
`Tokens` is input + output + cache write + cache read; the detail line below
|
||
the table shows the per-bucket breakdown, actual cost, first-token latency,
|
||
decode speed, upstream model mapping, user, and request id.
|
||
|
||
`Effort` is the request's `reasoning_effort` (`-` when absent). `First` is the
|
||
first-token latency and `Duration` the total request time, both shown in
|
||
seconds. `Tok/s` is the decode throughput computed as
|
||
`output_tokens / (duration - first_token)`; it shows `-` when there is no
|
||
output or no positive decode window. `Input`, `Output`, and `Cache` are token
|
||
buckets; `Cache` combines cache write and cache read. `Tokens` is their total.
|
||
`Age` is relative to local current time (`now`, `5m ago`, `2h ago`, etc.). In the TUI each
|
||
API key name is rendered in a stable per-key color so rows from the same
|
||
key are easy to group visually (`--once --logs` output stays plain text).
|
||
|
||
The logs URL defaults to `https://sub2apicn.shujk.top/api/v1/admin/usage` and
|
||
can be overridden with `--logs-url` / `SHUSUB2_LOGS_URL` /
|
||
`~/.config/shusub2/logs-url`.
|
||
|
||
The page needs a Sub2API **admin API key** (generated in the Sub2API web UI
|
||
under Settings). Configure it once:
|
||
|
||
```bash
|
||
mkdir -p ~/.config/shusub2 && chmod 700 ~/.config/shusub2
|
||
printf '%s\n' '<sub2api-admin-api-key>' > ~/.config/shusub2/logs-token
|
||
chmod 600 ~/.config/shusub2/logs-token
|
||
```
|
||
|
||
or via `SHUSUB2_LOGS_TOKEN`. The key is sent as the `x-api-key` header and is
|
||
never written anywhere else by the client. Without a token the dashboard Logs
|
||
and Errors tables stay empty and show a configuration hint; the Accounts table
|
||
keeps working without any secret.
|
||
|
||
## Errors Page
|
||
|
||
The bottom dashboard table shows merged errors, and `e` focuses it.
|
||
`shusub2 --errors` starts the dedicated errors page, while
|
||
`shusub2 --once --errors` prints one merged snapshot to stdout.
|
||
|
||
The page pulls the latest admin ops error logs from **both** cn and us in
|
||
parallel, then merges them by `created_at`:
|
||
|
||
- cn default: `https://sub2apicn.shujk.top/api/v1/admin/ops/errors`
|
||
- us default: `https://us.sub2.shujk.top/api/v1/admin/ops/errors`
|
||
|
||
`us.sub2.shujk.top` is the DNS-only standard HTTPS direct entry for the same
|
||
server4 backend. It avoids the Cloudflare 1010 behavior sometimes returned by
|
||
`sub2apius.shujk.top` to non-browser clients while avoiding the fixed `:19857`
|
||
port in the default client configuration. Use `--errors-us-url` to select a
|
||
different direct origin when required.
|
||
|
||
The dashboard and dedicated errors page refresh every five minutes by default
|
||
(`--errors-refresh-seconds` / `SHUSUB2_ERRORS_REFRESH_SECONDS`). Query window
|
||
defaults to `24h` (`--errors-time-range` / `SHUSUB2_ERRORS_TIME_RANGE`); each source uses
|
||
`page=1` and `page_size` from `--errors-limit` (default 100, max 500).
|
||
|
||
Columns:
|
||
|
||
```text
|
||
Node | Status | Key | Account | Model | Phase | Type | Owner | Time | Age
|
||
```
|
||
|
||
`Age` is relative to local current time (`now`, `5m ago`, `2h ago`, etc.).
|
||
Auth reuses the same admin API key as the logs page.
|
||
|
||
## Local Development
|
||
|
||
```bash
|
||
cd apps/sub2api-quota-tui
|
||
uv run shusub2
|
||
```
|
||
|
||
Run inside zellij:
|
||
|
||
```bash
|
||
zellij action new-pane --name sub2api-quota -- \
|
||
bash -lc 'cd /home/shujakuin/infra/apps/sub2api-quota-tui && uv run shusub2'
|
||
```
|
||
|
||
Configuration:
|
||
|
||
- `--api-url` / `SUB2API_QUOTA_TUI_API_URL`
|
||
- `--status-url` / `SHUSUB2_STATUS_URL`
|
||
- `--pricing-url` / `SHUSUB2_PRICING_URL`
|
||
- `--logs-url` / `SHUSUB2_LOGS_URL`
|
||
- `--logs-token` / `SHUSUB2_LOGS_TOKEN`
|
||
- `--logs-refresh-seconds` / `SHUSUB2_LOGS_REFRESH_SECONDS`
|
||
- `--logs-limit` / `SHUSUB2_LOGS_LIMIT`
|
||
- `--errors-refresh-seconds` / `SHUSUB2_ERRORS_REFRESH_SECONDS`
|
||
- `--errors-limit` / `SHUSUB2_ERRORS_LIMIT`
|
||
- `--errors-time-range` / `SHUSUB2_ERRORS_TIME_RANGE`
|
||
- `--accounts` / `--pricing` / `--logs` / `--errors`
|
||
- `--save-config`
|
||
- `--install`
|
||
- `--refresh-seconds` / `SUB2API_QUOTA_TUI_REFRESH_SECONDS`
|
||
- `--timeout` / `SUB2API_QUOTA_TUI_TIMEOUT`
|
||
|
||
The dashboard reads `/api/tui/accounts`, can optionally read `sub2api-status`
|
||
`/api/status` for channel monitor health, and reads Pricing Monitor's
|
||
`/api/ui-data?view=accounts` for upstream source balances. The default pricing URL
|
||
is the Tailscale HTTPS service; `--pricing-url`, `SHUSUB2_PRICING_URL`, or
|
||
`~/.config/shusub2/pricing-url` can override it. This is a cached, read-only
|
||
projection collected by Pricing Monitor on its own cadence: `shusub2` neither
|
||
contacts upstream providers directly nor asks Pricing Monitor to refresh. The Accounts table does not need SSH,
|
||
database access, API keys, OAuth credentials, or plaintext env files. The Keys,
|
||
Logs, and Errors tables authenticate to the Sub2API admin APIs with
|
||
an admin API key stored only in `~/.config/shusub2/logs-token` (0600) or
|
||
`SHUSUB2_LOGS_TOKEN`.
|
||
|
||
When `--status-url` is omitted, `shusub2` infers a sibling `/api/status` URL
|
||
from account URLs ending in `/api/tui/accounts`, so the public Codex endpoint
|
||
automatically enables monitor availability. On startup it also checks the
|
||
public Gitea repo for a newer package version and prints a short upgrade hint
|
||
when one is available.
|
||
|
||
On the dedicated `--accounts` page, a per-key panel shows today's usage for
|
||
each API key (`Key | Today | Tokens | Req`, sorted by cost, key names in the
|
||
same per-key colors as the logs page). It combines the Sub2API admin
|
||
`dashboard/api-keys-trend` and `dashboard/api-keys-usage` endpoints and
|
||
needs the same admin API key as the logs page; without a token the panel
|
||
stays empty and the status line shows a hint. `--once` prints the same
|
||
panel after the accounts table when a token is configured.
|
||
|
||
The dedicated Accounts page keeps the full columns for scanning inside zellij:
|
||
|
||
```text
|
||
Name | Provider | Group | Daily | Today | Tokens | Req | Kind | 5h | 7d | Reset | Status | Availability
|
||
```
|
||
|
||
`Provider` distinguishes `openai` and `anthropic` accounts from the public
|
||
`platform` field returned by the API.
|
||
|
||
`Daily` is shown as `used/limit` when Sub2API has `quota_daily_*` fields in
|
||
`accounts.extra`; otherwise it is `-`.
|
||
|
||
`5h` / `7d` are only the account feed's used/remaining quota percentages. They
|
||
are not upstream provider balances and therefore never claim to be a cash or
|
||
source-account balance.
|
||
|
||
The dedicated `--pricing` page shows Pricing Monitor source rows such as
|
||
`code-plan`, `codexapis`, `testvideo`, `kedaya`, `ycy`, and `mdkj`: raw available
|
||
balance with its source unit, derived CNY when supplied, health state, and last
|
||
successful collection time. It reads only the existing sanitized source projection.
|
||
|
||
`Group` is the derived Sub2API tier alias group. Higher tiers win when multiple
|
||
aliases exist: `id < slow < fast < sfast`. The table shows `sfast` first, then
|
||
`fast`, `slow`, `id`, and ungrouped accounts.
|
||
|
||
When `--status-url` is configured, the status line shows channel monitor
|
||
health, and the selected account detail shows the matching monitor status when
|
||
one exists. Monitor binding first uses the shared `base_url_hash` emitted by
|
||
the account API and `sub2api-status`; name-token matching remains only as a
|
||
fallback for older status payloads.
|
||
|
||
`Availability` is derived from the bound channel monitor. Accounts without a
|
||
matching monitor show `-`.
|