chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:16:30 +08:00
commit a06359fc30
216 changed files with 32971 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
# Android에서 프로세스 라이브 상태 유지
OpenClaw는 서버로 동작하므로 Android의 전원 관리 및 프로세스 종료 기능이 안정적인 운영을 방해할 수 있습니다. 이 가이드에서는 프로세스를 안정적으로 유지하기 위한 모든 설정을 다룹니다.
## 개발자 옵션 활성화
1. **설정** > **휴대전화 정보** (또는 **디바이스 정보**)
2. **빌드 번호**를 7번 연속 탭
3. "개발자 모드가 활성화되었습니다" 메시지 확인
4. 잠금화면 비밀번호가 설정되어 있으면 입력
> 일부 기기에서는 **설정** > **휴대전화 정보** > **소프트웨어 정보** 안에 빌드 번호가 있습니다.
## 충전 중 화면 켜짐 유지 (Stay Awake)
1. **설정** > **개발자 옵션** (위에서 활성화한 메뉴)
2. **화면 켜짐 유지** (Stay awake) 옵션을 **ON**
3. 이제 USB 또는 무선 충전 중에는 화면이 자동으로 꺿지지 않습니다
> 충전기를 분리하면 일반 화면 꺿짐 설정이 적용됩니다. 서버를 장시간 운영할 때는 충전기를 연결해두세요.
## 충전 제한 설정 (필수)
폰을 24시간 충전 상태로 두면 배터리가 펽창할 수 있습니다. 최대 충전량을 80%로 제한하면 배터리 수명과 안전성이 크게 향상됩니다.
- **삼성**: **설정** > **배터리** > **배터리 보호****최대 80%** 선택
- **Google Pixel**: **설정** > **배터리** > **배터리 보호** → ON
> 제조사마다 메뉴 이름이 다를 수 있습니다. "배터리 보호" 또는 "충전 제한"으로 검색하세요. 해당 기능이 없는 기기에서는 충전기를 수동으로 관리하거나 스마트 플러그를 활용할 수 있습니다.
## 배터리 최적화에서 Termux 제외
1. Android **설정** > **배터리** (또는 **배터리 및 기기 관리**)
2. **배터리 최적화** (또는 **앱 절전**) 메뉴 진입
3. 앱 목록에서 **Termux** 를 찾아서 **최적화하지 않음** (또는 **제한 없음**) 선택
> 메뉴 경로는 제조사(삼성, LG 등)와 Android 버전에 따라 다를 수 있습니다. "배터리 최적화 제외" 또는 "앱 절전 해제"로 검색하면 해당 기기의 정확한 경로를 찾을 수 있습니다.
## Phantom Process Killer 비활성화 (Android 12+)
Android 12 이상에는 **Phantom Process Killer**라는 기능이 포함되어 있어, 백그라운드 프로세스를 자동으로 종료합니다. 이로 인해 Termux에서 실행 중인 `openclaw gateway`, `sshd`, `ttyd` 등이 예고 없이 종료될 수 있습니다.
## 증상
Termux에서 다음과 같은 메시지가 보이면 Android가 프로세스를 강제 종료한 것입니다:
```
[Process completed (signal 9) - press Enter]
```
<img src="images/signal9/01-signal9-killed.png" width="300" alt="Process completed signal 9">
Signal 9 (SIGKILL)는 어떤 프로세스도 가로채거나 차단할 수 없습니다 — Android가 OS 수준에서 종료한 것입니다.
## 요구사항
- **Android 12 이상** (Android 11 이하는 해당 없음)
- **Termux**에 `android-tools` 설치 (OpenClaw on Android에 포함)
## 1단계: Wake Lock 활성화
알림 바를 내려서 Termux 알림을 찾으세요. **Acquire wakelock**을 탭하면 Android가 Termux를 중단시키는 것을 방지할 수 있습니다.
<p>
<img src="images/signal9/02-termux-acquire-wakelock.png" width="300" alt="Acquire wakelock 탭">
<img src="images/signal9/03-termux-wakelock-held.png" width="300" alt="Wake lock held">
</p>
활성화되면 알림에 **"wake lock held"**가 표시되고 버튼이 **Release wakelock**으로 바뀝니다.
> Wake lock만으로는 Phantom Process Killer를 완전히 막을 수 없습니다. 아래 단계를 계속 진행하세요.
## 2단계: 무선 디버깅 활성화
1. **설정** > **개발자 옵션**으로 이동
2. **무선 디버깅** (Wireless debugging)을 찾아서 활성화
3. 확인 다이얼로그가 나타나면 — **"이 네트워크에서 항상 허용"**을 체크하고 **허용**
<img src="images/signal9/04-wireless-debugging-allow.png" width="300" alt="무선 디버깅 허용">
## 3단계: ADB 설치 (아직 설치하지 않은 경우)
Termux에서 `android-tools`를 설치합니다:
```bash
pkg install -y android-tools
```
> OpenClaw on Android를 설치했다면 `android-tools`가 이미 포함되어 있습니다.
## 4단계: ADB 페어링
1. **무선 디버깅** 설정에서 **페어링 코드로 기기 페어링** (Pair device with pairing code) 탭
2. **Wi-Fi 페어링 코드**와 **IP 주소 및 포트**가 표시된 다이얼로그가 나타남
<img src="images/signal9/05-pairing-code-dialog.png" width="300" alt="페어링 코드 다이얼로그">
3. Termux에서 화면에 표시된 포트와 코드를 사용하여 페어링 명령을 실행합니다:
```bash
adb pair localhost:<페어링_포트> <페어링_코드>
```
예시:
```bash
adb pair localhost:39555 269556
```
<img src="images/signal9/06-adb-pair-success.png" width="600" alt="adb pair 성공">
`Successfully paired`가 표시되면 성공입니다.
## 5단계: ADB 연결
페어링 후 **무선 디버깅** 메인 화면으로 돌아가세요. 상단에 표시된 **IP 주소 및 포트**를 확인합니다 — 이 포트는 페어링 포트와 다릅니다.
<img src="images/signal9/07-wireless-debugging-paired.png" width="300" alt="무선 디버깅 페어링 완료">
Termux에서 메인 화면에 표시된 포트로 연결합니다:
```bash
adb connect localhost:<연결_포트>
```
예시:
```bash
adb connect localhost:35541
```
`connected to localhost:35541`이 표시되면 성공입니다.
> 페어링 포트와 연결 포트는 다릅니다. `adb connect`에는 무선 디버깅 메인 화면에 표시된 포트를 사용하세요.
## 6단계: Phantom Process Killer 비활성화
다음 명령을 실행하여 Phantom Process Killer를 비활성화합니다:
```bash
adb shell "settings put global settings_enable_monitor_phantom_procs false"
```
설정이 적용되었는지 확인합니다:
```bash
adb shell "settings get global settings_enable_monitor_phantom_procs"
```
출력이 `false`이면 Phantom Process Killer가 성공적으로 비활성화된 것입니다.
<img src="images/signal9/08-adb-disable-ppk-done.png" width="600" alt="Phantom Process Killer 비활성화 완료">
## 참고 사항
- 이 설정은 **재부팅해도 유지**됩니다 — 한 번만 하면 됩니다
- 이 과정을 완료한 후 무선 디버깅을 켜둘 필요는 없습니다. 꺼도 됩니다
- 일반 앱 동작에는 영향을 주지 않습니다 — Termux의 백그라운드 프로세스가 종료되는 것만 방지합니다
- 폰을 초기화하면 이 과정을 다시 수행해야 합니다
## 추가 참고
일부 제조사(삼성, 샤오미, 화웨이 등)는 자체적으로 공격적인 배터리 최적화를 적용하여 백그라운드 앱을 종료시킬 수 있습니다. Phantom Process Killer를 비활성화한 후에도 프로세스가 종료되는 경우, [dontkillmyapp.com](https://dontkillmyapp.com)에서 기기별 가이드를 확인하세요.
+163
View File
@@ -0,0 +1,163 @@
# Keeping Processes Alive on Android
OpenClaw runs as a server, so Android's power management and process killing can interfere with stable operation. This guide covers all the settings needed to keep your processes running reliably.
## Enable Developer Options
1. Go to **Settings** > **About phone** (or **Device information**)
2. Tap **Build number** 7 times
3. You'll see "Developer mode has been enabled"
4. Enter your lock screen password if prompted
> On some devices, Build number is under **Settings** > **About phone** > **Software information**.
## Stay Awake While Charging
1. Go to **Settings** > **Developer options** (the menu you just enabled)
2. Turn on **Stay awake**
3. The screen will now stay on whenever the device is charging (USB or wireless)
> The screen will still turn off normally when unplugged. Keep the charger connected when running the server for extended periods.
## Set Charge Limit (Required)
Keeping a phone plugged in 24/7 at 100% can cause battery swelling. Limiting the maximum charge to 80% greatly improves battery lifespan and safety.
- **Samsung**: **Settings** > **Battery** > **Battery Protection** → Select **Maximum 80%**
- **Google Pixel**: **Settings** > **Battery** > **Battery Protection** → ON
> Menu names vary by manufacturer. Search for "battery protection" or "charge limit" in your settings. If your device doesn't have this feature, consider managing the charger manually or using a smart plug.
## Disable Battery Optimization for Termux
1. Go to Android **Settings** > **Battery** (or **Battery and device care**)
2. Open **Battery optimization** (or **App power management**)
3. Find **Termux** and set it to **Not optimized** (or **Unrestricted**)
> The exact menu path varies by manufacturer (Samsung, LG, etc.) and Android version. Search your settings for "battery optimization" to find it.
## Disable Phantom Process Killer (Android 12+)
Android 12 and above includes a feature called **Phantom Process Killer** that automatically terminates background processes. This can cause Termux processes like `openclaw gateway`, `sshd`, and `ttyd` to be killed without warning.
## Symptoms
If you see this message in Termux, Android has forcibly killed the process:
```
[Process completed (signal 9) - press Enter]
```
<img src="images/signal9/01-signal9-killed.png" width="300" alt="Process completed signal 9">
Signal 9 (SIGKILL) cannot be caught or blocked by any process — Android terminated it at the OS level.
## Requirements
- **Android 12 or higher** (Android 11 and below are not affected)
- **Termux** with `android-tools` installed (included in OpenClaw on Android)
## Step 1: Acquire Wake Lock
Pull down the notification bar and find the Termux notification. Tap **Acquire wakelock** to prevent Android from suspending Termux.
<p>
<img src="images/signal9/02-termux-acquire-wakelock.png" width="300" alt="Tap Acquire wakelock">
<img src="images/signal9/03-termux-wakelock-held.png" width="300" alt="Wake lock held">
</p>
Once activated, the notification will show **"wake lock held"** and the button changes to **Release wakelock**.
> Wake lock alone is not enough to prevent Phantom Process Killer. Continue with the steps below.
## Step 2: Enable Wireless Debugging
1. Go to **Settings** > **Developer options**
2. Find and enable **Wireless debugging**
3. A confirmation dialog will appear — check **"Always allow on this network"** and tap **Allow**
<img src="images/signal9/04-wireless-debugging-allow.png" width="300" alt="Allow wireless debugging">
## Step 3: Install ADB (if not already installed)
In Termux, install `android-tools`:
```bash
pkg install -y android-tools
```
> If you installed OpenClaw on Android, `android-tools` is already included.
## Step 4: Pair with ADB
1. In **Wireless debugging** settings, tap **Pair device with pairing code**
2. A dialog will show the **Wi-Fi pairing code** and **IP address & Port**
<img src="images/signal9/05-pairing-code-dialog.png" width="300" alt="Pairing code dialog">
3. In Termux, run the pairing command using the port and code shown on screen:
```bash
adb pair localhost:<PAIRING_PORT> <PAIRING_CODE>
```
Example:
```bash
adb pair localhost:39555 269556
```
<img src="images/signal9/06-adb-pair-success.png" width="600" alt="adb pair success">
You should see `Successfully paired`.
## Step 5: Connect with ADB
After pairing, go back to the **Wireless debugging** main screen. Note the **IP address & Port** shown at the top — this is different from the pairing port.
<img src="images/signal9/07-wireless-debugging-paired.png" width="300" alt="Wireless debugging paired">
In Termux, connect using the port shown on the main screen:
```bash
adb connect localhost:<CONNECTION_PORT>
```
Example:
```bash
adb connect localhost:35541
```
You should see `connected to localhost:35541`.
> The pairing port and connection port are different. Use the port shown on the Wireless debugging main screen for `adb connect`.
## Step 6: Disable Phantom Process Killer
Now run the following command to disable Phantom Process Killer:
```bash
adb shell "settings put global settings_enable_monitor_phantom_procs false"
```
Verify the setting:
```bash
adb shell "settings get global settings_enable_monitor_phantom_procs"
```
If the output is `false`, Phantom Process Killer has been successfully disabled.
<img src="images/signal9/08-adb-disable-ppk-done.png" width="600" alt="Phantom Process Killer disabled">
## Notes
- This setting **persists across reboots** — you only need to do this once
- You do **not** need to keep Wireless debugging enabled after completing these steps. You can turn it off
- This does not affect normal app behavior — it only prevents Android from killing background processes in Termux
- If you factory reset your phone, you will need to repeat this process
## Further Reading
Some manufacturers (Samsung, Xiaomi, Huawei, etc.) apply additional aggressive battery optimization that can kill background apps. If you still experience process termination after disabling Phantom Process Killer, check [dontkillmyapp.com](https://dontkillmyapp.com) for device-specific guides.
+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 108">
<circle cx="54" cy="54" r="54" fill="#111827"/>
<g transform="rotate(-25 54 54)">
<path fill="#FFF" d="M31,22 C28,44,28,64,31,86 C34,64,34,44,31,22Z"/>
<path fill="#FFF" d="M43,16 C40,40,40,64,43,92 C46,64,46,40,43,16Z"/>
<path fill="#FFF" d="M55,16 C52,40,52,64,55,92 C58,64,58,40,55,16Z"/>
<path fill="#FFF" d="M67,22 C64,44,64,64,67,86 C70,64,70,44,67,22Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 120 120"><defs><linearGradient id="a" x1="0%" x2="100%" y1="0%" y2="100%"><stop offset="0%" stop-color="#ff4d4d"/><stop offset="100%" stop-color="#991b1b"/></linearGradient></defs><path fill="url(#a)" d="M60 10c-30 0-45 25-45 45s15 40 30 45v10h10v-10s5 2 10 0v10h10v-10c15-5 30-25 30-45S90 10 60 10"/><path fill="url(#a)" d="M20 45C5 40 0 50 5 60s15 5 20-5c3-7 0-10-5-10M100 45c15-5 20 5 15 15s-15 5-20-5c-3-7 0-10 5-10"/><path stroke="#ff4d4d" stroke-linecap="round" stroke-width="3" d="M45 15Q35 5 30 8M75 15Q85 5 90 8"/><circle cx="45" cy="35" r="6" fill="#050810"/><circle cx="75" cy="35" r="6" fill="#050810"/><circle cx="46" cy="34" r="2.5" fill="#00e5cc"/><circle cx="76" cy="34" r="2.5" fill="#00e5cc"/></svg>

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

@@ -0,0 +1,258 @@
# Playwright Install Option Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add Playwright as an optional tool in `oa --install`, with automatic Chromium dependency resolution, environment variable setup, and usage guide.
**Architecture:** New `scripts/install-playwright.sh` follows the existing delegated-script pattern (like `install-chromium.sh`). `install-tools.sh` gets a new menu entry right after Chromium. The install script checks Chromium dependency, installs `playwright-core` via npm, sets Playwright environment variables in `.bashrc`, and prints a usage guide.
**Tech Stack:** Bash, npm (playwright-core), Termux environment
---
### Task 1: Create `scripts/install-playwright.sh`
**Files:**
- Create: `scripts/install-playwright.sh`
- [ ] **Step 1: Create the install script**
```bash
#!/usr/bin/env bash
# install-playwright.sh - Install Playwright for browser automation
# Usage: bash install-playwright.sh [install|update]
#
# What it does:
# 1. Ensure Chromium is installed (dependency)
# 2. Install playwright-core via npm global
# 3. Set Playwright environment variables in .bashrc
# 4. Print usage guide
#
# This script is WARN-level: failure does not abort the parent installer.
set -euo pipefail
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'
MODE="${1:-install}"
# ── Helper ────────────────────────────────────
fail_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
exit 0
}
# ── Detect Chromium binary path ───────────────
detect_chromium_bin() {
for bin in "$PREFIX/bin/chromium-browser" "$PREFIX/bin/chromium"; do
if [ -x "$bin" ]; then
echo "$bin"
return 0
fi
done
return 1
}
# ── Pre-checks ────────────────────────────────
if [ -z "${PREFIX:-}" ]; then
fail_warn "Not running in Termux (\$PREFIX not set)"
fi
if ! command -v npm &>/dev/null; then
fail_warn "npm not found — Node.js is required for Playwright"
fi
# ── Step 1: Ensure Chromium is installed ──────
if ! CHROMIUM_BIN=$(detect_chromium_bin); then
echo "Chromium is required for Playwright. Installing..."
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ -f "$SCRIPT_DIR/install-chromium.sh" ]; then
if ! bash "$SCRIPT_DIR/install-chromium.sh" install; then
fail_warn "Chromium installation failed — cannot proceed with Playwright"
fi
else
fail_warn "install-chromium.sh not found — install Chromium first"
fi
if ! CHROMIUM_BIN=$(detect_chromium_bin); then
fail_warn "Chromium binary not found after installation"
fi
fi
echo -e "${GREEN}[OK]${NC} Chromium found: $CHROMIUM_BIN"
# ── Step 2: Install playwright-core ───────────
if [ "$MODE" = "install" ]; then
# Check if already installed
if npm list -g playwright-core &>/dev/null; then
echo -e "${GREEN}[SKIP]${NC} playwright-core already installed"
else
echo "Installing playwright-core..."
if ! npm install -g playwright-core; then
fail_warn "Failed to install playwright-core"
fi
echo -e "${GREEN}[OK]${NC} playwright-core installed"
fi
elif [ "$MODE" = "update" ]; then
echo "Updating playwright-core..."
if ! npm install -g playwright-core@latest; then
fail_warn "Failed to update playwright-core"
fi
echo -e "${GREEN}[OK]${NC} playwright-core updated"
fi
# ── Step 3: Set environment variables ─────────
BASHRC="$HOME/.bashrc"
PW_MARKER_START="# >>> Playwright >>>"
PW_MARKER_END="# <<< Playwright <<<"
PW_BLOCK="${PW_MARKER_START}
export PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=\"$CHROMIUM_BIN\"
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
${PW_MARKER_END}"
touch "$BASHRC"
if grep -qF "$PW_MARKER_START" "$BASHRC"; then
sed -i "/${PW_MARKER_START//\//\\/}/,/${PW_MARKER_END//\//\\/}/d" "$BASHRC"
fi
echo "" >> "$BASHRC"
echo "$PW_BLOCK" >> "$BASHRC"
echo -e "${GREEN}[OK]${NC} Environment variables set in .bashrc"
echo " PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=$CHROMIUM_BIN"
echo " PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1"
# ── Step 4: Usage guide ──────────────────────
echo ""
echo -e "${BOLD} Playwright is ready!${NC}"
echo ""
echo " To use in your project:"
echo ""
echo " npm install playwright-core # add to your project"
echo ""
echo " Example code:"
echo ""
echo " const { chromium } = require('playwright-core');"
echo ""
echo " const browser = await chromium.launch();"
echo " const page = await browser.newPage();"
echo " await page.goto('https://example.com');"
echo " await page.screenshot({ path: 'screenshot.png' });"
echo " await browser.close();"
echo ""
echo -e " ${YELLOW}[NOTE]${NC} Environment variables are set. No need to specify"
echo " executablePath or --no-sandbox manually."
echo ""
echo " To apply environment variables in current session:"
echo " source ~/.bashrc"
echo ""
```
Write this to `scripts/install-playwright.sh`.
- [ ] **Step 2: Make the script executable**
Run: `chmod +x scripts/install-playwright.sh`
---
### Task 2: Add Playwright to `install-tools.sh`
**Files:**
- Modify: `install-tools.sh:79` (tool detection)
- Modify: `install-tools.sh:118` (flag declaration)
- Modify: `install-tools.sh:124` (user prompt — after Chromium)
- Modify: `install-tools.sh:135-137` (anything_selected check)
- Modify: `install-tools.sh:152` (NEEDS_TARBALL condition)
- Modify: `install-tools.sh:202-208` (installation phase — after Chromium)
- [ ] **Step 1: Add Playwright to tool detection**
After line 79 (`check_tool "Chromium" "chromium-browser"`), detect playwright-core via npm:
```bash
# Playwright detection — check via npm since it's a global npm package
if npm list -g playwright-core &>/dev/null 2>&1; then
TOOL_STATUS["Playwright"]="installed"
echo -e " ${GREEN}[INSTALLED]${NC} Playwright"
else
TOOL_STATUS["Playwright"]="not_installed"
echo -e " ${YELLOW}[NOT INSTALLED]${NC} Playwright"
fi
```
- [ ] **Step 2: Add flag declaration**
After `INSTALL_CHROMIUM=false` (line 118), add:
```bash
INSTALL_PLAYWRIGHT=false
```
- [ ] **Step 3: Add user prompt after Chromium prompt**
After the Chromium prompt line (line 124), add:
```bash
if [ "${TOOL_STATUS[Playwright]}" = "not_installed" ] && ask_yn " Install Playwright (browser automation library, requires Chromium)?"; then INSTALL_PLAYWRIGHT=true; fi
```
- [ ] **Step 4: Add to anything_selected check**
Add `INSTALL_PLAYWRIGHT` to the for loop on line 135-137:
```bash
for var in INSTALL_TMUX INSTALL_TTYD INSTALL_DUFS INSTALL_ANDROID_TOOLS \
INSTALL_CHROMIUM INSTALL_PLAYWRIGHT INSTALL_CODE_SERVER INSTALL_OPENCODE INSTALL_CLAUDE_CODE \
INSTALL_GEMINI_CLI INSTALL_CODEX_CLI; do
```
- [ ] **Step 5: Add NEEDS_TARBALL condition**
On line 152, add `INSTALL_PLAYWRIGHT` to the condition:
```bash
if [ "$INSTALL_CODE_SERVER" = true ] || [ "$INSTALL_OPENCODE" = true ] || [ "$INSTALL_CHROMIUM" = true ] || [ "$INSTALL_PLAYWRIGHT" = true ]; then
```
- [ ] **Step 6: Add installation block after Chromium**
After the Chromium installation block (after line 208), add:
```bash
if [ "$INSTALL_PLAYWRIGHT" = true ]; then
if bash "$RELEASE_TMP/scripts/install-playwright.sh" install; then
echo -e "${GREEN}[OK]${NC} Playwright installed"
else
echo -e "${YELLOW}[WARN]${NC} Playwright installation failed (non-critical)"
fi
fi
```
---
### Task 3: Verify
- [ ] **Step 1: Syntax check install-playwright.sh**
Run: `bash -n scripts/install-playwright.sh`
Expected: no output (no syntax errors)
- [ ] **Step 2: Syntax check install-tools.sh**
Run: `bash -n install-tools.sh`
Expected: no output (no syntax errors)
- [ ] **Step 3: Verify Playwright detection logic handles missing npm gracefully**
Run: `grep -n "npm list -g playwright-core" install-tools.sh`
Expected: the detection line appears in the tool detection section
+77
View File
@@ -0,0 +1,77 @@
# Termux SSH 접속 가이드
폰의 Termux에 컴퓨터에서 SSH로 접속하면, 컴퓨터 키보드로 모든 명령어를 입력할 수 있습니다.
## 준비물
- 폰과 컴퓨터가 **같은 Wi-Fi 네트워크**에 연결되어 있어야 합니다
## 1단계: openssh 설치
폰에서 Termux 앱을 열고 입력합니다:
```bash
pkg install -y openssh
```
설치가 완료될 때까지 기다리세요 (1~2분).
## 2단계: 비밀번호 설정
```bash
passwd
```
비밀번호를 입력합니다 (예: `1234`):
```
New password: 1234 ← 입력
Retype new password: 1234 ← 같은 비밀번호 다시 입력
```
> 비밀번호 입력 시 화면에 아무것도 표시되지 않는 것이 정상입니다. 그냥 입력하고 Enter를 누르면 됩니다.
## 3단계: SSH 서버 시작
> **중요**: `sshd`는 SSH가 아닌, 폰의 Termux 앱에서 직접 실행하세요.
```bash
sshd
```
아무 메시지 없이 프롬프트(`$`)가 다시 나오면 정상입니다.
<img src="images/termux_tab_2.png" width="300" alt="sshd 실행 화면">
## 4단계: IP 주소 확인
```bash
ifconfig
```
`wlan0` 항목을 찾으세요:
```
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.45.139 netmask 255.255.255.0
```
`inet` 뒤의 숫자가 폰의 IP 주소입니다 (위 예시에서는 `192.168.45.139`).
## 5단계: 컴퓨터에서 SSH 접속
컴퓨터 터미널(맥: 터미널, 윈도우: PowerShell 또는 명령 프롬프트)을 열고 입력합니다. IP 주소는 4단계에서 확인한 값으로 변경하세요:
```bash
ssh -p 8022 192.168.45.139
```
- `Are you sure you want to continue connecting?``yes` 입력
- `Password:` → 2단계에서 설정한 비밀번호 입력 (예: `1234`)
접속 성공하면 Termux의 `$` 프롬프트가 나타납니다. 이제부터 컴퓨터 키보드로 모든 Termux 명령어를 입력할 수 있습니다.
## 참고 사항
- Termux의 SSH 포트는 **8022**입니다 (일반 리눅스의 22가 아님)
- Termux 앱을 종료하면 SSH 서버도 꺼집니다. 다시 접속하려면 폰에서 Termux를 열고 `sshd`를 실행하세요
+77
View File
@@ -0,0 +1,77 @@
# Termux SSH Setup Guide
By connecting to Termux via SSH from your computer, you can type all commands using your computer keyboard.
## Prerequisites
- Both phone and computer must be on the **same Wi-Fi network**
## Step 1: Install openssh
Open the Termux app on your phone and type:
```bash
pkg install -y openssh
```
Wait for the installation to complete (1-2 minutes).
## Step 2: Set Password
```bash
passwd
```
Enter a password (e.g., `1234`):
```
New password: 1234 ← type
Retype new password: 1234 ← type the same password again
```
> It's normal that nothing shows on screen while typing the password. Just type it and press Enter.
## Step 3: Start SSH Server
> **Important**: Run `sshd` directly in the Termux app on your phone, not via SSH.
```bash
sshd
```
If the prompt (`$`) returns with no error message, it's working.
<img src="images/termux_tab_2.png" width="300" alt="sshd running in Termux">
## Step 4: Find the Phone's IP Address
```bash
ifconfig
```
Look for the `wlan0` section:
```
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.45.139 netmask 255.255.255.0
```
The number after `inet` is your phone's IP address (in this example, `192.168.45.139`).
## Step 5: Connect via SSH from Computer
Open a terminal on your computer (Mac: Terminal, Windows: PowerShell or Command Prompt) and type. Replace the IP address with the one you found in Step 4:
```bash
ssh -p 8022 192.168.45.139
```
- `Are you sure you want to continue connecting?` → type `yes`
- `Password:` → enter the password you set in Step 2 (e.g., `1234`)
Once connected, you'll see the Termux `$` prompt. From now on, you can type all Termux commands using your computer keyboard.
## Notes
- Termux uses SSH port **8022** (not the standard Linux port 22)
- If you close the Termux app, the SSH server stops. To reconnect, open Termux on the phone and run `sshd`
+304
View File
@@ -0,0 +1,304 @@
# 트러블슈팅
Termux에서 OpenClaw 사용 중 발생할 수 있는 문제와 해결 방법을 정리합니다.
## 게이트웨이가 시작되지 않음: "gateway already running" 또는 "Port is already in use"
```
Gateway failed to start: gateway already running (pid XXXXX); lock timeout after 5000ms
Port 18789 is already in use.
```
### 원인
이전 게이트웨이 프로세스가 비정상 종료되면서 잠금 파일이 남아있거나, 프로세스가 좀비 상태로 남아있는 경우 발생합니다. 주로 다음 상황에서 일어납니다:
- SSH 연결이 끊어지면서 게이트웨이 프로세스가 고아(orphan) 상태로 남음
- `Ctrl+Z`(일시정지)로 중단한 경우 프로세스가 종료되지 않고 백그라운드에 남음
- Termux가 Android에 의해 강제 종료된 경우
> **참고**: 게이트웨이를 종료할 때는 반드시 `Ctrl+C`를 사용하세요. `Ctrl+Z`는 프로세스를 일시정지시킬 뿐 종료하지 않습니다.
### 해결 방법
#### 1단계: 남아있는 프로세스 확인 및 종료
```bash
ps aux | grep -E "node|openclaw" | grep -v grep
```
프로세스가 보이면 PID를 확인하고 종료:
```bash
kill -9 <PID>
```
#### 2단계: 잠금 파일 삭제
```bash
rm -rf $PREFIX/tmp/openclaw-*
```
#### 3단계: 게이트웨이 재시작
```bash
openclaw gateway
```
### 그래도 안 되면
위 과정으로도 해결되지 않으면 Termux 앱을 완전히 종료했다가 다시 열고 `openclaw gateway`를 실행하세요. 폰을 재시작하면 확실하게 모든 상태가 초기화됩니다.
## 게이트웨이 연결 끊김: "gateway not connected"
```
send failed: Error: gateway not connected
disconnected | error
```
### 원인
게이트웨이 프로세스가 종료되었거나 SSH 세션이 끊어진 경우 발생합니다.
### 해결 방법
게이트웨이를 실행했던 SSH 세션을 확인하세요. 세션이 끊어졌다면 다시 SSH 접속 후 게이트웨이를 시작합니다:
```bash
openclaw gateway
```
"gateway already running" 에러가 나오면 위의 [게이트웨이가 시작되지 않음](#게이트웨이가-시작되지-않음-gateway-already-running-또는-port-is-already-in-use) 섹션을 참고하세요.
## SSH 접속 실패: "Connection refused"
```
ssh: connect to host 192.168.45.139 port 8022: Connection refused
```
### 원인
Termux의 SSH 서버(`sshd`)가 실행되지 않은 상태입니다. Termux 앱을 종료하거나 폰을 재시작하면 sshd가 꺼집니다.
### 해결 방법
폰에서 Termux 앱을 열고 `sshd`를 실행하세요. 폰에서 직접 타이핑하거나 adb로 전송:
```bash
adb shell input text 'sshd'
```
```bash
adb shell input keyevent 66
```
IP가 변경되었을 수 있으니 확인:
```bash
adb shell input text 'ifconfig'
```
```bash
adb shell input keyevent 66
```
> 매번 수동으로 `sshd`를 실행하기 번거로우면 `~/.bashrc` 맨 아래에 `sshd 2>/dev/null`을 추가하면 Termux 시작 시 자동으로 SSH 서버가 켜집니다.
## `openclaw --version` 실패
### 원인
환경변수가 로드되지 않은 상태입니다.
### 해결 방법
```bash
source ~/.bashrc
```
또는 Termux 앱을 완전히 종료했다가 다시 여세요.
## "Cannot find module glibc-compat.js" 에러
```
Error: Cannot find module '/data/data/com.termux/files/home/.openclaw-lite/patches/glibc-compat.js'
```
> **참고**: 이 문제는 v1.0.0 이전(Bionic) 설치에서만 발생합니다. v1.0.0+(glibc)에서는 `glibc-compat.js`가 node 래퍼 스크립트에 의해 로딩되므로 `NODE_OPTIONS`를 사용하지 않습니다.
### 원인
`~/.bashrc``NODE_OPTIONS` 환경변수가 이전 설치 경로(`.openclaw-lite`)를 참조하고 있습니다. 프로젝트명이 "OpenClaw Lite"였던 이전 버전에서 업데이트한 경우 발생합니다.
### 해결 방법
업데이터를 실행하면 환경변수 블록이 갱신됩니다:
```bash
oa --update && source ~/.bashrc
```
또는 수동으로 수정:
```bash
sed -i 's/\.openclaw-lite/\.openclaw-android/g' ~/.bashrc && source ~/.bashrc
```
## 업데이트 중 "systemctl --user unavailable: spawn systemctl ENOENT" 에러
```
Gateway service check failed: Error: systemctl --user unavailable: spawn systemctl ENOENT
```
### 원인
`openclaw update` 실행 후, OpenClaw이 `systemctl`로 게이트웨이 서비스를 재시작하려고 합니다. Termux에는 systemd가 없으므로 `systemctl` 바이너리를 찾을 수 없어 `ENOENT` 에러가 발생합니다.
### 영향
**이 에러는 무해합니다.** 업데이트 자체는 이미 성공적으로 완료되었으며, 자동 서비스 재시작만 실패한 것입니다. OpenClaw은 최신 상태로 업데이트되어 있습니다.
### 해결 방법
수동으로 게이트웨이를 시작하면 됩니다:
```bash
openclaw gateway
```
업데이트 전에 게이트웨이가 실행 중이었다면 기존 프로세스를 먼저 종료해야 할 수 있습니다. 위의 [게이트웨이가 시작되지 않음](#게이트웨이가-시작되지-않음-gateway-already-running-또는-port-is-already-in-use) 섹션을 참고하세요.
## `openclaw update` 중 sharp 빌드 실패
```
npm error gyp ERR! not ok
Update Result: ERROR
Reason: global update
```
### 원인
**v1.0.0+(glibc)**: `sharp` 모듈은 프리빌트 바이너리(`@img/sharp-linux-arm64`)를 사용하며 glibc 환경에서 네이티브로 로딩됩니다. 이 에러는 드문 — 주로 프리빌트 바이너리가 누락되거나 손상된 경우입니다.
**v1.0.0 이전(Bionic)**: `openclaw update`가 npm을 서브프로세스로 실행할 때, Termux 전용 빌드 환경변수(`CXXFLAGS`, `GYP_DEFINES`)가 서브프로세스 환경에서 사용 불가하여 네이티브 모듈 컴파일이 실패합니다.
### 영향
**이 에러는 무해합니다.** OpenClaw 자체는 정상적으로 업데이트되었으며, `sharp` 모듈(이미지 처리용)만 리빌드에 실패한 것입니다. OpenClaw는 sharp 없이도 정상적으로 작동합니다.
### 해결 방법
업데이트 후 아래 스크립트로 sharp를 수동 빌드하세요:
```bash
bash ~/.openclaw-android/scripts/build-sharp.sh
```
또는 `openclaw update` 대신 `oa --update`를 사용하면 sharp를 자동으로 처리합니다:
```bash
oa --update && source ~/.bashrc
```
## `clawdhub` 실행 시 "Cannot find package 'undici'" 에러
```
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'undici' imported from /data/data/com.termux/files/usr/lib/node_modules/clawdhub/dist/http.js
```
### 원인
Node.js v24+ Termux 환경에서는 `undici` 패키지가 Node.js에 번들되지 않습니다. `clawdhub`가 HTTP 요청에 `undici`를 사용하지만 찾을 수 없어 실패합니다.
### 해결 방법
업데이터를 실행하면 `clawdhub``undici` 의존성이 자동으로 설치됩니다:
```bash
oa --update && source ~/.bashrc
```
또는 수동으로 수정:
```bash
cd $(npm root -g)/clawdhub && npm install undici
```
## "not supported on android" 에러
```
Gateway status failed: Error: Gateway service install not supported on android
```
> **참고**: 이 문제는 v1.0.0 이전(Bionic) 설치에서만 발생합니다. v1.0.0+(glibc)에서는 Node.js가 `process.platform`을 `'linux'`으로 보고하므로 이 에러가 발생하지 않습니다.
### 원인
**v1.0.0 이전(Bionic)**: `glibc-compat.js``process.platform` 오버라이드가 적용되지 않은 상태입니다. `NODE_OPTIONS`가 설정되지 않았기 때문입니다.
### 해결 방법
어떤 Node.js가 사용되고 있는지 확인:
```bash
node -e "console.log(process.platform)"
```
`android`가 출력되면 glibc node 래퍼가 사용되지 않고 있는 것입니다. 환경변수를 로드하세요:
```bash
source ~/.bashrc
```
여전히 `android`가 출력되면, 최신 버전으로 업데이트하세요 (v1.0.0+는 glibc를 사용하여 이 문제를 영구적으로 해결합니다):
```bash
oa --update && source ~/.bashrc
```
## `openclaw update` 시 node-llama-cpp 빌드 에러
```
[node-llama-cpp] Cloning ggml-org/llama.cpp (local bundle)
npm error 48%
Update Result: ERROR
```
### 원인
OpenClaw이 npm으로 업데이트할 때, `node-llama-cpp`의 postinstall 스크립트가 `llama.cpp` 소스를 clone하고 컴파일을 시도합니다. Termux의 빌드 툴체인(`cmake`, `clang`)이 Bionic으로 링크되어 있고 Node.js는 glibc로 실행되므로 — 두 환경이 네이티브 컴파일에 호환되지 않아 실패합니다.
### 영향
**이 에러는 무해합니다.** 프리빌트 `node-llama-cpp` 바이너리(`@node-llama-cpp/linux-arm64`)가 이미 설치되어 있으며 glibc 환경에서 정상 작동합니다. 실패한 소스 빌드가 프리빌트 바이너리를 덮어쓰지 않습니다.
node-llama-cpp는 선택적 로컬 임베딩에 사용됩니다. 프리빌트 바이너리가 로딩되지 않으면 OpenClaw이 원격 임베딩 프로바이더(OpenAI, Gemini 등)로 자동 fallback합니다.
### 해결 방법
조치가 필요 없습니다. 이 에러는 안전하게 무시할 수 있습니다. 프리빌트 바이너리가 정상 작동하는지 확인하려면:
```bash
node -e "require('$(npm root -g)/openclaw/node_modules/@node-llama-cpp/linux-arm64/bins/linux-arm64/llama-addon.node'); console.log('OK')"
```
## OpenCode 설치 시 EACCES 권한 에러
```
EACCES: Permission denied while installing opencode-ai
Failed to install 118 packages
```
### 원인
Bun이 패키지 설치 시 하드링크와 심링크를 생성하려고 시도합니다. Android 파일시스템이 이러한 작업을 제한하여 의존성 패키지에서 `EACCES` 에러가 발생합니다.
### 영향
**이 에러는 무해합니다.** 메인 바이너리(`opencode`)는 의존성 링크 실패에도 불구하고 정상적으로 설치됩니다. ld.so 결합과 proot 래퍼가 실행을 처리합니다.
### 해결 방법
조치가 필요 없습니다. OpenCode가 정상 작동하는지 확인:
```bash
opencode --version
```
+304
View File
@@ -0,0 +1,304 @@
# Troubleshooting
Common issues and solutions when using OpenClaw on Termux.
## Gateway won't start: "gateway already running" or "Port is already in use"
```
Gateway failed to start: gateway already running (pid XXXXX); lock timeout after 5000ms
Port 18789 is already in use.
```
### Cause
A previous gateway process was terminated abnormally, leaving behind a lock file or a zombie process. This typically happens when:
- SSH connection drops, leaving the gateway process orphaned
- `Ctrl+Z` (suspend) was used instead of `Ctrl+C` (terminate), leaving the process alive in the background
- Termux was force-killed by Android
> **Note**: Always use `Ctrl+C` to stop the gateway. `Ctrl+Z` only suspends the process — it does not terminate it.
### Solution
#### Step 1: Find and kill remaining processes
```bash
ps aux | grep -E "node|openclaw" | grep -v grep
```
If processes are listed, note the PID and kill them:
```bash
kill -9 <PID>
```
#### Step 2: Remove lock files
```bash
rm -rf $PREFIX/tmp/openclaw-*
```
#### Step 3: Restart the gateway
```bash
openclaw gateway
```
### If it still doesn't work
If the above steps don't help, fully close and reopen the Termux app, then run `openclaw gateway`. Rebooting the phone will reliably clear all state.
## Gateway disconnected: "gateway not connected"
```
send failed: Error: gateway not connected
disconnected | error
```
### Cause
The gateway process has stopped or the SSH session was disconnected.
### Solution
Check the SSH session where the gateway was running. If the session was disconnected, reconnect via SSH and start the gateway:
```bash
openclaw gateway
```
If you get a "gateway already running" error, see the [Gateway won't start](#gateway-wont-start-gateway-already-running-or-port-is-already-in-use) section above.
## SSH connection failed: "Connection refused"
```
ssh: connect to host 192.168.45.139 port 8022: Connection refused
```
### Cause
The Termux SSH server (`sshd`) is not running. Closing the Termux app or rebooting the phone stops sshd.
### Solution
Open the Termux app on the phone and run `sshd`. Either type directly on the phone or send via adb:
```bash
adb shell input text 'sshd'
```
```bash
adb shell input keyevent 66
```
The IP address may have changed, so verify:
```bash
adb shell input text 'ifconfig'
```
```bash
adb shell input keyevent 66
```
> To start sshd automatically, add `sshd 2>/dev/null` to the end of your `~/.bashrc` file so the SSH server starts whenever Termux opens.
## `openclaw --version` fails
### Cause
Environment variables are not loaded.
### Solution
```bash
source ~/.bashrc
```
Or fully close and reopen the Termux app.
## "Cannot find module glibc-compat.js" error
```
Error: Cannot find module '/data/data/com.termux/files/home/.openclaw-lite/patches/glibc-compat.js'
```
> **Note**: This issue only affects pre-1.0.0 (Bionic) installations. In v1.0.0+ (glibc), `glibc-compat.js` is loaded by the node wrapper script, not `NODE_OPTIONS`.
### Cause
The `NODE_OPTIONS` environment variable in `~/.bashrc` still references the old installation path (`.openclaw-lite`). This happens when updating from an older version where the project was named "OpenClaw Lite".
### Solution
Run the updater to refresh the environment variable block:
```bash
oa --update && source ~/.bashrc
```
Or manually fix it:
```bash
sed -i 's/\.openclaw-lite/\.openclaw-android/g' ~/.bashrc && source ~/.bashrc
```
## "systemctl --user unavailable: spawn systemctl ENOENT" during update
```
Gateway service check failed: Error: systemctl --user unavailable: spawn systemctl ENOENT
```
### Cause
After running `openclaw update`, OpenClaw tries to restart the gateway service using `systemctl`. Since Termux doesn't have systemd, the `systemctl` binary doesn't exist and the command fails with `ENOENT`.
### Impact
**This error is harmless.** The update itself has already completed successfully — only the automatic service restart failed. Your OpenClaw installation is up to date.
### Solution
Simply start the gateway manually:
```bash
openclaw gateway
```
If the gateway was already running before the update, you may need to stop the old process first. See the [Gateway won't start](#gateway-wont-start-gateway-already-running-or-port-is-already-in-use) section above.
## sharp build fails during `openclaw update`
```
npm error gyp ERR! not ok
Update Result: ERROR
Reason: global update
```
### Cause
**v1.0.0+ (glibc)**: The `sharp` module uses prebuilt binaries (`@img/sharp-linux-arm64`) that load natively under the glibc environment. This error is rare — it typically means the prebuilt binary is missing or corrupted.
**Pre-1.0.0 (Bionic)**: When `openclaw update` ran npm as a subprocess, the Termux-specific build environment variables (`CXXFLAGS`, `GYP_DEFINES`) were not available in the subprocess context, causing the native module compilation to fail.
### Impact
**This error is non-critical.** OpenClaw itself has been updated successfully — only the `sharp` module (used for image processing) failed to rebuild. OpenClaw works normally without it.
### Solution
After the update, manually rebuild `sharp` using the provided script:
```bash
bash ~/.openclaw-android/scripts/build-sharp.sh
```
Alternatively, use `oa --update` instead of `openclaw update` — it handles sharp automatically:
```bash
oa --update && source ~/.bashrc
```
## `clawdhub` fails with "Cannot find package 'undici'"
```
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'undici' imported from /data/data/com.termux/files/usr/lib/node_modules/clawdhub/dist/http.js
```
### Cause
Node.js v24+ on Termux doesn't bundle the `undici` package, which `clawdhub` depends on for HTTP requests.
### Solution
Run the updater to automatically install `clawdhub` and its `undici` dependency:
```bash
oa --update && source ~/.bashrc
```
Or fix it manually:
```bash
cd $(npm root -g)/clawdhub && npm install undici
```
## "not supported on android" error
```
Gateway status failed: Error: Gateway service install not supported on android
```
> **Note**: This issue only affects pre-1.0.0 (Bionic) installations. In v1.0.0+ (glibc), Node.js natively reports `process.platform` as `'linux'`, so this error does not occur.
### Cause
**Pre-1.0.0 (Bionic)**: The `process.platform` override in `glibc-compat.js` is not being applied because `NODE_OPTIONS` is not set.
### Solution
Check which Node.js is being used:
```bash
node -e "console.log(process.platform)"
```
If it prints `android`, the glibc node wrapper is not being used. Load the environment:
```bash
source ~/.bashrc
```
If it still prints `android`, update to the latest version (v1.0.0+ uses glibc and resolves this permanently):
```bash
oa --update && source ~/.bashrc
```
## `openclaw update` fails with node-llama-cpp build error
```
[node-llama-cpp] Cloning ggml-org/llama.cpp (local bundle)
npm error 48%
Update Result: ERROR
```
### Cause
When OpenClaw updates via npm, `node-llama-cpp`'s postinstall script attempts to clone and compile `llama.cpp` from source. This fails on Termux because the build toolchain (`cmake`, `clang`) is linked against Bionic, while Node.js runs under glibc — the two are incompatible for native compilation.
### Impact
**This error is harmless.** The prebuilt `node-llama-cpp` binaries (`@node-llama-cpp/linux-arm64`) are already installed and work correctly under the glibc environment. The failed source build does not overwrite them.
Node-llama-cpp is used for optional local embeddings. If the prebuilt binaries don't load, OpenClaw automatically falls back to remote embedding providers (OpenAI, Gemini, etc.).
### Solution
No action needed. The error can be safely ignored. To verify that the prebuilt binaries are working:
```bash
node -e "require('$(npm root -g)/openclaw/node_modules/@node-llama-cpp/linux-arm64/bins/linux-arm64/llama-addon.node'); console.log('OK')"
```
## OpenCode install shows EACCES permission errors
```
EACCES: Permission denied while installing opencode-ai
Failed to install 118 packages
```
### Cause
Bun attempts to create hardlinks and symlinks when installing packages. Android's filesystem restricts these operations, causing `EACCES` errors for dependency packages.
### Impact
**These errors are harmless.** The main binary (`opencode`) is installed correctly despite the dependency link failures. The ld.so concatenation and proot wrapper handle execution.
### Solution
No action needed. Verify that OpenCode works:
```bash
opencode --version
```