# dxcli portable

`dxcli-portable` 是不接主控端的单机版客户端。

当前统一客户端版本：`0.6.28`。它与服务端托管版 `dxcli` 使用同一个版本号发布。

## 快速安装命令

本地两文件安装：把下面两个文件上传到目标机器同一个目录，例如 `/root`。

```text
install_dxcli_portable_bundle.sh
dxcli-portable-0.6.28.tar.gz
```

然后在目标机器执行：

```bash
cd /root
bash ./install_dxcli_portable_bundle.sh
```

如需显式指定压缩包和安装目录：

```bash
cd /root
bash ./install_dxcli_portable_bundle.sh \
  --archive ./dxcli-portable-0.6.28.tar.gz \
  --install-dir /root/ls/dxcli-portable
```

在线安装：只上传在线安装脚本到目标机器，例如 `/root`。

```text
install_dxcli_portable_online.sh
```

然后在目标机器执行：

```bash
cd /root
bash ./install_dxcli_portable_online.sh
```

在线安装脚本默认下载：

```text
https://v5lzzuocozpnci5.wsr888.top/Y6cLxn8hPUB1Z78/folder/dxcli-portable/dxcli-portable-0.6.28.tar.gz
```

并校验 `install_dxcli_portable_online.sh` 里内置的 `EXPECTED_MD5`。

本机版本检查：

```bash
dx version
```

它保留 `dxcli` 的本地 `tmux` 托管会话能力：

- `dxs <local_id>` / `dxsa <global_id>` 创建 Codex 托管会话
- `dxsd <local_id>` / `dxsda <global_id>` 创建托管 shell
- `dxa <local_id>` / `dxaa <global_id>` 重新进入会话
- `dx ls` / `dx lsa` 查看本机托管会话
- Codex Stop hook 会在任务结束后直接发送完成信息到 Telegram 频道

模型短码：

- `dxs 1 3` -> `codex -m gpt-5.3-codex-spark`
- `dxs 1 4` -> `codex -m gpt-5.4`
- `dxs 1 5` -> `codex -m gpt-5.5`
- `dxs 1 44` -> `codex -m gpt-5.4 -c 'model_reasoning_effort="xhigh"'`
- `dxs 1 54` -> `codex -m gpt-5.5 -c 'model_reasoning_effort="xhigh"'`

它不包含这些主控端能力：

- 没有 `dx-remote`
- 没有 `dxcli_remote.py`
- 没有 `dxcli_remote_api.py`
- 没有主控端 webhook
- 不支持从 Telegram 或主服务器反向交互客户端

## 两文件安装

推荐发布方式是只上传两个文件到目标机器同一个目录：

- `install_dxcli_portable_bundle.sh`
- `dxcli-portable-0.6.28.tar.gz`

在镜像区先构建压缩包：

```bash
bash /root/dx/dxcli-portable/scripts/build_portable_bundle.sh
```

构建产物在：

```bash
/root/dx/dxcli-portable/dxcli-portable-0.6.28.tar.gz
/root/dx/dxcli-portable/dxcli-portable-latest.tar.gz
```

把安装脚本和压缩包上传到目标机后执行：

```bash
bash ./install_dxcli_portable_bundle.sh
```

默认会暴力覆盖安装到：

```bash
/root/ls/dxcli-portable
```

也可以指定安装目录：

```bash
bash ./install_dxcli_portable_bundle.sh \
  --archive ./dxcli-portable-0.6.28.tar.gz \
  --install-dir /root/ls/dxcli-portable
```

安装器会：

1. 解压同目录的 `dxcli-portable*.tar.gz`
2. 删除并重建 `/root/ls/dxcli-portable`
3. 执行包内 `scripts/setup_dxcli_portable.sh`
4. 写入 `~/.local/bin/dx`
5. 写入 Codex Stop hook 和 Telegram 配置

## 在线安装

如果只想上传一个在线安装脚本，也可以使用：

```bash
bash /root/dx/dxcli-portable/scripts/install_dxcli_portable_online.sh
```

它默认下载：

```text
https://v5lzzuocozpnci5.wsr888.top/Y6cLxn8hPUB1Z78/folder/dxcli-portable/dxcli-portable-0.6.28.tar.gz
```

并校验 `install_dxcli_portable_online.sh` 里内置的 `EXPECTED_MD5`。

只有 MD5 完全一致才会继续解压和安装。这个在线安装脚本不打进 `dxcli-portable-0.6.28.tar.gz`，否则会改变已经发布压缩包的 MD5。

可覆盖下载地址或 MD5：

```bash
bash ./install_dxcli_portable_online.sh \
  --url <archive_url> \
  --md5 <expected_md5>
```

## 源码目录直接安装

如果已经把完整 `dxcli-portable` 目录放到目标机器，也可以直接执行：

```bash
bash /path/to/dxcli-portable/scripts/setup_dxcli_portable.sh
```

默认会写入：

- 状态目录：`~/.dxcli-portable`
- 命令入口：`~/.local/bin/dx`
- Codex hook：`~/.codex/hooks.json`
- Codex hooks feature：`~/.codex/config.toml` 的 `[features].hooks = true`
- Telegram bot token：`8710936223:AAEwrYdMzcyjP1_JK5sM39nsjd7YVIBQSz0`
- Telegram chat id：`-1003965113006`

如果需要覆盖 Telegram 配置：

```bash
bash ./install_dxcli_portable_bundle.sh \
  --telegram-bot-token <bot_token> \
  --telegram-chat-id <chat_id>
```

安装后确认：

```bash
dx ls
cat ~/.dxcli-portable/portable_config.json
cat ~/.codex/hooks.json
```

## Hook 行为

Codex 触发 `Stop` hook 后，`dxcli_codex_hook.py` 会读取 hook payload 和 transcript 尾部内容，然后调用 Telegram `sendMessage`。

Telegram 消息包含：

- host
- global_id
- cwd
- model
- timestamp
- 完成内容或 transcript 摘要

如果 Telegram 发送失败，事件会追加到：

```bash
~/.dxcli-portable/telegram_hook_spool.jsonl
```

当前 portable 版本只做失败落盘，不做后台重试 daemon。

## 可配置项

安装脚本参数：

```bash
setup_dxcli_portable.sh \
  --state-dir ~/.dxcli-portable \
  --codex-dir ~/.codex \
  --bin-dir ~/.local/bin \
  --telegram-bot-token token \
  --telegram-chat-id -100... \
  --hook-spool-path ~/.dxcli-portable/telegram_hook_spool.jsonl
```

运行时环境变量：

```bash
DXCLI_STATE_DIR
DXCLI_PORTABLE_CONFIG_PATH
DXCLI_TELEGRAM_BOT_TOKEN
DXCLI_TELEGRAM_CHAT_ID
DXCLI_HOOK_SPOOL_PATH
DXCLI_CODEX_BIN
DXCLI_FINALSHELL_CURSOR_WHEEL_FILTER
DXCLI_TMUX_MOUSE
```

## 与 dxcli 同步维护

版本管理规则：

- `dxcli` 和 `dxcli-portable` 必须保持同一个 `DXCLI_VERSION`。
- 修改公共文件时两个目录同步，两个版本一起发布。
- 修改分叉文件时只改对应目录，但仍统一提升两个目录的版本号和更新日志。

两个版本应共同维护这些文件的核心逻辑：

- `dxcli.py`
- `dxcli_sessions.py`
- `dxcli_terminal_filter.py`
- `dxcli_metadata.py`
- `scripts/install_codex_hook.py` 中的 Codex hooks feature 写入逻辑

两个版本有意不同的文件：

- `dxcli/dxcli_runtime_config.py`：服务端版，配置主控端 webhook、release、remote state
- `dxcli-portable/dxcli_runtime_config.py`：portable 版，只配置 Telegram 和本地 spool
- `dxcli/dxcli_codex_hook.py`：服务端版，事件发给主控端 webhook 或本地 spool
- `dxcli-portable/dxcli_codex_hook.py`：portable 版，事件直接发 Telegram
- `dxcli/scripts/setup_dxcli_remote.sh`：服务端版安装器
- `dxcli-portable/scripts/setup_dxcli_portable.sh`：portable 版安装器
- `dxcli-portable/scripts/build_portable_bundle.sh`：portable 版压缩包构建器
- `dxcli-portable/scripts/install_dxcli_portable_bundle.sh`：portable 版目标机压缩包安装器

修改建议：

1. 如果改的是 `tmux` 会话、ID、attach、FinalShell 输入过滤，先改 `dxcli`，再同步到 `dxcli-portable`。
2. 如果改的是 hook payload 解析、transcript 摘要提取，两个 hook 都要看，但发送目标不同。
3. 如果改的是主控端交互、远程 JSON API、release 自动升级，只改 `dxcli`。
4. 如果改的是 Telegram 直发、单机安装、一行脚本安装，只改 `dxcli-portable`。
