Skip to content

Commit 80efe71

Browse files
committed
Redesign landing page to match claw-code.codes, fix workflow, add clawcode-android
- Redesign docs/index.html with light theme matching claw-code.codes design - Fix sync-upstream.yml to gracefully skip when no upstream releases exist - Copy openclaw-android as clawcode-android with Claw Code branding Made-with: Cursor
1 parent 9729981 commit 80efe71

719 files changed

Lines changed: 105816 additions & 968 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/sync-upstream.yml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,20 @@ jobs:
111111
python3 << 'PYEOF'
112112
import json, re, html, urllib.request, sys
113113
from datetime import datetime
114+
from urllib.error import HTTPError
115+
116+
try:
117+
req = urllib.request.Request(
118+
"https://api.github.com/repos/instructkr/claw-code/releases/latest",
119+
headers={"User-Agent": "claw-code-android-sync"}
120+
)
121+
resp = urllib.request.urlopen(req)
122+
except HTTPError as e:
123+
if e.code == 404:
124+
print("No releases found upstream, skipping.")
125+
sys.exit(0)
126+
raise
114127
115-
resp = urllib.request.urlopen("https://api.github.com/repos/instructkr/claw-code/releases/latest")
116128
rel = json.loads(resp.read())
117129
118130
name = rel.get("name", "")
@@ -174,6 +186,11 @@ jobs:
174186
</div>
175187
<!-- RELEASE_END -->"""
176188
189+
import os
190+
if not os.path.exists("docs/index.html"):
191+
print("docs/index.html not found, skipping.")
192+
sys.exit(0)
193+
177194
with open("docs/index.html", "r") as f:
178195
content = f.read()
179196

clawcode-android/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules/
2+
dist/
3+
dist-cli/
4+
*.tsbuildinfo
5+
PROJECT_SPEC.md
6+
codex_tools_from_notices.txt
7+
package-lock.json
8+
/output/

clawcode-android/AGENTS.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# AGENTS.md
2+
3+
## Detached HEAD: Merge To Local `main` Without Creating A Branch
4+
5+
- If working in detached `HEAD`, commit there first.
6+
- Then apply that commit onto local `main` from the main worktree using fast-forward merge or cherry-pick.
7+
8+
## Merging Worktree Branch to Main With Conflicts
9+
10+
When merging a worktree branch into `main` and conflicts arise:
11+
12+
1. Run `git merge <branch> --no-commit` from the main worktree (`/Users/igor/Git-projects/codex-web-local`).
13+
2. Identify conflicted files with `git diff --name-only --diff-filter=U`.
14+
3. For each conflicted file, resolve using a Python script that replaces the conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) with the correctly merged content — keeping changes from **both** sides.
15+
4. Do **not** blindly `--ours` or `--theirs` — manually combine both sides.
16+
5. After fixing, `git add <file>` and `git commit`.
17+
6. Note: the worktree workspace (`zpw/`) is restricted — `StrReplace` tool cannot edit files in the main worktree. Use `Shell` or a Python script instead.
18+
19+
## Commit After Each Task
20+
21+
- Always create a commit after completing each discrete task or sub-task.
22+
- Do not batch multiple tasks into a single commit.
23+
- Each commit message should describe the specific change made.
24+
25+
## Completion Verification Requirement
26+
27+
- After completing a task that changes behavior or UI, always run a Playwright verification in headless mode.
28+
- Before taking any screenshot, wait a few seconds to ensure the UI has fully loaded.
29+
- Always capture a screenshot of the changed result and display that screenshot in chat when reporting completion.

clawcode-android/LICENSE

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
MIT License
2+
3+
Copyright 2026 Pavel Voronin
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10+
11+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
*.iml
2+
.gradle/
3+
local.properties
4+
.idea/
5+
build/
6+
app/build/
7+
captures/
8+
.externalNativeBuild/
9+
.cxx/
10+
11+
# Bootstrap archive (downloaded at build time, ~30MB)
12+
app/src/main/assets/bootstrap-*.zip
13+
14+
# Server bundle (built from parent project)
15+
app/src/main/assets/server-bundle/
16+
17+
# Gradle wrapper jar (downloaded automatically)
18+
gradle/wrapper/gradle-wrapper.jar
19+
/.kotlin/

clawcode-android/android/README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# AnyClaw (Android)
2+
3+
Android APK that embeds a Termux-style Linux bootstrap environment, installs OpenClaw + Codex on first run, and presents the AnyClaw UI inside a WebView.
4+
5+
## Architecture
6+
7+
```
8+
┌─────────────────────────────────────────┐
9+
│ Android APK │
10+
│ │
11+
│ ┌──────────────┐ ┌────────────────┐ │
12+
│ │ WebView │ │ Bootstrap │ │
13+
│ │ │ │ Installer │ │
14+
│ │ localhost: │ │ │ │
15+
│ │ 18923 │ │ Extracts │ │
16+
│ │ │ │ Termux env │ │
17+
│ └──────┬───────┘ └───────┬────────┘ │
18+
│ │ │ │
19+
│ ▼ ▼ │
20+
│ ┌──────────────────────────────────┐ │
21+
│ │ /data/data/com.codex.mobile/ │ │
22+
│ │ files/usr/ (Termux prefix) │ │
23+
│ │ │ │
24+
│ │ ├── bin/node │ │
25+
│ │ ├── bin/codex │ │
26+
│ │ └── lib/node_modules/ │ │
27+
│ │ └── codex-web-local/ │ │
28+
│ │ ├── dist/ (Vue) │ │
29+
│ │ └── dist-cli/ (srv) │ │
30+
│ └──────────────────────────────────┘ │
31+
└─────────────────────────────────────────┘
32+
```
33+
34+
## Prerequisites
35+
36+
- Android Studio (or just the Android SDK command-line tools)
37+
- Java 17+
38+
- curl (for downloading bootstrap)
39+
40+
## Build Instructions
41+
42+
### 1. Download the Termux bootstrap
43+
44+
```bash
45+
cd android
46+
./scripts/download-bootstrap.sh
47+
```
48+
49+
This downloads `bootstrap-aarch64.zip` (~30 MB) from Termux releases into `app/src/main/assets/`.
50+
51+
### 2. (Optional) Bundle the server
52+
53+
If you want to pre-bundle the codex-web-local server in the APK so users don't need to `npm install` it on first run:
54+
55+
```bash
56+
./scripts/build-server-bundle.sh
57+
```
58+
59+
This builds the Vue frontend + Express CLI from the parent project and copies them into `app/src/main/assets/server-bundle/`.
60+
61+
### 3. Build the APK
62+
63+
```bash
64+
./gradlew assembleDebug
65+
```
66+
67+
The APK will be at `app/build/outputs/apk/debug/app-debug.apk`.
68+
69+
For a release build:
70+
71+
```bash
72+
./gradlew assembleRelease
73+
```
74+
75+
## First Run
76+
77+
On first launch, the app will:
78+
79+
1. Extract the bootstrap environment (~30 MB compressed, ~100 MB extracted)
80+
2. Run `apt-get install nodejs-lts` (downloads ~30 MB)
81+
3. Run `npm install -g @openai/codex codex-web-local`
82+
4. Prompt for your OpenAI API key (stored encrypted on device)
83+
5. Start the server and load the WebView
84+
85+
Steps 1-3 only happen once. Subsequent launches skip straight to step 4-5.
86+
87+
## Minimum Requirements
88+
89+
- Android 7.0 (API 24) or higher
90+
- arm64-v8a device (most modern Android phones)
91+
- ~500 MB free storage for bootstrap + Node.js + Codex
92+
- Internet connection (for API calls and first-run package installs)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
plugins {
2+
id("com.android.application")
3+
id("org.jetbrains.kotlin.android")
4+
}
5+
6+
android {
7+
namespace = "com.codex.mobile"
8+
compileSdk = 35
9+
10+
defaultConfig {
11+
applicationId = "com.codex.mobile"
12+
minSdk = 24
13+
// targetSdk 28 allows executing binaries from app data directory.
14+
// Android 10+ (targetSdk 29+) enforces W^X which blocks this via SELinux.
15+
// Termux (F-Droid) uses the same approach.
16+
targetSdk = 28
17+
versionCode = 2
18+
versionName = "0.1.1"
19+
}
20+
21+
buildTypes {
22+
release {
23+
isMinifyEnabled = false
24+
proguardFiles(
25+
getDefaultProguardFile("proguard-android-optimize.txt"),
26+
"proguard-rules.pro"
27+
)
28+
}
29+
}
30+
31+
compileOptions {
32+
sourceCompatibility = JavaVersion.VERSION_17
33+
targetCompatibility = JavaVersion.VERSION_17
34+
}
35+
36+
kotlinOptions {
37+
jvmTarget = "17"
38+
}
39+
40+
packaging {
41+
resources {
42+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
43+
}
44+
}
45+
46+
// Don't compress bootstrap zip or server bundle in assets
47+
androidResources {
48+
noCompress += listOf("zip", "tar.gz")
49+
}
50+
}
51+
52+
dependencies {
53+
implementation("androidx.core:core-ktx:1.15.0")
54+
implementation("androidx.appcompat:appcompat:1.7.0")
55+
implementation("androidx.webkit:webkit:1.12.1")
56+
implementation("androidx.security:security-crypto:1.1.0-alpha06")
57+
implementation("com.google.android.material:material:1.12.0")
58+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# No special ProGuard rules needed for this app.
2+
# WebView and security-crypto are handled by default Android rules.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
<uses-permission android:name="android.permission.INTERNET" />
5+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
6+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
7+
<uses-permission android:name="android.permission.WAKE_LOCK" />
8+
9+
<application
10+
android:allowBackup="true"
11+
android:icon="@mipmap/ic_launcher"
12+
android:label="@string/app_name"
13+
android:supportsRtl="true"
14+
android:theme="@style/Theme.AnyClaw"
15+
android:usesCleartextTraffic="true">
16+
17+
<activity
18+
android:name=".MainActivity"
19+
android:exported="true"
20+
android:configChanges="orientation|screenSize|keyboardHidden"
21+
android:windowSoftInputMode="adjustResize">
22+
<intent-filter>
23+
<action android:name="android.intent.action.MAIN" />
24+
<category android:name="android.intent.category.LAUNCHER" />
25+
</intent-filter>
26+
</activity>
27+
28+
<service
29+
android:name=".CodexForegroundService"
30+
android:exported="false" />
31+
32+
</application>
33+
34+
</manifest>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* bionic-compat.js - Android Bionic libc compatibility shim
3+
*
4+
* Loaded via NODE_OPTIONS="-r /path/to/bionic-compat.js"
5+
*
6+
* Based on https://github.com/AidanPark/openclaw-android
7+
*
8+
* Patches:
9+
* - process.platform: 'android' -> 'linux' (OpenClaw rejects 'android')
10+
* - os.networkInterfaces(): try-catch for Bionic getifaddrs() crashes
11+
* - os.cpus(): fallback for empty array (Android /proc/cpuinfo restriction)
12+
*/
13+
14+
'use strict';
15+
16+
Object.defineProperty(process, 'platform', {
17+
value: 'linux',
18+
writable: false,
19+
enumerable: true,
20+
configurable: true,
21+
});
22+
23+
const os = require('os');
24+
25+
const _originalCpus = os.cpus;
26+
os.cpus = function cpus() {
27+
const result = _originalCpus.call(os);
28+
if (result.length > 0) return result;
29+
return [{ model: 'unknown', speed: 0, times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 } }];
30+
};
31+
32+
const _originalNetworkInterfaces = os.networkInterfaces;
33+
os.networkInterfaces = function networkInterfaces() {
34+
try {
35+
return _originalNetworkInterfaces.call(os);
36+
} catch {
37+
return {
38+
lo: [{
39+
address: '127.0.0.1',
40+
netmask: '255.0.0.0',
41+
family: 'IPv4',
42+
mac: '00:00:00:00:00:00',
43+
internal: true,
44+
cidr: '127.0.0.1/8',
45+
}],
46+
};
47+
}
48+
};

0 commit comments

Comments
 (0)