我发现最新Claude code已无法直接在Android termux用npm安装来直接使用,会有报错,肯定是Termux环境的兼容问题,毕竟不是标准的Linux。如何解决:
1.非proot方案(推荐)
#!/data/data/com.termux/files/usr/bin/bash
set -euo pipefail
readonly SCRIPT_NAME="$(basename "$0")"
readonly PREFIX_DIR="${PREFIX:-/data/data/com.termux/files/usr}"
readonly STATE_DIR="${CLAUDE_CODE_HOME:-$HOME/.claude-code-termux}"
readonly NODE_DIR="$STATE_DIR/node"
readonly WRAPPER_BIN_DIR="$STATE_DIR/bin"
readonly PATCH_DIR="$STATE_DIR/patches"
readonly GLOBAL_PREFIX_DIR="$STATE_DIR/npm-global"
readonly GLOBAL_BIN_DIR="$GLOBAL_PREFIX_DIR/bin"
readonly NPM_CACHE_DIR="$STATE_DIR/npm-cache"
readonly TMP_ROOT_DIR="${TMPDIR:-$PREFIX_DIR/tmp}"
readonly GLIBC_LDSO="$PREFIX_DIR/glibc/lib/ld-linux-aarch64.so.1"
readonly GLIBC_RUNNER_BIN="$PREFIX_DIR/bin/grun"
readonly GLIBC_MARKER="$STATE_DIR/.glibc-arch"
readonly HOST_CLAUDE_PATH="$PREFIX_DIR/bin/claude"
readonly BACKUP_DIR="$STATE_DIR/backups"
readonly CLAUDE_PACKAGE_NAME="@anthropic-ai/claude-code"
readonly CLAUDE_PACKAGE_VERSION="${CLAUDE_CODE_VERSION:-latest}"
readonly NODE_VERSION="${CLAUDE_CODE_NODE_VERSION:-22.22.0}"
readonly NODE_TARBALL="node-v${NODE_VERSION}-linux-arm64.tar.xz"
readonly NODE_URL="https://nodejs.org/dist/v${NODE_VERSION}/${NODE_TARBALL}"
readonly COMPAT_PATCH_PATH="$PATCH_DIR/claude-glibc-compat.js"
readonly CLAUDE_EXE_PATH="$GLOBAL_PREFIX_DIR/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe"
readonly HOST_WRAPPER_MARKER="# claude-code-termux-nonproot-wrapper"
readonly C_BOLD_BLUE="\033[1;34m"
readonly C_BOLD_GREEN="\033[1;32m"
readonly C_BOLD_YELLOW="\033[1;33m"
readonly C_BOLD_RED="\033[1;31m"
readonly C_RESET="\033[0m"
info() {
printf '%b[INFO]%b %s\n' "$C_BOLD_BLUE" "$C_RESET" "$*"
}
success() {
printf '%b[ OK ]%b %s\n' "$C_BOLD_GREEN" "$C_RESET" "$*"
}
warn() {
printf '%b[WARN]%b %s\n' "$C_BOLD_YELLOW" "$C_RESET" "$*" >&2
}
die() {
printf '%b[ERR ]%b %s\n' "$C_BOLD_RED" "$C_RESET" "$*" >&2
exit 1
}
usage() {
cat <<EOF
Usage:
bash $SCRIPT_NAME
What it does:
1. Installs Termux dependencies needed for a glibc-based Node runtime.
2. Installs glibc-runner through pacman (no proot distro).
3. Downloads official Node.js ${NODE_VERSION} linux-arm64.
4. Wraps node/npm with ld.so so they run on Termux.
5. Installs ${CLAUDE_PACKAGE_NAME} and exposes it as:
$HOST_CLAUDE_PATH
Environment overrides:
CLAUDE_CODE_HOME install state dir, default: $STATE_DIR
CLAUDE_CODE_VERSION npm package version/tag, default: $CLAUDE_PACKAGE_VERSION
CLAUDE_CODE_NODE_VERSION Node.js linux-arm64 version, default: $NODE_VERSION
Notes:
- This follows the non-proot glibc-wrapper approach used by openclaw-android.
- Only aarch64 Termux is supported.
- Existing $HOST_CLAUDE_PATH will be backed up if it is not already managed.
EOF
}
command_exists() {
command -v "$1" >/dev/null 2>&1
}
require_termux() {
[ -d "$PREFIX_DIR" ] || die "This script must run in Termux."
command_exists pkg || die "pkg not found. This script must run in Termux."
}
ensure_tmp_root() {
mkdir -p "$TMP_ROOT_DIR"
}
ensure_state_dirs() {
mkdir -p "$STATE_DIR" "$WRAPPER_BIN_DIR" "$PATCH_DIR" "$GLOBAL_PREFIX_DIR" \
"$GLOBAL_BIN_DIR" "$NPM_CACHE_DIR" "$BACKUP_DIR"
}
ensure_termux_package() {
local package_name="$1"
if dpkg -s "$package_name" >/dev/null 2>&1; then
success "Termux package already installed: $package_name"
return 0
fi
info "Installing Termux package: $package_name"
pkg install -y "$package_name"
success "Installed Termux package: $package_name"
}
ensure_glibc_runner() {
local arch
local pacman_conf
local siglevel_patched=0
arch="$(uname -m)"
[ "$arch" = "aarch64" ] || die "glibc mode only supports aarch64, got: $arch"
if [ -f "$GLIBC_MARKER" ] && [ -x "$GLIBC_LDSO" ]; then
success "glibc-runner already available"
return 0
fi
ensure_termux_package "pacman"
pacman_conf="$PREFIX_DIR/etc/pacman.conf"
info "Initializing pacman for glibc-runner"
if [ -f "$pacman_conf" ] && ! grep -q '^SigLevel = Never' "$pacman_conf"; then
cp "$pacman_conf" "${pacman_conf}.bak"
sed -i 's/^SigLevel\s*=.*/SigLevel = Never/' "$pacman_conf"
siglevel_patched=1
warn "Applied temporary pacman SigLevel workaround"
fi
pacman-key --init 2>/dev/null || true
pacman-key --populate 2>/dev/null || true
info "Installing glibc-runner"
if ! pacman -Sy glibc-runner --noconfirm --assume-installed bash,patchelf,resolv-conf; then
if [ "$siglevel_patched" -eq 1 ] && [ -f "${pacman_conf}.bak" ]; then
mv "${pacman_conf}.bak" "$pacman_conf"
fi
die "Failed to install glibc-runner"
fi
if [ "$siglevel_patched" -eq 1 ] && [ -f "${pacman_conf}.bak" ]; then
mv "${pacman_conf}.bak" "$pacman_conf"
success "Restored pacman SigLevel"
fi
[ -x "$GLIBC_LDSO" ] || die "glibc dynamic linker not found at $GLIBC_LDSO"
touch "$GLIBC_MARKER"
success "glibc-runner is ready"
}
write_compat_patch() {
info "Writing Node compatibility patch"
cat >"$COMPAT_PATCH_PATH" <<'EOF'
'use strict';
const childProcess = require('child_process');
const dns = require('dns');
const fs = require('fs');
const os = require('os');
const path = require('path');
const prefix = process.env.PREFIX || '/data/data/com.termux/files/usr';
const home = process.env.HOME || '/data/data/com.termux/files/home';
const wrapperPath = process.env._CLAUDE_WRAPPER_PATH || path.join(home, '.claude-code-termux', 'bin', 'node');
const termuxExec = path.join(prefix, 'lib', 'libtermux-exec-ld-preload.so');
const termuxShell = path.join(prefix, 'bin', 'sh');
try {
if (fs.existsSync(wrapperPath)) {
Object.defineProperty(process, 'execPath', {
value: wrapperPath,
writable: true,
configurable: true,
});
}
} catch {}
if (process.env._CLAUDE_ORIG_LD_PRELOAD) {
process.env.LD_PRELOAD = process.env._CLAUDE_ORIG_LD_PRELOAD;
delete process.env._CLAUDE_ORIG_LD_PRELOAD;
} else if (!process.env.LD_PRELOAD) {
try {
if (fs.existsSync(termuxExec)) {
process.env.LD_PRELOAD = termuxExec;
}
} catch {}
}
const originalCpus = os.cpus;
os.cpus = function cpus() {
try {
const result = originalCpus.call(os);
if (Array.isArray(result) && result.length > 0) {
return result;
}
} catch {}
return [{
model: 'unknown',
speed: 0,
times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 },
}];
};
const originalNetworkInterfaces = os.networkInterfaces;
os.networkInterfaces = function networkInterfaces() {
try {
return originalNetworkInterfaces.call(os);
} catch {
return {
lo: [{
address: '127.0.0.1',
netmask: '255.0.0.0',
family: 'IPv4',
mac: '00:00:00:00:00:00',
internal: true,
cidr: '127.0.0.1/8',
}],
};
}
};
if (!fs.existsSync('/bin/sh') && fs.existsSync(termuxShell)) {
const originalExec = childProcess.exec;
const originalExecSync = childProcess.execSync;
childProcess.exec = function exec(command, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
options = options || {};
if (!options.shell) {
options.shell = termuxShell;
}
return originalExec.call(childProcess, command, options, callback);
};
childProcess.execSync = function execSync(command, options) {
options = options || {};
if (!options.shell) {
options.shell = termuxShell;
}
return originalExecSync.call(childProcess, command, options);
};
}
try {
let dnsServers = ['8.8.8.8', '8.8.4.4'];
try {
const resolvConf = fs.readFileSync(path.join(prefix, 'etc', 'resolv.conf'), 'utf8');
const matches = resolvConf.match(/^nameserver\s+(.+)$/gm);
if (matches && matches.length > 0) {
dnsServers = matches.map((line) => line.replace(/^nameserver\s+/, '').trim());
}
} catch {}
try {
dns.setServers(dnsServers);
} catch {}
const originalLookup = dns.lookup;
const originalLookupPromise = dns.promises.lookup;
dns.lookup = function lookup(hostname, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
const originalOptions = options;
const opts = typeof options === 'number' ? { family: options } : (options || {});
const wantAll = opts.all === true;
const family = opts.family || 0;
const resolveWith = (fam, done) => {
const resolver = fam === 6 ? dns.resolve6 : dns.resolve4;
resolver(hostname, done);
};
const tryResolve = (fam) => {
resolveWith(fam, (error, addresses) => {
if (!error && Array.isArray(addresses) && addresses.length > 0) {
const resolvedFamily = fam === 6 ? 6 : 4;
if (wantAll) {
callback(null, addresses.map((address) => ({
address,
family: resolvedFamily,
})));
return;
}
callback(null, addresses[0], resolvedFamily);
return;
}
if (family === 0 && fam === 4) {
tryResolve(6);
return;
}
originalLookup.call(dns, hostname, originalOptions, callback);
});
};
tryResolve(family === 6 ? 6 : 4);
};
dns.promises.lookup = async function lookup(hostname, options) {
const opts = typeof options === 'number' ? { family: options } : (options || {});
const wantAll = opts.all === true;
const family = opts.family || 0;
const resolveWith = family === 6 ? dns.promises.resolve6 : dns.promises.resolve4;
try {
const addresses = await resolveWith(hostname);
if (addresses.length > 0) {
const resolvedFamily = family === 6 ? 6 : 4;
if (wantAll) {
return addresses.map((address) => ({
address,
family: resolvedFamily,
}));
}
return {
address: addresses[0],
family: resolvedFamily,
};
}
} catch {}
if (family === 0) {
try {
const addresses = await dns.promises.resolve6(hostname);
if (addresses.length > 0) {
if (wantAll) {
return addresses.map((address) => ({ address, family: 6 }));
}
return {
address: addresses[0],
family: 6,
};
}
} catch {}
}
return originalLookupPromise.call(dns.promises, hostname, options);
};
} catch {}
EOF
success "Compatibility patch written to $COMPAT_PATCH_PATH"
}
write_node_wrappers() {
local node_bin_path
local node_real_path
node_bin_path="$NODE_DIR/bin/node"
node_real_path="$NODE_DIR/bin/node.real"
if [ -f "$node_real_path" ]; then
:
elif [ -f "$node_bin_path" ]; then
mv "$node_bin_path" "$node_real_path"
else
die "Node binary missing at $node_bin_path"
fi
info "Writing node/npm wrappers"
cat >"$WRAPPER_BIN_DIR/node" <<EOF
#!$PREFIX_DIR/bin/bash
[ -n "\${LD_PRELOAD:-}" ] && export _CLAUDE_ORIG_LD_PRELOAD="\$LD_PRELOAD"
unset LD_PRELOAD
export _CLAUDE_WRAPPER_PATH="$WRAPPER_BIN_DIR/node"
export TMPDIR="\${TMPDIR:-$TMP_ROOT_DIR}"
_CLAUDE_COMPAT="$COMPAT_PATCH_PATH"
if [ -f "\$_CLAUDE_COMPAT" ]; then
case "\${NODE_OPTIONS:-}" in
*"\$_CLAUDE_COMPAT"*) ;;
*) export NODE_OPTIONS="\${NODE_OPTIONS:+\$NODE_OPTIONS }-r \$_CLAUDE_COMPAT" ;;
esac
fi
exec "$GLIBC_LDSO" --library-path "$PREFIX_DIR/glibc/lib" "$NODE_DIR/bin/node.real" "\$@"
EOF
cat >"$WRAPPER_BIN_DIR/npm" <<EOF
#!$PREFIX_DIR/bin/bash
export PATH="$WRAPPER_BIN_DIR:$NODE_DIR/bin:\$PATH"
export TMPDIR="\${TMPDIR:-$TMP_ROOT_DIR}"
export NPM_CONFIG_PREFIX="$GLOBAL_PREFIX_DIR"
export npm_config_prefix="$GLOBAL_PREFIX_DIR"
export NPM_CONFIG_CACHE="$NPM_CACHE_DIR"
export npm_config_cache="$NPM_CACHE_DIR"
export NPM_CONFIG_SCRIPT_SHELL="$PREFIX_DIR/bin/sh"
export npm_config_script_shell="$PREFIX_DIR/bin/sh"
exec "$WRAPPER_BIN_DIR/node" "$NODE_DIR/lib/node_modules/npm/bin/npm-cli.js" "\$@"
EOF
cat >"$WRAPPER_BIN_DIR/npx" <<EOF
#!$PREFIX_DIR/bin/bash
export PATH="$WRAPPER_BIN_DIR:$NODE_DIR/bin:\$PATH"
export TMPDIR="\${TMPDIR:-$TMP_ROOT_DIR}"
export NPM_CONFIG_PREFIX="$GLOBAL_PREFIX_DIR"
export npm_config_prefix="$GLOBAL_PREFIX_DIR"
export NPM_CONFIG_CACHE="$NPM_CACHE_DIR"
export npm_config_cache="$NPM_CACHE_DIR"
export NPM_CONFIG_SCRIPT_SHELL="$PREFIX_DIR/bin/sh"
export npm_config_script_shell="$PREFIX_DIR/bin/sh"
exec "$WRAPPER_BIN_DIR/node" "$NODE_DIR/lib/node_modules/npm/bin/npx-cli.js" "\$@"
EOF
chmod 755 "$WRAPPER_BIN_DIR/node" "$WRAPPER_BIN_DIR/npm" "$WRAPPER_BIN_DIR/npx"
success "node/npm wrappers are ready"
}
install_node_runtime() {
local installed_version
local tmp_dir
local extract_dir
local fresh_dir
ensure_termux_package "curl"
ensure_termux_package "xz-utils"
if [ -x "$WRAPPER_BIN_DIR/node" ]; then
installed_version="$("$WRAPPER_BIN_DIR/node" --version 2>/dev/null | sed 's/^v//')"
if [ "$installed_version" = "$NODE_VERSION" ]; then
success "Node.js already installed: v$installed_version"
write_compat_patch
write_node_wrappers
return 0
fi
fi
info "Downloading official Node.js ${NODE_VERSION} linux-arm64"
tmp_dir="$(mktemp -d "$TMP_ROOT_DIR/claude-node.XXXXXX")"
curl -fL --max-time 300 "$NODE_URL" -o "$tmp_dir/$NODE_TARBALL"
success "Downloaded $NODE_TARBALL"
extract_dir="$tmp_dir/extract"
fresh_dir="$tmp_dir/node-fresh"
mkdir -p "$extract_dir" "$fresh_dir"
tar -xJf "$tmp_dir/$NODE_TARBALL" -C "$extract_dir"
mv "$extract_dir"/node-v"${NODE_VERSION}"-linux-arm64/* "$fresh_dir"/
rm -rf "$NODE_DIR"
mkdir -p "$(dirname "$NODE_DIR")"
mv "$fresh_dir" "$NODE_DIR"
write_compat_patch
write_node_wrappers
rm -rf "$tmp_dir"
success "Node.js runtime installed in $NODE_DIR"
}
install_claude_package() {
local package_spec
package_spec="$CLAUDE_PACKAGE_NAME"
if [ "$CLAUDE_PACKAGE_VERSION" != "latest" ]; then
package_spec="${CLAUDE_PACKAGE_NAME}@${CLAUDE_PACKAGE_VERSION}"
fi
info "Installing $package_spec"
PATH="$WRAPPER_BIN_DIR:$GLOBAL_BIN_DIR:$PATH" "$WRAPPER_BIN_DIR/npm" install -g "$package_spec"
[ -e "$GLOBAL_BIN_DIR/claude" ] || die "npm install completed, but $GLOBAL_BIN_DIR/claude was not created"
[ -x "$CLAUDE_EXE_PATH" ] || die "Claude native binary missing at $CLAUDE_EXE_PATH"
success "Claude Code is installed under $GLOBAL_PREFIX_DIR"
}
backup_existing_launcher() {
local backup_path
if [ ! -e "$HOST_CLAUDE_PATH" ]; then
return 0
fi
if grep -Fq "$HOST_WRAPPER_MARKER" "$HOST_CLAUDE_PATH" 2>/dev/null; then
success "Managed host launcher already present"
return 0
fi
backup_path="$BACKUP_DIR/claude.host-backup.$(date +%Y%m%d_%H%M%S)"
cp "$HOST_CLAUDE_PATH" "$backup_path"
success "Backed up existing launcher to $backup_path"
}
install_host_wrapper() {
local tmp_wrapper
tmp_wrapper="$(mktemp "$TMP_ROOT_DIR/claude-wrapper.XXXXXX")"
cat >"$tmp_wrapper" <<EOF
#!$PREFIX_DIR/bin/bash
$HOST_WRAPPER_MARKER
export PATH="$WRAPPER_BIN_DIR:$GLOBAL_BIN_DIR:\$PATH"
export TMPDIR="\${TMPDIR:-$TMP_ROOT_DIR}"
exec "$GLIBC_RUNNER_BIN" -t "$CLAUDE_EXE_PATH" "\$@"
EOF
chmod 755 "$tmp_wrapper"
cp "$tmp_wrapper" "$HOST_CLAUDE_PATH"
chmod 755 "$HOST_CLAUDE_PATH"
rm -f "$tmp_wrapper"
success "Installed host launcher: $HOST_CLAUDE_PATH"
}
verify_install() {
info "Verifying Node wrapper"
"$WRAPPER_BIN_DIR/node" --version
info "Verifying npm wrapper"
"$WRAPPER_BIN_DIR/npm" --version
info "Verifying Claude Code launcher"
"$HOST_CLAUDE_PATH" --version
success "Non-proot Claude Code setup completed"
}
main() {
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
require_termux
ensure_tmp_root
ensure_state_dirs
ensure_glibc_runner
install_node_runtime
install_claude_package
backup_existing_launcher
install_host_wrapper
verify_install
cat <<EOF
Run Claude Code with:
claude
Current configuration:
state dir: $STATE_DIR
node version: $NODE_VERSION
package version: $CLAUDE_PACKAGE_VERSION
launcher: $HOST_CLAUDE_PATH
EOF
}
main "$@"
2.proot方案 (会卡顿 卡就对了 卡了就等待)
#!/data/data/com.termux/files/usr/bin/bash
set -euo pipefail
readonly SCRIPT_NAME="$(basename "$0")"
readonly DISTRO_NAME="${CLAUDE_CODE_DISTRO:-debian}"
readonly CLAUDE_PACKAGE_NAME="@anthropic-ai/claude-code"
readonly CLAUDE_PACKAGE_VERSION="${CLAUDE_CODE_VERSION:-latest}"
readonly PREFIX_DIR="${PREFIX:-/data/data/com.termux/files/usr}"
readonly HOST_CLAUDE_PATH="$PREFIX_DIR/bin/claude"
readonly PROOT_ROOT_DIR="$PREFIX_DIR/var/lib/proot-distro/installed-rootfs"
readonly BACKUP_DIR="$HOME/.codex/tmp"
readonly WRAPPER_MARKER="# claude-code-termux-wrapper"
readonly C_BOLD_BLUE="\033[1;34m"
readonly C_BOLD_GREEN="\033[1;32m"
readonly C_BOLD_YELLOW="\033[1;33m"
readonly C_BOLD_RED="\033[1;31m"
readonly C_RESET="\033[0m"
info() {
printf '%b[INFO]%b %s\n' "$C_BOLD_BLUE" "$C_RESET" "$*"
}
success() {
printf '%b[ OK ]%b %s\n' "$C_BOLD_GREEN" "$C_RESET" "$*"
}
warn() {
printf '%b[WARN]%b %s\n' "$C_BOLD_YELLOW" "$C_RESET" "$*" >&2
}
die() {
printf '%b[ERR ]%b %s\n' "$C_BOLD_RED" "$C_RESET" "$*" >&2
exit 1
}
usage() {
cat <<EOF
Usage:
bash $SCRIPT_NAME
What it does:
1. Installs proot-distro in Termux if needed.
2. Installs Debian userspace if needed.
3. Installs nodejs + npm inside Debian.
4. Installs ${CLAUDE_PACKAGE_NAME} inside Debian.
5. Replaces Termux's claude launcher with a wrapper that forwards into Debian.
Environment overrides:
CLAUDE_CODE_DISTRO proot distro alias, default: ${DISTRO_NAME}
CLAUDE_CODE_VERSION npm package version/tag, default: ${CLAUDE_PACKAGE_VERSION}
Notes:
- Official Claude Code npm binaries do not support Termux's android-arm64 host.
- This script uses Debian in proot as the supported Linux runtime.
EOF
}
command_exists() {
command -v "$1" >/dev/null 2>&1
}
require_termux() {
[ -d "$PREFIX_DIR" ] || die "This script must run in Termux."
command_exists pkg || die "pkg not found. This script must run in Termux."
}
ensure_termux_package() {
local package_name="$1"
if dpkg -s "$package_name" >/dev/null 2>&1; then
success "Termux package already installed: $package_name"
return 0
fi
info "Installing Termux package: $package_name"
pkg install -y "$package_name"
success "Installed Termux package: $package_name"
}
ensure_distro() {
if [ -d "$PROOT_ROOT_DIR/$DISTRO_NAME" ]; then
success "proot distro already installed: $DISTRO_NAME"
return 0
fi
info "Installing proot distro: $DISTRO_NAME"
proot-distro install "$DISTRO_NAME"
success "Installed proot distro: $DISTRO_NAME"
}
run_in_distro() {
local command_text="$1"
proot-distro login "$DISTRO_NAME" -- bash -lc "$command_text"
}
ensure_distro_packages() {
info "Updating apt metadata inside $DISTRO_NAME"
run_in_distro "env DEBIAN_FRONTEND=noninteractive apt-get update"
info "Installing nodejs and npm inside $DISTRO_NAME"
run_in_distro "env DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs npm"
success "nodejs and npm are ready inside $DISTRO_NAME"
}
install_claude_in_distro() {
local package_spec="$CLAUDE_PACKAGE_NAME"
if [ "$CLAUDE_PACKAGE_VERSION" != "latest" ]; then
package_spec="${CLAUDE_PACKAGE_NAME}@${CLAUDE_PACKAGE_VERSION}"
fi
info "Installing ${package_spec} inside $DISTRO_NAME"
run_in_distro "npm install -g ${package_spec@Q}"
success "Claude Code is installed inside $DISTRO_NAME"
}
backup_existing_launcher() {
local backup_path
mkdir -p "$BACKUP_DIR"
if [ ! -e "$HOST_CLAUDE_PATH" ]; then
return 0
fi
if grep -Fq "$WRAPPER_MARKER" "$HOST_CLAUDE_PATH" 2>/dev/null; then
success "Managed Termux launcher already present"
return 0
fi
backup_path="$BACKUP_DIR/claude.host-backup.$(date +%Y%m%d_%H%M%S)"
cp "$HOST_CLAUDE_PATH" "$backup_path"
success "Backed up existing launcher to $backup_path"
}
install_host_wrapper() {
local tmp_wrapper
tmp_wrapper="$(mktemp "${TMPDIR:-/tmp}/claude-wrapper.XXXXXX")"
cat >"$tmp_wrapper" <<EOF
#!/data/data/com.termux/files/usr/bin/sh
$WRAPPER_MARKER
work_dir=\$PWD
if [ ! -d "\$work_dir" ]; then
work_dir=/root
fi
exec proot-distro login --shared-tmp --work-dir "\$work_dir" $DISTRO_NAME -- /usr/local/bin/claude "\$@"
EOF
chmod 755 "$tmp_wrapper"
cp "$tmp_wrapper" "$HOST_CLAUDE_PATH"
chmod 755 "$HOST_CLAUDE_PATH"
rm -f "$tmp_wrapper"
success "Installed Termux launcher: $HOST_CLAUDE_PATH"
}
verify_install() {
info "Verifying Claude inside $DISTRO_NAME"
run_in_distro "claude --version"
info "Verifying Termux launcher"
"$HOST_CLAUDE_PATH" --version
success "Claude Code setup completed"
}
main() {
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
require_termux
ensure_termux_package "proot-distro"
ensure_distro
ensure_distro_packages
install_claude_in_distro
backup_existing_launcher
install_host_wrapper
verify_install
cat <<EOF
Run Claude Code with:
claude
Current configuration:
distro: $DISTRO_NAME
host launcher: $HOST_CLAUDE_PATH
EOF
}
main "$@"
1 个帖子 - 1 位参与者