chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env bash
|
||||
# install-opencode.sh - Install OpenCode on Termux
|
||||
# Uses proot + ld.so concatenation for Bun standalone binaries.
|
||||
#
|
||||
# This script is NON-CRITICAL: failure does not affect OpenClaw.
|
||||
#
|
||||
# Why proot + ld.so concatenation?
|
||||
# 1. Bun uses raw syscalls (LD_PRELOAD shims don't work)
|
||||
# 2. patchelf causes SIGSEGV on Android (seccomp)
|
||||
# 3. Bun standalone reads embedded JS via /proc/self/exe offset
|
||||
# → grun makes /proc/self/exe point to ld.so, breaking this
|
||||
# → concatenating ld.so + binary data fixes the offset math
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
OPENCLAW_DIR="$HOME/.openclaw-android"
|
||||
GLIBC_LDSO="$PREFIX/glibc/lib/ld-linux-aarch64.so.1"
|
||||
PROOT_ROOT="$OPENCLAW_DIR/proot-root"
|
||||
|
||||
fail_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
exit 0
|
||||
}
|
||||
|
||||
echo "=== Installing OpenCode ==="
|
||||
echo ""
|
||||
|
||||
# ── Pre-checks ───────────────────────────────
|
||||
|
||||
if [ ! -f "$OPENCLAW_DIR/.glibc-arch" ]; then
|
||||
fail_warn "glibc environment not installed — skipping OpenCode install"
|
||||
fi
|
||||
|
||||
if [ ! -x "$GLIBC_LDSO" ]; then
|
||||
fail_warn "glibc dynamic linker not found — skipping OpenCode install"
|
||||
fi
|
||||
|
||||
if ! command -v proot &>/dev/null; then
|
||||
echo "Installing proot..."
|
||||
if ! pkg install -y proot; then
|
||||
fail_warn "Failed to install proot — skipping OpenCode install"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Helper: Create ld.so concatenation ───────
|
||||
|
||||
# Bun standalone binaries store embedded JS at the end of the file.
|
||||
# The last 8 bytes contain the original file size as a LE u64.
|
||||
# Bun calculates: embedded_offset = current_file_size - stored_size
|
||||
# By prepending ld.so, current_file_size increases, and the offset
|
||||
# shifts correctly to find the embedded data after ld.so.
|
||||
create_ldso_concat() {
|
||||
local bin_path="$1"
|
||||
local output_path="$2"
|
||||
local name="$3"
|
||||
|
||||
if [ ! -f "$bin_path" ]; then
|
||||
echo -e "${RED}[FAIL]${NC} $name binary not found at $bin_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo " Creating ld.so concatenation for $name..."
|
||||
echo " (Copying large binary files — this may take a minute)"
|
||||
cp "$GLIBC_LDSO" "$output_path"
|
||||
cat "$bin_path" >> "$output_path"
|
||||
chmod +x "$output_path"
|
||||
|
||||
# Verify the Bun magic marker exists at the end
|
||||
local marker
|
||||
marker=$(tail -c 32 "$output_path" | strings 2>/dev/null | grep -o "Bun" || true)
|
||||
if [ -n "$marker" ]; then
|
||||
echo -e "${GREEN}[OK]${NC} $name ld.so concatenation created ($(du -h "$output_path" | cut -f1))"
|
||||
else
|
||||
echo -e "${YELLOW}[WARN]${NC} $name ld.so concatenation created but Bun marker not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Helper: Create proot wrapper script ──────
|
||||
|
||||
create_proot_wrapper() {
|
||||
local wrapper_path="$1"
|
||||
local ldso_path="$2"
|
||||
local bin_path="$3"
|
||||
local name="$4"
|
||||
|
||||
cat > "$wrapper_path" << WRAPPER
|
||||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
# $name wrapper — proot + ld.so concatenation
|
||||
# proot: intercepts raw syscalls (Bun uses inline asm, not glibc calls)
|
||||
# ld.so concat: fixes /proc/self/exe offset for embedded JS
|
||||
# unset LD_PRELOAD: prevents Bionic libtermux-exec.so version mismatch
|
||||
unset LD_PRELOAD
|
||||
exec proot \\
|
||||
-R "$PROOT_ROOT" \\
|
||||
-b "\$PREFIX:\$PREFIX" \\
|
||||
-b /system:/system \\
|
||||
-b /apex:/apex \\
|
||||
-w "\$(pwd)" \\
|
||||
"$ldso_path" "$bin_path" "\$@"
|
||||
WRAPPER
|
||||
chmod +x "$wrapper_path"
|
||||
echo -e "${GREEN}[OK]${NC} $name wrapper script created"
|
||||
}
|
||||
|
||||
# ── Step 1: Create minimal proot rootfs ──────
|
||||
|
||||
echo "Setting up proot minimal rootfs..."
|
||||
mkdir -p "$PROOT_ROOT/data/data/com.termux/files"
|
||||
echo -e "${GREEN}[OK]${NC} proot rootfs created at $PROOT_ROOT"
|
||||
|
||||
# ── Step 2: Install Bun (package manager) ────
|
||||
|
||||
echo ""
|
||||
echo "Installing Bun..."
|
||||
echo " (Downloading and installing Bun runtime — this may take a few minutes)"
|
||||
BUN_BIN="$HOME/.bun/bin/bun"
|
||||
if [ -x "$BUN_BIN" ]; then
|
||||
echo -e "${GREEN}[OK]${NC} Bun already installed"
|
||||
else
|
||||
# Install bun via the official installer
|
||||
# Bun is needed to download the opencode package
|
||||
if curl -fsSL https://bun.sh/install | bash 2>/dev/null; then
|
||||
echo -e "${GREEN}[OK]${NC} Bun installed"
|
||||
else
|
||||
fail_warn "Failed to install Bun — cannot install OpenCode"
|
||||
fi
|
||||
BUN_BIN="$HOME/.bun/bin/bun"
|
||||
fi
|
||||
|
||||
# Bun itself needs grun to run (it's a glibc binary)
|
||||
# Create a temporary wrapper for bun
|
||||
BUN_WRAPPER=$(mktemp "$PREFIX/tmp/bun-wrapper.XXXXXX")
|
||||
cat > "$BUN_WRAPPER" << WRAPPER
|
||||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
unset LD_PRELOAD
|
||||
exec "$GLIBC_LDSO" "$BUN_BIN" "\$@"
|
||||
WRAPPER
|
||||
chmod +x "$BUN_WRAPPER"
|
||||
|
||||
# Verify bun works
|
||||
BUN_VER=$("$BUN_WRAPPER" --version 2>/dev/null) || {
|
||||
rm -f "$BUN_WRAPPER"
|
||||
fail_warn "Bun verification failed"
|
||||
}
|
||||
echo -e "${GREEN}[OK]${NC} Bun $BUN_VER verified"
|
||||
|
||||
# ── Step 3: Install OpenCode ────────────────
|
||||
|
||||
echo ""
|
||||
echo "Installing OpenCode..."
|
||||
echo " (Downloading package — this may take a few minutes)"
|
||||
# Use bun to install opencode-ai package
|
||||
# Note: bun may exit non-zero due to optional platform packages (windows, darwin)
|
||||
# failing to install, but the linux-arm64 binary is still installed successfully.
|
||||
"$BUN_WRAPPER" install -g opencode-ai 2>&1 || true
|
||||
echo -e "${GREEN}[OK]${NC} opencode-ai package install attempted"
|
||||
|
||||
# Remove bun's global shim — it tries to spawnSync the native binary directly,
|
||||
# which fails on Termux (missing /lib/ld-linux-aarch64.so.1).
|
||||
# Our proot wrapper at $PREFIX/bin/opencode will be used instead.
|
||||
rm -f "$HOME/.bun/bin/opencode"
|
||||
|
||||
# Find the OpenCode binary
|
||||
OPENCODE_BIN=""
|
||||
for pattern in \
|
||||
"$HOME/.bun/install/cache/opencode-linux-arm64@*/bin/opencode" \
|
||||
"$HOME/.bun/install/global/node_modules/opencode-linux-arm64/bin/opencode"; do
|
||||
# shellcheck disable=SC2012,SC2086
|
||||
FOUND=$(ls $pattern 2>/dev/null | sort -V | tail -1 || true)
|
||||
if [ -n "$FOUND" ] && [ -f "$FOUND" ]; then
|
||||
OPENCODE_BIN="$FOUND"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$OPENCODE_BIN" ]; then
|
||||
rm -f "$BUN_WRAPPER"
|
||||
fail_warn "OpenCode binary not found after installation"
|
||||
fi
|
||||
echo -e "${GREEN}[OK]${NC} OpenCode binary found: $OPENCODE_BIN"
|
||||
|
||||
# Create ld.so concatenation
|
||||
mkdir -p "$OPENCLAW_DIR/bin"
|
||||
LDSO_OPENCODE="$OPENCLAW_DIR/bin/ld.so.opencode"
|
||||
create_ldso_concat "$OPENCODE_BIN" "$LDSO_OPENCODE" "OpenCode" || {
|
||||
rm -f "$BUN_WRAPPER"
|
||||
fail_warn "Failed to create OpenCode ld.so concatenation"
|
||||
}
|
||||
|
||||
# Create wrapper script
|
||||
create_proot_wrapper "$PREFIX/bin/opencode" "$LDSO_OPENCODE" "$OPENCODE_BIN" "OpenCode"
|
||||
|
||||
# Verify
|
||||
echo ""
|
||||
echo "Verifying OpenCode..."
|
||||
OC_VER=$("$PREFIX/bin/opencode" --version 2>/dev/null) || true
|
||||
if [ -n "$OC_VER" ]; then
|
||||
echo -e "${GREEN}[OK]${NC} OpenCode v$OC_VER verified"
|
||||
else
|
||||
echo -e "${YELLOW}[WARN]${NC} OpenCode --version check failed (may work in interactive mode)"
|
||||
fi
|
||||
|
||||
|
||||
# ── Step 4: Create OpenCode config ───────────
|
||||
|
||||
echo ""
|
||||
echo "Setting up OpenCode configuration..."
|
||||
|
||||
OPENCODE_CONFIG_DIR="$HOME/.config/opencode"
|
||||
OPENCODE_CONFIG="$OPENCODE_CONFIG_DIR/opencode.json"
|
||||
mkdir -p "$OPENCODE_CONFIG_DIR"
|
||||
|
||||
if [ ! -f "$OPENCODE_CONFIG" ]; then
|
||||
cat > "$OPENCODE_CONFIG" << 'CONFIG'
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json"
|
||||
}
|
||||
CONFIG
|
||||
echo -e "${GREEN}[OK]${NC} OpenCode config created"
|
||||
else
|
||||
echo -e "${GREEN}[OK]${NC} OpenCode config already exists"
|
||||
fi
|
||||
|
||||
# ── Cleanup ──────────────────────────────────
|
||||
|
||||
rm -f "$BUN_WRAPPER"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}OpenCode installation complete.${NC}"
|
||||
if [ -n "${OC_VER:-}" ]; then
|
||||
echo " OpenCode: v$OC_VER"
|
||||
fi
|
||||
echo " Run: opencode"
|
||||
Reference in New Issue
Block a user