docs: añadir guías paso a paso (01-05) y scripts faltantes
Cubre el flujo completo desde Termux limpio (F-Droid/GitHub) hasta personalizaciones de Claude Code, con base en la restauración de 2026-06-15 y el handoff curado del 2026-06-10. - bin/: añade mdns-publish.py y ssh-fallback - config/: añade smb.conf.example y aliases samba/mDNS en bashrc.snippet - docs/: nuevos 01-termux-fresh-install, 02-base-packages, 03-ssh-mdns-tailscale, 04-samba, 05-claude-code - install.sh: copia los 7 scripts e instala zeroconf vía pip - README.md: índice de docs y sanitización de identificadores personales Gotchas documentados: - Samba msg.lock 0755 tras tar restore - zeroconf no viaja en backup tar - fallos silenciosos por 2>/dev/null en .bashrc Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0d5d099dee
commit
f7ad71bd3c
19
README.md
19
README.md
|
|
@ -21,7 +21,7 @@ Claude Code v2.1.113+ switched to native binaries, breaking Termux/Android compa
|
|||
## Quick Install
|
||||
|
||||
```bash
|
||||
git clone <your-gitea-url>/andres/claude-code-termux.git
|
||||
git clone <your-git-host>/<your-user>/claude-code-termux.git
|
||||
cd claude-code-termux
|
||||
bash install.sh
|
||||
```
|
||||
|
|
@ -40,7 +40,9 @@ bash install.sh --dry-run
|
|||
| `claude-check-env` | `~/.local/bin/` | Environment validator |
|
||||
| `claude-update` | `~/.local/bin/` | Safe updater (ceiling-aware) |
|
||||
| `mdns-resolve` | `~/.local/bin/` | Pure Python mDNS resolver |
|
||||
| `ssh-mdns-proxy` | `~/.local/bin/` | SSH ProxyCommand with mDNS+Tailscale |
|
||||
| `mdns-publish.py` | `~/.local/bin/` | Publish this device as `movil.local` (needs `zeroconf`) |
|
||||
| `ssh-mdns-proxy` | `~/.local/bin/` | SSH ProxyCommand: mDNS → Tailscale |
|
||||
| `ssh-fallback` | `~/.local/bin/` | SSH ProxyCommand: Tailscale → LAN fallback |
|
||||
| `settings.json` | `~/.claude/` | Disables auto-updater |
|
||||
|
||||
## Usage
|
||||
|
|
@ -71,6 +73,19 @@ export TAILSCALE_DOMAIN="your-tailnet.ts.net"
|
|||
|
||||
`claude-update` enforces this automatically. See `docs/TROUBLESHOOTING.md` for details.
|
||||
|
||||
## Docs
|
||||
|
||||
Step-by-step guides for each piece of the stack:
|
||||
|
||||
| # | Doc | Covers |
|
||||
|---|-----|--------|
|
||||
| 01 | [docs/01-termux-fresh-install.md](docs/01-termux-fresh-install.md) | Clean Termux install (F-Droid / GitHub Releases, not Play) |
|
||||
| 02 | [docs/02-base-packages.md](docs/02-base-packages.md) | Required `pkg` / `pip` / `npm` packages |
|
||||
| 03 | [docs/03-ssh-mdns-tailscale.md](docs/03-ssh-mdns-tailscale.md) | SSH, `.local` resolution, Tailscale |
|
||||
| 04 | [docs/04-samba.md](docs/04-samba.md) | Samba server on port 4450 + `msg.lock` gotcha |
|
||||
| 05 | [docs/05-claude-code.md](docs/05-claude-code.md) | Claude Code internals (version ceiling, `clauded`, settings) |
|
||||
| — | [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) | Common errors and fixes |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) for:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
#!/data/data/com.termux/files/usr/bin/python
|
||||
"""Publica servicios via mDNS/Zeroconf: SSH + Samba"""
|
||||
import socket
|
||||
import signal
|
||||
import sys
|
||||
from zeroconf import ServiceInfo, Zeroconf, IPVersion
|
||||
|
||||
HOSTNAME = 'movil'
|
||||
|
||||
SERVICES = [
|
||||
{
|
||||
'type': '_ssh._tcp.local.',
|
||||
'port': 8022,
|
||||
'props': {'description': 'Termux SSH'},
|
||||
},
|
||||
{
|
||||
'type': '_smb._tcp.local.',
|
||||
'port': 4450,
|
||||
'props': {'description': 'Termux Samba', 'path': '/'},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def get_local_ip():
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(('8.8.8.8', 80))
|
||||
ip = s.getsockname()[0]
|
||||
s.close()
|
||||
return ip
|
||||
except Exception:
|
||||
return '127.0.0.1'
|
||||
|
||||
|
||||
def main():
|
||||
ip = get_local_ip()
|
||||
addr = socket.inet_aton(ip)
|
||||
zc = Zeroconf(ip_version=IPVersion.V4Only)
|
||||
infos = []
|
||||
|
||||
for svc in SERVICES:
|
||||
info = ServiceInfo(
|
||||
svc['type'],
|
||||
f'{HOSTNAME}.{svc["type"]}',
|
||||
addresses=[addr],
|
||||
port=svc['port'],
|
||||
properties=svc['props'],
|
||||
server=f'{HOSTNAME}.local.',
|
||||
)
|
||||
zc.register_service(info)
|
||||
infos.append(info)
|
||||
print(f'Publicando {HOSTNAME}.local {svc["type"]} -> {ip}:{svc["port"]}')
|
||||
|
||||
def cleanup(sig, frame):
|
||||
print('Deteniendo mDNS...')
|
||||
for info in infos:
|
||||
zc.unregister_service(info)
|
||||
zc.close()
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, cleanup)
|
||||
signal.signal(signal.SIGTERM, cleanup)
|
||||
|
||||
try:
|
||||
signal.pause()
|
||||
except AttributeError:
|
||||
import time
|
||||
while True:
|
||||
time.sleep(3600)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
#
|
||||
# ssh-fallback: Proxy para SSH con fallback automático
|
||||
# Usado como ProxyCommand en ~/.ssh/config
|
||||
#
|
||||
# Uso: ssh-fallback <tailscale_host> <lan_ip> <port>
|
||||
|
||||
TAILSCALE_HOST="$1"
|
||||
LAN_IP="$2"
|
||||
PORT="${3:-22}"
|
||||
|
||||
# Intentar Tailscale primero (1 segundo timeout)
|
||||
if ping -c 1 -W 1 "$TAILSCALE_HOST" &>/dev/null 2>&1; then
|
||||
exec nc -w 5 "$TAILSCALE_HOST" "$PORT"
|
||||
fi
|
||||
|
||||
# Fallback a IP LAN
|
||||
if ping -c 1 -W 1 "$LAN_IP" &>/dev/null 2>&1; then
|
||||
exec nc -w 5 "$LAN_IP" "$PORT"
|
||||
fi
|
||||
|
||||
# Último intento: Tailscale sin verificar
|
||||
exec nc -w 10 "$TAILSCALE_HOST" "$PORT"
|
||||
|
|
@ -13,3 +13,24 @@ export PATH="$HOME/.local/bin:$PATH"
|
|||
|
||||
# Prevent Claude Code auto-updater (enforced at shell level too)
|
||||
export DISABLE_AUTOUPDATER=1
|
||||
|
||||
# === Optional: helper aliases for samba + mDNS services ===
|
||||
# Only useful if you set up smbd and mdns-publish (see docs/04-samba.md, docs/03-ssh-mdns-tailscale.md).
|
||||
|
||||
alias smb-start='smbd -D -s ~/.config/samba/smb.conf 2>>~/.config/samba/samba-startup.log && echo "Samba started on port 4450"'
|
||||
alias smb-stop='pkill smbd && echo "Samba stopped"'
|
||||
alias smb-restart='pkill smbd 2>/dev/null; sleep 1; smbd -D -s ~/.config/samba/smb.conf 2>>~/.config/samba/samba-startup.log && echo "Samba restarted"'
|
||||
alias smb-status='pgrep -a smbd && echo "Port: 4450" || echo "Samba is not running"'
|
||||
alias smb-log='tail -50 ~/.config/samba/samba.log'
|
||||
|
||||
alias mdns-status='pgrep -af mdns-publish || echo "mDNS publisher is not running"'
|
||||
alias mdns-restart='pkill -f mdns-publish; sleep 1; nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 & disown && echo "mDNS restarted"'
|
||||
alias mdns-stop='pkill -f mdns-publish && echo "mDNS stopped"'
|
||||
|
||||
# === Optional: auto-start samba + mDNS on shell login ===
|
||||
# Uncomment the block below if you want services up automatically.
|
||||
# Failures are logged (NOT silenced) — silent failures after a tar restore are the worst class of bug.
|
||||
#
|
||||
# mkdir -p ~/.cache
|
||||
# pgrep -x smbd >/dev/null 2>&1 || smbd -D -s ~/.config/samba/smb.conf 2>>~/.config/samba/samba-startup.log
|
||||
# pgrep -f "mdns-publish" >/dev/null 2>&1 || nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 & disown
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
# Samba on Termux — port 4450 (445 requires root)
|
||||
#
|
||||
# Replace YOUR_USER with the Samba user you created via `smbpasswd -a YOUR_USER`.
|
||||
# Replace HOST_NAME with whatever you want this device to advertise as.
|
||||
# Connect from clients with: \\<ip>:4450 or smb://<ip>:4450/
|
||||
|
||||
[global]
|
||||
workgroup = WORKGROUP
|
||||
server string = Termux Mobile
|
||||
netbios name = HOST_NAME
|
||||
|
||||
# Non-privileged port
|
||||
smb ports = 4450
|
||||
|
||||
security = user
|
||||
map to guest = never
|
||||
passdb backend = tdbsam
|
||||
|
||||
# SMB2/3 only — Windows 11 and modern Linux/macOS prefer these
|
||||
client min protocol = SMB2
|
||||
server min protocol = SMB2
|
||||
server max protocol = SMB3
|
||||
client max protocol = SMB3
|
||||
|
||||
smb encrypt = desired
|
||||
server signing = desired
|
||||
client signing = desired
|
||||
|
||||
# Termux paths (these MUST exist before smbd starts)
|
||||
private dir = /data/data/com.termux/files/home/.config/samba/private
|
||||
lock directory = /data/data/com.termux/files/home/.config/samba/lock
|
||||
state directory = /data/data/com.termux/files/home/.config/samba/state
|
||||
cache directory = /data/data/com.termux/files/home/.config/samba/cache
|
||||
pid directory = /data/data/com.termux/files/home/.config/samba
|
||||
|
||||
log file = /data/data/com.termux/files/home/.config/samba/samba.log
|
||||
max log size = 500
|
||||
log level = 1
|
||||
|
||||
socket options = TCP_NODELAY IPTOS_LOWDELAY
|
||||
use sendfile = yes
|
||||
aio read size = 16384
|
||||
aio write size = 16384
|
||||
|
||||
unix charset = UTF-8
|
||||
dos charset = CP850
|
||||
|
||||
server multi channel support = yes
|
||||
mdns name = mdns
|
||||
|
||||
# Share: Android shared storage (DCIM, Downloads, etc.)
|
||||
[Storage]
|
||||
comment = Android shared storage
|
||||
path = /storage/emulated/0
|
||||
browseable = yes
|
||||
writable = yes
|
||||
valid users = YOUR_USER
|
||||
create mask = 0644
|
||||
directory mask = 0755
|
||||
|
||||
# Share: Termux $HOME (be careful, this exposes shell config too)
|
||||
[Termux]
|
||||
comment = Termux home
|
||||
path = /data/data/com.termux/files/home
|
||||
browseable = yes
|
||||
writable = yes
|
||||
valid users = YOUR_USER
|
||||
create mask = 0644
|
||||
directory mask = 0755
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# 01 — Fresh Termux install (Android)
|
||||
|
||||
This guide covers a clean Termux install **without** Google Play, which is required for Termux:API / Termux:Boot compatibility.
|
||||
|
||||
## Why not Google Play
|
||||
|
||||
Google Play freezes Termux on an old version. Addons (Termux:API, Termux:Boot, Termux:Widget) require signature parity with the base app, so the Play version is incompatible with the F-Droid / GitHub-Release addons.
|
||||
|
||||
**Decision:** install everything from F-Droid or GitHub Releases (Termux team's official builds).
|
||||
|
||||
## Recommended source
|
||||
|
||||
Use one of:
|
||||
- [F-Droid](https://f-droid.org/en/packages/com.termux/) — auto-updates, signed by F-Droid
|
||||
- [GitHub Releases](https://github.com/termux/termux-app/releases) — latest, signed by the Termux team
|
||||
|
||||
Both share the same signing key, so addons work across them. **Don't mix Play and F-Droid/GitHub.**
|
||||
|
||||
## APK list (download to the device beforehand)
|
||||
|
||||
| Package | Purpose | Required |
|
||||
|---------|---------|----------|
|
||||
| `termux-app_*.apk` | Base shell | yes |
|
||||
| `termux-api-app_*.apk` | Sensors, camera, clipboard | recommended |
|
||||
| `termux-boot-app_*.apk` | Auto-start on boot | optional |
|
||||
| `termux-widget-app_*.apk` | Home-screen launchers | optional |
|
||||
|
||||
## First boot
|
||||
|
||||
```bash
|
||||
# 1. Allow Termux access to shared storage (~/storage symlink)
|
||||
termux-setup-storage
|
||||
|
||||
# 2. Prevent Android from killing the process when screen is off
|
||||
termux-wake-lock
|
||||
|
||||
# 3. Update and install the minimum
|
||||
pkg update -y
|
||||
pkg install -y openssh nodejs git proot python
|
||||
```
|
||||
|
||||
## SSH for remote access (optional but useful)
|
||||
|
||||
```bash
|
||||
passwd # set an SSH password
|
||||
sshd # start the daemon on port 8022
|
||||
whoami # note the u0_a*** username for remote login
|
||||
```
|
||||
|
||||
From another machine:
|
||||
```bash
|
||||
ssh -p 8022 u0_a***@<device-ip>
|
||||
```
|
||||
|
||||
If the device is on Tailscale, use the Tailscale IP instead of the LAN one. Termux itself can't run the Tailscale CLI; use the Android Tailscale app and the device will appear in your tailnet automatically.
|
||||
|
||||
## What's next
|
||||
|
||||
Continue with `02-base-packages.md` to install the rest of the stack used by Claude Code and helper scripts.
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# 02 — Base packages
|
||||
|
||||
Minimum set of packages needed by Claude Code and the helper scripts in this repo. Everything here is idempotent — re-running is safe.
|
||||
|
||||
## Required (Claude Code itself)
|
||||
|
||||
```bash
|
||||
pkg install -y \
|
||||
nodejs \
|
||||
python \
|
||||
git \
|
||||
proot \
|
||||
ripgrep
|
||||
```
|
||||
|
||||
| Package | Why |
|
||||
|---------|-----|
|
||||
| `nodejs` | Claude Code runs on Node.js |
|
||||
| `python` | `mdns-resolve`, `mdns-publish.py` |
|
||||
| `git` | Cloning this repo, version control inside Claude |
|
||||
| `proot` | Provides `termux-chroot` used by `clauded` (fixes `/tmp`) |
|
||||
| `ripgrep` | Faster search inside Claude (the bundled one may not run on aarch64) |
|
||||
|
||||
## Recommended (SSH + mDNS)
|
||||
|
||||
```bash
|
||||
pkg install -y openssh termux-api
|
||||
pip install zeroconf
|
||||
```
|
||||
|
||||
| Package | Why |
|
||||
|---------|-----|
|
||||
| `openssh` | SSH server (`sshd`) and client |
|
||||
| `termux-api` | Backs the `termux-*` Android-bridge commands |
|
||||
| `zeroconf` (pip) | Required by `mdns-publish.py`; **does not survive a `tar` backup** of `$HOME`, reinstall after restore |
|
||||
|
||||
## Optional (file sharing)
|
||||
|
||||
```bash
|
||||
pkg install -y samba
|
||||
```
|
||||
|
||||
See `04-samba.md` for the rest of the setup (custom port 4450, share definitions).
|
||||
|
||||
## Optional (image support in Claude)
|
||||
|
||||
```bash
|
||||
npm install -g @img/sharp-wasm32 sharp --force
|
||||
```
|
||||
|
||||
Enables image input (screenshots, diagrams). Sometimes flaky on aarch64; safe to skip.
|
||||
|
||||
## Verifying
|
||||
|
||||
```bash
|
||||
node --version # >= 22 LTS recommended
|
||||
python --version # 3.11+
|
||||
proot --version
|
||||
ssh -V
|
||||
```
|
||||
|
||||
If any of these fail, re-run the relevant `pkg install`.
|
||||
|
||||
## What's next
|
||||
|
||||
`03-ssh-mdns-tailscale.md` — set up dynamic hostname resolution so you never hardcode IPs.
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# 03 — SSH, mDNS and Tailscale on Termux
|
||||
|
||||
Termux's Bionic libc does **not** resolve `.local` names. This repo ships small Python helpers that fix this without needing root or a custom resolver.
|
||||
|
||||
## Scripts involved
|
||||
|
||||
| Script | Role |
|
||||
|--------|------|
|
||||
| `mdns-resolve` | Resolve `<host>.local` via raw multicast UDP. Returns the IP or non-zero. |
|
||||
| `mdns-publish.py` | Publish this device on the LAN as `movil.local` (or whatever you choose) using `zeroconf`. |
|
||||
| `ssh-mdns-proxy` | SSH `ProxyCommand` that tries mDNS first, then Tailscale DNS. Used from `~/.ssh/config`. |
|
||||
| `ssh-fallback` | Lower-level fallback: tries Tailscale, then LAN. Useful when Tailscale is flaky. |
|
||||
|
||||
## SSH config
|
||||
|
||||
Drop the contents of `config/ssh_config.example` into `~/.ssh/config` (or merge it).
|
||||
|
||||
The key entries:
|
||||
|
||||
```ssh-config
|
||||
Host *.local
|
||||
ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p
|
||||
|
||||
Host lenovo-ideapad
|
||||
ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p
|
||||
|
||||
Host *.YOUR-TAILNET.ts.net
|
||||
User YOUR_USER
|
||||
```
|
||||
|
||||
Adjust the username and the tailnet domain. The proxy command will try mDNS first, then fall through to your Tailscale magic-DNS name (env `TAILSCALE_DOMAIN`).
|
||||
|
||||
## Publishing this device as `movil.local`
|
||||
|
||||
If you want the device to be reachable as `movil.local` from your LAN:
|
||||
|
||||
```bash
|
||||
# One-shot foreground (to see errors):
|
||||
python ~/.local/bin/mdns-publish.py
|
||||
|
||||
# Background (typical use, from ~/.bashrc):
|
||||
nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 &
|
||||
disown
|
||||
```
|
||||
|
||||
**Gotcha:** `zeroconf` (the pip package) is **not** part of a `pkg`-managed install. If you restore Termux from a `tar` backup, `zeroconf` will be missing from `$PREFIX/lib/python*/site-packages` and the script will silently fail with `ModuleNotFoundError`. Re-run `pip install zeroconf` after every restore.
|
||||
|
||||
## Logging, not silencing
|
||||
|
||||
The `.bashrc` auto-start lines in this repo (`config/bashrc.snippet`) deliberately log to a file instead of `/dev/null`. Silent failures are the single most painful class of bug after a restore — keep the logs.
|
||||
|
||||
## Tailscale
|
||||
|
||||
Tailscale on Termux runs **as the Android app**, not as a CLI inside Termux. You can't `tailscale up` from a shell. The app provides magic DNS for `*.YOUR-TAILNET.ts.net`, which Bionic-libc resolves fine (it's just a regular DNS query).
|
||||
|
||||
When you list hosts in `~/.ssh/config`, prefer:
|
||||
1. `<host>.local` for fast LAN access
|
||||
2. `<host>.YOUR-TAILNET.ts.net` for off-LAN
|
||||
|
||||
Never hardcode IPs — they rotate.
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# 04 — Samba server on Termux (port 4450)
|
||||
|
||||
Optional but useful: expose a few folders over SMB so other devices on the LAN can read/write files without ADB.
|
||||
|
||||
## Why port 4450
|
||||
|
||||
Ports below 1024 require root. Samba on Termux runs unprivileged, so we use a high port (`4450`) and configure clients to point at it explicitly.
|
||||
|
||||
```
|
||||
\\<device-ip>:4450
|
||||
```
|
||||
|
||||
Most file managers accept `smb://<ip>:4450/`.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pkg install -y samba
|
||||
mkdir -p ~/.config/samba/lock ~/.config/samba/private
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy `config/smb.conf.example` to `~/.config/samba/smb.conf` and edit the share paths to match your device. Then add a Samba user:
|
||||
|
||||
```bash
|
||||
smbpasswd -a YOUR_USER
|
||||
```
|
||||
|
||||
This creates `~/.config/samba/private/passdb.tdb`.
|
||||
|
||||
## Start / stop
|
||||
|
||||
```bash
|
||||
smbd -D -s ~/.config/samba/smb.conf 2>>~/.config/samba/samba-startup.log
|
||||
pkill smbd
|
||||
```
|
||||
|
||||
The aliases in `config/bashrc.snippet` (`smb-start`, `smb-stop`, `smb-restart`, `smb-status`, `smb-log`) wrap these.
|
||||
|
||||
## Critical gotcha — `msg.lock` permissions after restore
|
||||
|
||||
If you restore `~/.config/samba/` from a `tar` backup, the `lock/msg.lock` directory ends up with mode `0700` because `tar` preserves the source attributes. `smbd` requires `0755` on this directory and refuses to start with:
|
||||
|
||||
```
|
||||
invalid permissions on directory '.../msg.lock': has 0700 should be 0755
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
|
||||
```bash
|
||||
chmod 0755 ~/.config/samba/lock/msg.lock
|
||||
```
|
||||
|
||||
This must be re-run after every restore from a backup that includes the samba lock dir.
|
||||
|
||||
## Connecting from another machine
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
smbclient -L //<device-ip> -p 4450 -U YOUR_USER
|
||||
mount -t cifs //<device-ip>/Home /mnt -o port=4450,username=YOUR_USER
|
||||
|
||||
# Windows: \\<device-ip>:4450 in Explorer
|
||||
|
||||
# macOS: Finder → Go → Connect to Server → smb://<device-ip>:4450
|
||||
```
|
||||
|
||||
## Discovery via mDNS
|
||||
|
||||
If you also run `mdns-publish.py` (see `03-ssh-mdns-tailscale.md`), the device shows up as `movil.local` and SMB clients can use that name directly.
|
||||
|
||||
## Don't bind to `0.0.0.0` if you don't need to
|
||||
|
||||
The default `smb.conf` example binds to all interfaces. If the device roams across hostile WiFi networks, restrict `interfaces =` and `bind interfaces only = yes` to the LAN interface (`wlan0` on most devices).
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
# 05 — Claude Code on Termux
|
||||
|
||||
The reason this whole repo exists. Two non-obvious things break Claude Code on Termux:
|
||||
|
||||
1. **Version ceiling: v2.1.112.** Starting v2.1.113, the CLI ships as a native binary (SEA) for `linux-x64`, `linux-arm64-glibc`, `linux-arm64-musl`, `darwin-*`, `win32-*`. Termux's `process.platform === 'android'` is not in that list. Even `--force`-installing the `linux-arm64` package fails because the binary needs `/lib/ld-linux-aarch64.so.1` or `/lib/ld-musl-aarch64.so.1`, neither of which exist in Bionic libc.
|
||||
2. **`/tmp` is hardcoded.** Claude Code writes to `/tmp/claude/...` for background tasks. Termux doesn't have a writable `/tmp`. `termux-chroot` exposes `/tmp -> /usr/tmp` to **all child processes** (which `proot -b` alone does not).
|
||||
|
||||
## Install
|
||||
|
||||
If you just want it working, run the top-level installer:
|
||||
|
||||
```bash
|
||||
bash install.sh
|
||||
```
|
||||
|
||||
Otherwise, the manual steps are:
|
||||
|
||||
```bash
|
||||
# 1. Pin to the last compatible version
|
||||
npm install -g @anthropic-ai/claude-code@2.1.112
|
||||
|
||||
# 2. Protect against the auto-updater (it can ignore env vars)
|
||||
chmod -R a-w $PREFIX/lib/node_modules/@anthropic-ai/claude-code/
|
||||
|
||||
# 3. Make the wrapper available
|
||||
cp bin/clauded ~/.local/bin/
|
||||
chmod +x ~/.local/bin/clauded
|
||||
```
|
||||
|
||||
## Settings
|
||||
|
||||
`~/.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"env": { "DISABLE_AUTOUPDATER": "1" },
|
||||
"skipDangerousModePermissionPrompt": true,
|
||||
"effortLevel": "xhigh"
|
||||
}
|
||||
```
|
||||
|
||||
`DISABLE_AUTOUPDATER` is also exported from `bashrc.snippet`. Belt and suspenders — the auto-updater has been observed to ignore the env var in some releases, which is why `chmod -R a-w` is the real defense.
|
||||
|
||||
## How `clauded` works
|
||||
|
||||
```
|
||||
clauded
|
||||
└── termux-chroot
|
||||
└── claude --dangerously-skip-permissions "$@"
|
||||
```
|
||||
|
||||
`termux-chroot` simulates a Linux FHS where `/tmp`, `/etc`, `/usr` etc. point to their Termux equivalents. Crucially, every child process inherits this view, so any background task Claude spawns also sees a working `/tmp`.
|
||||
|
||||
## Updating safely
|
||||
|
||||
```bash
|
||||
claude-update
|
||||
```
|
||||
|
||||
This script:
|
||||
- Checks npm for the latest version.
|
||||
- Compares against the ceiling (`2.1.112`).
|
||||
- Refuses to upgrade past the ceiling.
|
||||
- Reapplies `chmod -R a-w` after a successful install.
|
||||
|
||||
If a future Anthropic release re-supports Android (or restores the pure-Node fallback), bump the ceiling in `claude-update` and the README.
|
||||
|
||||
## Authenticating
|
||||
|
||||
```bash
|
||||
clauded
|
||||
# Inside Claude: /login
|
||||
```
|
||||
|
||||
Subscription accounts and API keys both work. The OAuth flow opens a URL; long-press to copy it on Android and paste it in a real browser (Chromium, Firefox).
|
||||
|
||||
## When the wheels come off
|
||||
|
||||
See `TROUBLESHOOTING.md` for:
|
||||
- `EACCES: /tmp/claude/...`
|
||||
- `native binary not installed`
|
||||
- `claude --version` works but `clauded` errors
|
||||
- Sharp / image support
|
||||
- `proot-distro` escape hatch (lets you run newer versions inside a real glibc rootfs)
|
||||
14
install.sh
14
install.sh
|
|
@ -36,7 +36,7 @@ if [[ -z "$PREFIX" || ! -d "$PREFIX" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
TOTAL=8
|
||||
TOTAL=9
|
||||
echo -e "${GREEN}=== Claude Code for Termux - Installer ===${NC}"
|
||||
echo "Version ceiling: v$CLAUDE_VERSION"
|
||||
$DRY_RUN && echo -e "${YELLOW}(dry-run mode - no changes will be made)${NC}"
|
||||
|
|
@ -59,7 +59,7 @@ fi
|
|||
# 4. Install scripts
|
||||
step 4 "Installing scripts to $BIN_DIR..."
|
||||
run mkdir -p "$BIN_DIR"
|
||||
for script in clauded claude-check-env claude-update mdns-resolve ssh-mdns-proxy; do
|
||||
for script in clauded claude-check-env claude-update mdns-resolve mdns-publish.py ssh-mdns-proxy ssh-fallback; do
|
||||
run cp "$SCRIPT_DIR/bin/$script" "$BIN_DIR/$script"
|
||||
run chmod +x "$BIN_DIR/$script"
|
||||
done
|
||||
|
|
@ -91,8 +91,14 @@ run npm install -g @img/sharp-wasm32 sharp --force 2>/dev/null || {
|
|||
echo -e " ${YELLOW}Sharp install failed (optional, images won't work)${NC}"
|
||||
}
|
||||
|
||||
# 8. Done
|
||||
step 8 "Verifying installation..."
|
||||
# 8. Install zeroconf for mDNS publisher (needed by mdns-publish.py)
|
||||
step 8 "Installing zeroconf (pip) for mDNS publishing..."
|
||||
run pip install --quiet zeroconf 2>/dev/null || {
|
||||
echo -e " ${YELLOW}zeroconf install failed (optional, mdns-publish won't work)${NC}"
|
||||
}
|
||||
|
||||
# 9. Done
|
||||
step 9 "Verifying installation..."
|
||||
if ! $DRY_RUN; then
|
||||
echo ""
|
||||
"$BIN_DIR/claude-check-env"
|
||||
|
|
|
|||
Loading…
Reference in New Issue