# Pixel + Termux 复用 Mac OAuth Token 跑 Claude Code > 在 Pixel 10 Pro Fold(Android 16)的 Termux + Ubuntu proot 容器里运行 `claude-run`,复用 Mac 上同款 Anthropic OAuth Token,**完全替换原来的 Kimi 反代方案**。 --- ## 1. 背景 - **设备**:Pixel 10 Pro Fold(代号 `rango`)/ Android 16,bootloader 已解锁,Magisk root - **原方案**:claude-code 通过 Kimi Coding Plan 反代(`https://api.kimi.com/coding/`),用 Kimi token 走所有请求 - **新目标**:直连 `api.anthropic.com`,复用 Mac 上 `~/.zshrc` 里的 `CLAUDE_CODE_OAUTH_TOKEN`,使用真正的 Claude Opus 4.7 - **动机**:Frida 在 Android 16 无法跑(ptrace/CFI/SELinux 多重限制),后续工作转向 LSPosed + 真 Claude 模型,需要 Pixel 端本地也能跑 claude-code --- ## 2. 架构总览 ``` ┌─────────────────────────────────────────────────────────────┐ │ Termux (Android shell, user u0_a327) │ │ │ │ ~/.bashrc │ │ └─ export CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-... │ │ │ │ /usr/bin/claude-run ←─ wrapper │ │ └─ proot-distro login --shared-home ubuntu │ │ -- env CLAUDE_CODE_OAUTH_TOKEN="..." claude "$@" │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Ubuntu proot container │ │ │ │ /root ←(--shared-home)─→ /data/data/com.termux/.../home│ │ │ ├─ .claude.json ← claude 主配置 │ │ │ │ └─ .claude/settings.json ← per-user settings │ │ │ │ │ │ │ │ claude (ELF/glibc, Node.js) │ │ │ │ └─→ HTTPS api.anthropic.com │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` **关键点**: - Claude Code 是 ELF/glibc 编译,Android Bionic 跑不了 → 必须 proot 跑 Ubuntu - `--shared-home` 把 Termux home 绑定为容器 `/root`,配置文件可以从 Termux 端直接编辑 - Token 通过 `env KEY=VAL claude` 在容器内瞬时注入,不进容器 shell 环境 --- ## 3. Claude Code 的双重认证闸门 这是整个迁移最关键的认知 —— claude-code 启动时有两道**独立**闸门: | 闸门 | 检查内容 | headless (`claude -p`) | 交互模式 (`claude`) | |------|---------|----------------------|---------------------| | ① Token 闸门 | `CLAUDE_CODE_OAUTH_TOKEN` env 或 `~/.claude/.credentials.json` 任一存在 | 必须过 | 必须过 | | ② Onboarding 闸门 | `~/.claude.json` 的 `hasCompletedOnboarding === true` | **跳过** | **必须过** | **Kimi 反代为什么"屏蔽"了 Onboarding 闸门**:它的 `.env` 块在启动时被 claude-code 注入进进程,等价于一直在用 API key 模式,OAuth onboarding 检查被旁路掉了。一旦切回官方 Anthropic,闸门被激活,于是裸 `claude-run` 弹出 OAuth URL。 --- ## 4. 完整改动清单 按依赖顺序(缺一不可): ### 4.1 改 wrapper:注入 OAuth Token **文件**:`/data/data/com.termux/files/usr/bin/claude-run` ```sh #!/data/data/com.termux/files/usr/bin/sh # Claude Code via proot-distro with shared home + Mac OAuth token exec proot-distro login --shared-home ubuntu -- env CLAUDE_CODE_OAUTH_TOKEN="sk-ant-oat01-<...>" claude "$@" ``` 权限:`chmod 711`(owner 可读,其他 app 不可读),owner `u0_a327:u0_a327`。 > Token 直接 inline 到 wrapper,避免依赖 shell 启动文件。Android 沙箱保证其他 app 读不到 `/data/data/com.termux/` 下的文件。 ### 4.2 写 settings.json:清理 Kimi 痕迹 **文件**:`/data/data/com.termux/files/home/.claude/settings.json` ```json { "env": {}, "model": "opus", "skipDangerousModePermissionPrompt": true, "theme": "dark" } ``` 权限:`chmod 600`。 > **`env` 必须为空 `{}`** —— 之前 Kimi 配置在这里塞了 `ANTHROPIC_BASE_URL` / `ANTHROPIC_API_KEY` 等,会覆盖 wrapper 注入的 token。 ### 4.3 清理 `~/.claude.json` 的 env 块(最容易漏的一步) **文件**:`/data/data/com.termux/files/home/.claude.json` claude-code 的"主配置文件",顶层有个 `env` 字段会在进程启动时被 setenv 进去,**优先级高于 wrapper 传入的 env**。 操作(用 jq 在 Mac 端处理后 push 回去): ```bash jq 'del(.env) | .customApiKeyResponses.rejected = [] | .model = "opus" | .hasCompletedOnboarding = true | .lastOnboardingVersion = "2.1.144"' \ ~/.claude.json.backup > ~/.claude.json.cleaned ``` 需要保证: - `.env` 字段被**删除**(不是设为空,是整个 key 不存在) - `.hasCompletedOnboarding = true` ← **过 Onboarding 闸门** - `.lastOnboardingVersion = "2.1.144"` ← 与当前 claude-code 版本对齐 - `.model = "opus"`(之前 Kimi 用的 `"opus[1m]"` 在官方 API 不存在) - `.customApiKeyResponses.rejected = []`(清掉之前 reject 过的 Kimi key 历史) 权限:`chmod 600`,owner `u0_a327:u0_a327`。 --- ## 5. 完整执行脚本(从 Mac 端一键执行) ```bash #!/usr/bin/env bash set -euo pipefail # 1. 从 Mac ~/.zshrc 提取 token TOKEN=$(awk -F= '/^export CLAUDE_CODE_OAUTH_TOKEN=/{sub(/^export CLAUDE_CODE_OAUTH_TOKEN=/, ""); print; exit}' ~/.zshrc) [ -z "$TOKEN" ] && { echo "ERROR: token not found in ~/.zshrc"; exit 1; } # 2. 构造文件 mkdir -p /tmp/claude-pixel cat > /tmp/claude-pixel/claude-run < /tmp/claude-pixel/settings.json <<'EOF' { "env": {}, "model": "opus", "skipDangerousModePermissionPrompt": true, "theme": "dark" } EOF # 3. 处理 .claude.json(先 pull,patch,push) adb shell "su -c 'cp /data/data/com.termux/files/home/.claude.json /sdcard/cc.json && chmod 644 /sdcard/cc.json'" adb pull /sdcard/cc.json /tmp/claude-pixel/cc.json adb shell "su -c 'rm /sdcard/cc.json'" jq 'del(.env) | .customApiKeyResponses.rejected = [] | .model = "opus" | .hasCompletedOnboarding = true | .lastOnboardingVersion = "2.1.144"' \ /tmp/claude-pixel/cc.json > /tmp/claude-pixel/cc.patched.json # 4. push & 安装 adb push /tmp/claude-pixel/claude-run /sdcard/claude-run.new adb push /tmp/claude-pixel/settings.json /sdcard/settings.json.new adb push /tmp/claude-pixel/cc.patched.json /sdcard/cc.json adb shell "su -c ' WRAPPER=/data/data/com.termux/files/usr/bin/claude-run SETTINGS=/data/data/com.termux/files/home/.claude/settings.json MAIN=/data/data/com.termux/files/home/.claude.json # 备份原版(只备份一次) [ ! -f \$WRAPPER.prekimi ] && cp \$WRAPPER \$WRAPPER.prekimi [ ! -f \$MAIN.prekimi ] && cp \$MAIN \$MAIN.prekimi # 安装新版 cp /sdcard/claude-run.new \$WRAPPER chown u0_a327:u0_a327 \$WRAPPER && chmod 711 \$WRAPPER mkdir -p \$(dirname \$SETTINGS) cp /sdcard/settings.json.new \$SETTINGS chown u0_a327:u0_a327 \$SETTINGS && chmod 600 \$SETTINGS cp /sdcard/cc.json \$MAIN chown u0_a327:u0_a327 \$MAIN && chmod 600 \$MAIN # 清理 rm /sdcard/claude-run.new /sdcard/settings.json.new /sdcard/cc.json echo done '" # 5. 清理 Mac 临时文件 rm -rf /tmp/claude-pixel ``` --- ## 6. 验证 ### 6.1 headless 模式 ```bash adb shell 'su u0_a327 -c " export HOME=/data/data/com.termux/files/home export PATH=/data/data/com.termux/files/usr/bin:\$PATH cd \$HOME claude-run -p \"reply with just OK\" < /dev/null "' ``` 期望输出:`OK`(前面有几行 `proot warning: can't sanitize binding /proc/self/fd/...` 是 adb 非 tty 模式的良性警告,无视)。 ### 6.2 交互模式(用户在 Termux 里直接跑) ``` $ claude-run ``` 期望:直接进入 Claude Code TUI 界面,**不**弹出 OAuth URL。 ### 6.3 排错检查清单 | 症状 | 检查 | |------|------| | 报错指向 `kimi.com/code/console` | `~/.claude.json` 的 `env` 块没删干净 | | 弹出 OAuth 登录 URL | `~/.claude.json` 的 `hasCompletedOnboarding` 不是 `true` | | `Failed to authenticate. 401` | Mac 那边的 token 已失效,去 Mac 重新登录刷新 | | `Warning: no stdin data received in 3s` | headless 模式没加 `< /dev/null`,等 3 秒后会继续,不是错误 | | `which claude-run` 指向其他路径 | PATH 优先级问题 / 装了 alias | --- ## 7. 回滚 之前的版本都备份在 `.prekimi` 后缀文件里: ```bash adb shell "su -c ' cp /data/data/com.termux/files/usr/bin/claude-run.prekimi /data/data/com.termux/files/usr/bin/claude-run cp /data/data/com.termux/files/home/.claude.json.prekimi /data/data/com.termux/files/home/.claude.json echo rolled back to Kimi config '" ``` --- ## 8. 安全注意 - Wrapper 里 inline 的 OAuth token 是个**长效凭证**,泄露等于账号被借用 - 文件权限:wrapper `711`(不可读其他 app),`.claude.json` `600` - Android 沙箱保证其他 app 无法访问 `/data/data/com.termux/` 下的文件 - 如果手机丢失或共享,**Mac 端立刻去 Anthropic console 撤销该 token**(一撤销 Mac/Pixel 同时失效,需要重新登录刷新) --- ## 9. 已知限制 1. **网络出口**:直连 `api.anthropic.com` 需要稳定国际网络。Pixel 在 4G 蜂窝下不一定通,建议挂代理或走 Wi-Fi 2. **计费归属**:使用 Mac 同一个 Anthropic 账号配额,Pixel 跑多了会消耗 Mac 同账号的 quota 3. **Token 刷新**:OAuth access token 有效期约 1 年,过期后需要在 Mac 端重新执行 OAuth 流程刷新,然后把新 token 同步到 Pixel wrapper 4. **多设备 token 复用合规性**:Anthropic ToS 允许同一账号在多设备使用,但禁止把 token 分享给第三方 --- ## 10. 相关文件索引 | 路径 | 作用 | |------|------| | `/data/data/com.termux/files/usr/bin/claude-run` | wrapper(inline token) | | `/data/data/com.termux/files/usr/bin/claude-run.prekimi` | 旧版备份 | | `/data/data/com.termux/files/home/.claude.json` | claude-code 主配置 | | `/data/data/com.termux/files/home/.claude.json.prekimi` | 主配置备份(含 Kimi) | | `/data/data/com.termux/files/home/.claude/settings.json` | per-user settings | | `/data/data/com.termux/files/usr/var/lib/proot-distro/containers/ubuntu/rootfs` | Ubuntu 容器 rootfs | Mac 端: | 路径 | 作用 | |------|------| | `~/.zshrc` 里的 `CLAUDE_CODE_OAUTH_TOKEN` | Token 来源 | | `~/.claude.json` | Mac 上对应的主配置(参照对比用) |