clawdroid 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/bin/clawdroid +8 -0
- package/install.sh +72 -0
- package/lib/bionic-bypass.js +75 -0
- package/lib/index.js +334 -0
- package/lib/installer.js +325 -0
- package/lib/postinstall.js +36 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mithun Gowda B
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# ClawDroid
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/clawdroid)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](https://www.android.com/)
|
|
7
|
+
[](https://f-droid.org/packages/com.termux/)
|
|
8
|
+
[](https://github.com/NOSYTLABS/clawdroid/pulls)
|
|
9
|
+
|
|
10
|
+
> Run optimized OpenClaw AI Gateway on Android using Termux with automatic proot Ubuntu setup and Bionic Bypass fix.
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<img src="https://img.shields.io/badge/Platform-Android%20%7C%20Termux-blue" alt="Platform">
|
|
14
|
+
<img src="https://img.shields.io/badge/Status-Active-success" alt="Status">
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **One-Command Setup** - Automatically installs proot-distro, Ubuntu, Node.js 22, and OpenClaw
|
|
22
|
+
- **Bionic Bypass** - Fixes `os.networkInterfaces()` crash on Android's Bionic libc
|
|
23
|
+
- **Smart Loading** - Shows spinner until gateway is actually ready
|
|
24
|
+
- **Pass-through Commands** - Run any OpenClaw command directly via `clawdroid`
|
|
25
|
+
- **Optimized for Android** - Pre-configured for mobile performance
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick Install
|
|
30
|
+
|
|
31
|
+
### One-liner (recommended)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
curl -fsSL https://raw.githubusercontent.com/NOSYTLABS/clawdroid/main/install.sh | bash
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Or via npm
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g clawdroid
|
|
41
|
+
clawdroid setup
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Requirements
|
|
47
|
+
|
|
48
|
+
| Requirement | Details |
|
|
49
|
+
|-------------|---------|
|
|
50
|
+
| **Android** | 10 or higher |
|
|
51
|
+
| **Termux** | From [F-Droid](https://f-droid.org/packages/com.termux/) (NOT Play Store) |
|
|
52
|
+
| **Storage** | ~2GB for Ubuntu + OpenClaw |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# First-time setup (installs proot + Ubuntu + Node.js + OpenClaw)
|
|
60
|
+
clawdroid setup
|
|
61
|
+
|
|
62
|
+
# Check installation status
|
|
63
|
+
clawdroid status
|
|
64
|
+
|
|
65
|
+
# Update OpenClaw to latest version
|
|
66
|
+
clawdroid update
|
|
67
|
+
|
|
68
|
+
# Start OpenClaw gateway
|
|
69
|
+
clawdroid start
|
|
70
|
+
|
|
71
|
+
# Run onboarding to configure API keys
|
|
72
|
+
clawdroid onboarding
|
|
73
|
+
|
|
74
|
+
# Enter Ubuntu shell
|
|
75
|
+
clawdroid shell
|
|
76
|
+
|
|
77
|
+
# Any OpenClaw command works directly
|
|
78
|
+
clawdroid doctor
|
|
79
|
+
clawdroid gateway --verbose
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
> **Pro Tip:** Use `clawdroid gateway status` to check detailed health or `clawdroid logs --follow` to watch the gateway in real-time.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## How It Works
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
90
|
+
│ Termux │ │ proot-distro │ │ Ubuntu │
|
|
91
|
+
│ clawdroid │ ──► │ │ ──► │ OpenClaw │
|
|
92
|
+
│ │ │ Bionic Bypass │ │ Gateway │
|
|
93
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
1. **clawdroid** runs in Termux
|
|
97
|
+
2. Commands are passed through **proot-distro** with Bionic Bypass
|
|
98
|
+
3. **OpenClaw** runs inside Ubuntu environment
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
### Onboarding
|
|
105
|
+
|
|
106
|
+
When running `clawdroid onboarding`:
|
|
107
|
+
|
|
108
|
+
- **Binding**: Select `Loopback (127.0.0.1)` for non-rooted devices
|
|
109
|
+
- **API Keys**: Add your Gemini/OpenAI/Claude keys
|
|
110
|
+
|
|
111
|
+
### Battery Optimization
|
|
112
|
+
|
|
113
|
+
> **Important:** Disable battery optimization for Termux
|
|
114
|
+
|
|
115
|
+
Settings → Apps → Termux → Battery → **Unrestricted**
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Dashboard
|
|
120
|
+
|
|
121
|
+
Access the web dashboard at: **http://127.0.0.1:18789**
|
|
122
|
+
|
|
123
|
+
| Command | Description |
|
|
124
|
+
|---------|-------------|
|
|
125
|
+
| `/status` | Check gateway status |
|
|
126
|
+
| `/think high` | Enable high-quality thinking |
|
|
127
|
+
| `/reset` | Reset session |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Troubleshooting
|
|
132
|
+
|
|
133
|
+
### Gateway won't start
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Check status
|
|
137
|
+
clawdroid status
|
|
138
|
+
|
|
139
|
+
# Re-run setup if needed
|
|
140
|
+
clawdroid setup
|
|
141
|
+
|
|
142
|
+
# Make sure onboarding is complete
|
|
143
|
+
clawdroid onboarding
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### "os.networkInterfaces" error
|
|
147
|
+
|
|
148
|
+
Bionic Bypass not configured. Run:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
clawdroid setup
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Process killed in background
|
|
155
|
+
|
|
156
|
+
Disable battery optimization for Termux in Android settings.
|
|
157
|
+
|
|
158
|
+
### Permission denied
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
termux-setup-storage
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Manual Setup
|
|
167
|
+
|
|
168
|
+
<details>
|
|
169
|
+
<summary>Click to expand manual installation steps</summary>
|
|
170
|
+
|
|
171
|
+
### 1. Install proot-distro and Ubuntu
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
pkg update && pkg install -y proot-distro
|
|
175
|
+
proot-distro install ubuntu
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 2. Setup Node.js in Ubuntu
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
proot-distro login ubuntu
|
|
182
|
+
apt update && apt install -y curl
|
|
183
|
+
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
|
184
|
+
apt install -y nodejs
|
|
185
|
+
npm install -g openclaw
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 3. Create Bionic Bypass
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
mkdir -p ~/.openclawd
|
|
192
|
+
cat > ~/.openclawd/bionic-bypass.js << 'EOF'
|
|
193
|
+
const os = require('os');
|
|
194
|
+
const originalNetworkInterfaces = os.networkInterfaces;
|
|
195
|
+
os.networkInterfaces = function() {
|
|
196
|
+
try {
|
|
197
|
+
const interfaces = originalNetworkInterfaces.call(os);
|
|
198
|
+
if (interfaces && Object.keys(interfaces).length > 0) {
|
|
199
|
+
return interfaces;
|
|
200
|
+
}
|
|
201
|
+
} catch (e) {}
|
|
202
|
+
return {
|
|
203
|
+
lo: [{
|
|
204
|
+
address: '127.0.0.1',
|
|
205
|
+
netmask: '255.0.0.0',
|
|
206
|
+
family: 'IPv4',
|
|
207
|
+
mac: '00:00:00:00:00:00',
|
|
208
|
+
internal: true,
|
|
209
|
+
cidr: '127.0.0.1/8'
|
|
210
|
+
}]
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
EOF
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 4. Add to bashrc
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
echo 'export NODE_OPTIONS="--require ~/.openclawd/bionic-bypass.js"' >> ~/.bashrc
|
|
220
|
+
source ~/.bashrc
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### 5. Run OpenClaw
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
openclaw onboarding # Select "Loopback (127.0.0.1)"
|
|
227
|
+
openclaw gateway --verbose
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
</details>
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Credits
|
|
235
|
+
|
|
236
|
+
- Developed by **NOSYTLABS**
|
|
237
|
+
- Powered by [OpenClaw](https://github.com/anthropics/openclaw)
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Contributing
|
|
242
|
+
|
|
243
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
244
|
+
|
|
245
|
+
1. Fork the repository
|
|
246
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
247
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
248
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
249
|
+
5. Open a Pull Request
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## License
|
|
254
|
+
|
|
255
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
<p align="center">
|
|
260
|
+
Made with ❤️ by NOSYTLABS
|
|
261
|
+
</p>
|
package/bin/clawdroid
ADDED
package/install.sh
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# ClawDroid Installer
|
|
4
|
+
# One-liner: curl -fsSL https://raw.githubusercontent.com/NOSYTLABS/clawdroid/main/install.sh | bash
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
# Colors
|
|
10
|
+
RED='\033[0;31m'
|
|
11
|
+
GREEN='\033[0;32m'
|
|
12
|
+
YELLOW='\033[1;33m'
|
|
13
|
+
BLUE='\033[0;34m'
|
|
14
|
+
NC='\033[0m'
|
|
15
|
+
|
|
16
|
+
echo -e "${BLUE}"
|
|
17
|
+
cat << "EOF"
|
|
18
|
+
."". ."".
|
|
19
|
+
| | | | CLAWDROID
|
|
20
|
+
\ \._./ / Installer
|
|
21
|
+
'-./ \.-'
|
|
22
|
+
/ \
|
|
23
|
+
/_____\
|
|
24
|
+
EOF
|
|
25
|
+
echo -e "${NC}"
|
|
26
|
+
|
|
27
|
+
# Check if running in Termux
|
|
28
|
+
if [ ! -d "/data/data/com.termux" ] && [ -z "$TERMUX_VERSION" ]; then
|
|
29
|
+
echo -e "${YELLOW}Warning:${NC} Not running in Termux - some features may not work"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Update and install packages
|
|
33
|
+
echo -e "\n${BLUE}[1/3]${NC} Installing required packages..."
|
|
34
|
+
|
|
35
|
+
# Optimize Termux repo access and update
|
|
36
|
+
echo -e " ${BLUE}•${NC} Updating Termux repositories..."
|
|
37
|
+
pkg update -y -o Dpkg::Options::="--force-confnew" || true
|
|
38
|
+
pkg upgrade -y -o Dpkg::Options::="--force-confnew" || true
|
|
39
|
+
|
|
40
|
+
echo -e " ${BLUE}•${NC} Installing dependencies..."
|
|
41
|
+
pkg install -y nodejs-lts git proot-distro android-tools termux-api
|
|
42
|
+
|
|
43
|
+
echo -e " ${GREEN}✓${NC} Node.js $(node --version)"
|
|
44
|
+
echo -e " ${GREEN}✓${NC} npm $(npm --version)"
|
|
45
|
+
echo -e " ${GREEN}✓${NC} git installed"
|
|
46
|
+
echo -e " ${GREEN}✓${NC} proot-distro installed"
|
|
47
|
+
echo -e " ${GREEN}✓${NC} adb $(adb version | head -n 1)"
|
|
48
|
+
echo -e " ${GREEN}✓${NC} termux-api installed"
|
|
49
|
+
|
|
50
|
+
# Install clawdroid from npm
|
|
51
|
+
echo -e "\n${BLUE}[2/3]${NC} Installing clawdroid..."
|
|
52
|
+
npm install -g clawdroid
|
|
53
|
+
|
|
54
|
+
echo -e "\n${BLUE}[3/3]${NC} Verifying Android tools..."
|
|
55
|
+
adb start-server >/dev/null 2>&1 || true
|
|
56
|
+
adb devices || true
|
|
57
|
+
|
|
58
|
+
echo -e "\n${GREEN}═══════════════════════════════════════════${NC}"
|
|
59
|
+
echo -e "${GREEN}Installation complete!${NC}"
|
|
60
|
+
echo -e "${GREEN}═══════════════════════════════════════════${NC}"
|
|
61
|
+
echo ""
|
|
62
|
+
echo -e "${YELLOW}Next steps:${NC}"
|
|
63
|
+
echo " 1. Run setup: clawdroid setup"
|
|
64
|
+
echo " 2. Run onboarding: clawdroid onboarding"
|
|
65
|
+
echo " → Select 'Loopback (127.0.0.1)' when asked!"
|
|
66
|
+
echo " 3. Start gateway: clawdroid start"
|
|
67
|
+
echo ""
|
|
68
|
+
echo -e "Dashboard: ${BLUE}http://127.0.0.1:18789${NC}"
|
|
69
|
+
echo ""
|
|
70
|
+
echo -e "${YELLOW}Tip:${NC} Disable battery optimization for Termux in Android settings"
|
|
71
|
+
echo -e "${YELLOW}Tip:${NC} Install Termux:API app from F-Droid for camera, wakelock, and sensors"
|
|
72
|
+
echo ""
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bionic Bypass - Fixes os.networkInterfaces() on Android
|
|
3
|
+
*
|
|
4
|
+
* Android's Bionic libc blocks certain network interface queries.
|
|
5
|
+
* This module provides a workaround by returning a mock interface.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
const BYPASS_SCRIPT = `
|
|
12
|
+
// ClawDroid Bionic Bypass - Auto-generated
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const originalNetworkInterfaces = os.networkInterfaces;
|
|
15
|
+
|
|
16
|
+
os.networkInterfaces = function() {
|
|
17
|
+
try {
|
|
18
|
+
const interfaces = originalNetworkInterfaces.call(os);
|
|
19
|
+
if (interfaces && Object.keys(interfaces).length > 0) {
|
|
20
|
+
return interfaces;
|
|
21
|
+
}
|
|
22
|
+
} catch (e) {
|
|
23
|
+
// Bionic blocked the call, use fallback
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Return mock loopback interface
|
|
27
|
+
return {
|
|
28
|
+
lo: [
|
|
29
|
+
{
|
|
30
|
+
address: '127.0.0.1',
|
|
31
|
+
netmask: '255.0.0.0',
|
|
32
|
+
family: 'IPv4',
|
|
33
|
+
mac: '00:00:00:00:00:00',
|
|
34
|
+
internal: true,
|
|
35
|
+
cidr: '127.0.0.1/8'
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
export function getBypassScriptPath() {
|
|
43
|
+
const homeDir = process.env.HOME || '/data/data/com.termux/files/home';
|
|
44
|
+
return path.join(homeDir, '.clawdroid', 'bionic-bypass.js');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function installBypass() {
|
|
48
|
+
const scriptPath = getBypassScriptPath();
|
|
49
|
+
const scriptDir = path.dirname(scriptPath);
|
|
50
|
+
|
|
51
|
+
if (!fs.existsSync(scriptDir)) {
|
|
52
|
+
fs.mkdirSync(scriptDir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fs.writeFileSync(scriptPath, BYPASS_SCRIPT, 'utf8');
|
|
56
|
+
fs.chmodSync(scriptPath, '644');
|
|
57
|
+
|
|
58
|
+
return scriptPath;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getNodeOptions() {
|
|
62
|
+
const scriptPath = getBypassScriptPath();
|
|
63
|
+
return `--require "${scriptPath}"`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function isAndroid() {
|
|
67
|
+
return process.platform === 'android' ||
|
|
68
|
+
fs.existsSync('/data/data/com.termux') ||
|
|
69
|
+
process.env.TERMUX_VERSION !== undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function checkBypassInstalled() {
|
|
73
|
+
const scriptPath = getBypassScriptPath();
|
|
74
|
+
return fs.existsSync(scriptPath);
|
|
75
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClawDroid - Main entry point
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
configureTermux,
|
|
7
|
+
getInstallStatus,
|
|
8
|
+
installProot,
|
|
9
|
+
installUbuntu,
|
|
10
|
+
setupProotUbuntu,
|
|
11
|
+
setupBionicBypassInProot,
|
|
12
|
+
runInProot,
|
|
13
|
+
runInProotWithCallback
|
|
14
|
+
} from './installer.js';
|
|
15
|
+
import { isAndroid } from './bionic-bypass.js';
|
|
16
|
+
import { spawn } from 'child_process';
|
|
17
|
+
|
|
18
|
+
const VERSION = '1.0.0';
|
|
19
|
+
|
|
20
|
+
function printBanner() {
|
|
21
|
+
console.log(`
|
|
22
|
+
|
|
23
|
+
CLAWDROID
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function printHelp() {
|
|
30
|
+
console.log(`
|
|
31
|
+
Usage: clawdroid [command] [args...]
|
|
32
|
+
|
|
33
|
+
If run without arguments, it starts the OpenClaw gateway.
|
|
34
|
+
|
|
35
|
+
Commands:
|
|
36
|
+
start Start OpenClaw gateway (Default)
|
|
37
|
+
setup Full installation (proot + Ubuntu + OpenClaw)
|
|
38
|
+
update Update OpenClaw to the latest version
|
|
39
|
+
repair Re-install OpenClaw and dependencies
|
|
40
|
+
status Check installation status
|
|
41
|
+
shell Open Ubuntu shell with OpenClaw ready
|
|
42
|
+
help Show this help message
|
|
43
|
+
|
|
44
|
+
Any other command is passed directly to openclaw in proot:
|
|
45
|
+
clawdroid onboarding → openclaw onboarding
|
|
46
|
+
clawdroid gateway -v → openclaw gateway -v
|
|
47
|
+
clawdroid doctor → openclaw doctor
|
|
48
|
+
clawdroid <anything> → openclaw <anything>
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
clawdroid # Start gateway
|
|
52
|
+
clawdroid setup # First-time setup
|
|
53
|
+
clawdroid update # Update OpenClaw
|
|
54
|
+
clawdroid repair # Fix installation
|
|
55
|
+
clawdroid shell # Enter Ubuntu shell
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function runSetup() {
|
|
60
|
+
console.log('Starting ClawDroid setup for Termux...\n');
|
|
61
|
+
console.log('This will install: proot-distro → Ubuntu → Node.js 22 → OpenClaw\n');
|
|
62
|
+
|
|
63
|
+
if (!isAndroid()) {
|
|
64
|
+
console.log('Warning: This package is designed for Android/Termux.');
|
|
65
|
+
console.log('Some features may not work on other platforms.\n');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let status = getInstallStatus();
|
|
69
|
+
|
|
70
|
+
// Step 1: Install proot-distro
|
|
71
|
+
console.log('[1/5] Checking proot-distro...');
|
|
72
|
+
if (!status.proot) {
|
|
73
|
+
console.log(' Installing proot-distro...');
|
|
74
|
+
installProot();
|
|
75
|
+
} else {
|
|
76
|
+
console.log(' ✓ proot-distro installed');
|
|
77
|
+
}
|
|
78
|
+
console.log('');
|
|
79
|
+
|
|
80
|
+
// Step 2: Install Ubuntu
|
|
81
|
+
console.log('[2/5] Checking Ubuntu in proot...');
|
|
82
|
+
status = getInstallStatus();
|
|
83
|
+
if (!status.ubuntu) {
|
|
84
|
+
console.log(' Installing Ubuntu (this takes a while)...');
|
|
85
|
+
installUbuntu();
|
|
86
|
+
} else {
|
|
87
|
+
console.log(' ✓ Ubuntu installed');
|
|
88
|
+
}
|
|
89
|
+
console.log('');
|
|
90
|
+
|
|
91
|
+
// Step 3: Setup Node.js and OpenClaw in Ubuntu
|
|
92
|
+
console.log('[3/5] Setting up Node.js and OpenClaw in Ubuntu...');
|
|
93
|
+
status = getInstallStatus();
|
|
94
|
+
if (!status.openClawInProot) {
|
|
95
|
+
setupProotUbuntu();
|
|
96
|
+
} else {
|
|
97
|
+
console.log(' ✓ OpenClaw already installed in proot');
|
|
98
|
+
}
|
|
99
|
+
console.log('');
|
|
100
|
+
|
|
101
|
+
// Step 4: Setup Bionic Bypass in proot
|
|
102
|
+
console.log('[4/5] Setting up Bionic Bypass in proot...');
|
|
103
|
+
setupBionicBypassInProot();
|
|
104
|
+
console.log('');
|
|
105
|
+
|
|
106
|
+
// Step 5: Configure Termux wake-lock
|
|
107
|
+
console.log('[5/5] Configuring Termux...');
|
|
108
|
+
configureTermux();
|
|
109
|
+
console.log('');
|
|
110
|
+
|
|
111
|
+
// Done
|
|
112
|
+
console.log('═══════════════════════════════════════════');
|
|
113
|
+
console.log('Setup complete!');
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log('Next steps:');
|
|
116
|
+
console.log(' 1. Run onboarding: clawdroid onboarding');
|
|
117
|
+
console.log(' → Select "Loopback (127.0.0.1)" when asked!');
|
|
118
|
+
console.log(' 2. Start gateway: clawdroid start');
|
|
119
|
+
console.log('');
|
|
120
|
+
console.log('Dashboard: http://127.0.0.1:18789');
|
|
121
|
+
console.log('═══════════════════════════════════════════');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function showStatus() {
|
|
125
|
+
// Quick loading while checking proot
|
|
126
|
+
process.stdout.write('Checking installation status...');
|
|
127
|
+
const status = getInstallStatus();
|
|
128
|
+
process.stdout.write('\r' + ' '.repeat(35) + '\r');
|
|
129
|
+
|
|
130
|
+
console.log('Installation Status:\n');
|
|
131
|
+
|
|
132
|
+
console.log('Termux:');
|
|
133
|
+
console.log(` proot-distro: ${status.proot ? '✓ installed' : '✗ missing'}`);
|
|
134
|
+
console.log(` Ubuntu (proot): ${status.ubuntu ? '✓ installed' : '✗ not installed'}`);
|
|
135
|
+
console.log('');
|
|
136
|
+
|
|
137
|
+
if (status.ubuntu) {
|
|
138
|
+
console.log('Inside Ubuntu:');
|
|
139
|
+
console.log(` OpenClaw: ${status.openClawInProot ? '✓ installed' : '✗ not installed'}`);
|
|
140
|
+
console.log(` Bionic Bypass: ${status.bionicBypassInProot ? '✓ configured' : '✗ not configured'}`);
|
|
141
|
+
console.log('');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (status.proot && status.ubuntu && status.openClawInProot) {
|
|
145
|
+
console.log('Status: ✓ Ready to run!');
|
|
146
|
+
console.log('');
|
|
147
|
+
console.log('Commands:');
|
|
148
|
+
console.log(' clawdroid start # Start gateway');
|
|
149
|
+
console.log(' clawdroid onboarding # Configure API keys');
|
|
150
|
+
console.log(' clawdroid shell # Enter Ubuntu shell');
|
|
151
|
+
} else {
|
|
152
|
+
console.log('Status: ✗ Setup incomplete');
|
|
153
|
+
console.log('Run: clawdroid setup');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function updateOpenClaw() {
|
|
158
|
+
const status = getInstallStatus();
|
|
159
|
+
|
|
160
|
+
if (!status.proot || !status.ubuntu) {
|
|
161
|
+
console.error('proot/Ubuntu not installed. Run: clawdroid setup');
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log('Updating OpenClaw to the latest version...');
|
|
166
|
+
|
|
167
|
+
// Use runInProotWithCallback to show real-time output
|
|
168
|
+
const proc = runInProotWithCallback('npm install -g openclaw@latest', () => {
|
|
169
|
+
console.log('npm output:');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
proc.on('close', (code) => {
|
|
173
|
+
if (code === 0) {
|
|
174
|
+
console.log('\n✓ OpenClaw updated successfully!');
|
|
175
|
+
console.log('Run "clawdroid start" to launch the gateway.');
|
|
176
|
+
} else {
|
|
177
|
+
console.error(`\n✗ Update failed with code ${code}`);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function startGateway() {
|
|
183
|
+
const status = getInstallStatus();
|
|
184
|
+
|
|
185
|
+
if (!status.proot || !status.ubuntu) {
|
|
186
|
+
console.error('proot/Ubuntu not installed. Run: clawdroid setup');
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!status.openClawInProot) {
|
|
191
|
+
console.error('OpenClaw not installed in proot. Run: clawdroid setup');
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Loading animation until dashboard responds
|
|
196
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
197
|
+
let i = 0;
|
|
198
|
+
let started = false;
|
|
199
|
+
const DASHBOARD_URL = 'http://127.0.0.1:18789';
|
|
200
|
+
|
|
201
|
+
const spinner = setInterval(() => {
|
|
202
|
+
if (!started) {
|
|
203
|
+
process.stdout.write(`\r${frames[i++ % frames.length]} Starting OpenClaw gateway...`);
|
|
204
|
+
}
|
|
205
|
+
}, 80);
|
|
206
|
+
|
|
207
|
+
// Poll dashboard until it responds
|
|
208
|
+
const checkDashboard = setInterval(async () => {
|
|
209
|
+
if (started) return;
|
|
210
|
+
try {
|
|
211
|
+
const response = await fetch(DASHBOARD_URL, { method: 'HEAD', signal: AbortSignal.timeout(1000) });
|
|
212
|
+
if (response.ok || response.status < 500) {
|
|
213
|
+
started = true;
|
|
214
|
+
clearInterval(spinner);
|
|
215
|
+
clearInterval(checkDashboard);
|
|
216
|
+
process.stdout.write('\r' + ' '.repeat(40) + '\r');
|
|
217
|
+
console.log('✓ OpenClaw gateway started!\n');
|
|
218
|
+
console.log(`Dashboard: ${DASHBOARD_URL}`);
|
|
219
|
+
console.log('Press Ctrl+C to stop\n');
|
|
220
|
+
console.log('─'.repeat(45) + '\n');
|
|
221
|
+
}
|
|
222
|
+
} catch { /* ignore polling errors */ }
|
|
223
|
+
}, 500);
|
|
224
|
+
|
|
225
|
+
// Start gateway in background (suppress output until ready)
|
|
226
|
+
const gateway = runInProot('openclaw gateway --verbose');
|
|
227
|
+
|
|
228
|
+
gateway.on('error', (err) => {
|
|
229
|
+
clearInterval(spinner);
|
|
230
|
+
clearInterval(checkDashboard);
|
|
231
|
+
console.error('\nFailed to start gateway:', err.message);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
gateway.on('close', (code) => {
|
|
235
|
+
clearInterval(spinner);
|
|
236
|
+
clearInterval(checkDashboard);
|
|
237
|
+
if (!started) {
|
|
238
|
+
console.log('\nGateway exited before starting. Run: clawdroid onboarding');
|
|
239
|
+
}
|
|
240
|
+
console.log(`Gateway exited with code ${code}`);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function runOpenclawCommand(args) {
|
|
245
|
+
const status = getInstallStatus();
|
|
246
|
+
|
|
247
|
+
if (!status.proot || !status.ubuntu || !status.openClawInProot) {
|
|
248
|
+
console.error('Setup not complete. Run: clawdroid setup');
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const command = args.join(' ');
|
|
253
|
+
console.log(`Running: openclaw ${command}\n`);
|
|
254
|
+
|
|
255
|
+
// Special hint for onboarding
|
|
256
|
+
if (args[0] === 'onboarding') {
|
|
257
|
+
console.log('TIP: Select "Loopback (127.0.0.1)" when asked for binding!\n');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const proc = runInProot(`openclaw ${command}`);
|
|
261
|
+
|
|
262
|
+
proc.on('error', (err) => {
|
|
263
|
+
console.error('Failed to run command:', err.message);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function openShell() {
|
|
268
|
+
const status = getInstallStatus();
|
|
269
|
+
|
|
270
|
+
if (!status.proot || !status.ubuntu) {
|
|
271
|
+
console.error('proot/Ubuntu not installed. Run: clawdroid setup');
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log('Entering Ubuntu shell (with Bionic Bypass)...');
|
|
276
|
+
console.log('Type "exit" to return to Termux\n');
|
|
277
|
+
|
|
278
|
+
const shell = spawn('proot-distro', ['login', 'ubuntu'], {
|
|
279
|
+
stdio: 'inherit'
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
shell.on('error', (err) => {
|
|
283
|
+
console.error('Failed to open shell:', err.message);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export async function main(args) {
|
|
288
|
+
const command = args[0] || 'start'; // Default to start
|
|
289
|
+
|
|
290
|
+
printBanner();
|
|
291
|
+
|
|
292
|
+
switch (command) {
|
|
293
|
+
case 'setup':
|
|
294
|
+
case 'install':
|
|
295
|
+
await runSetup();
|
|
296
|
+
break;
|
|
297
|
+
|
|
298
|
+
case 'status':
|
|
299
|
+
showStatus();
|
|
300
|
+
break;
|
|
301
|
+
|
|
302
|
+
case 'update':
|
|
303
|
+
updateOpenClaw();
|
|
304
|
+
break;
|
|
305
|
+
|
|
306
|
+
case 'repair':
|
|
307
|
+
console.log('Repairing installation...');
|
|
308
|
+
await setupProotUbuntu();
|
|
309
|
+
setupBionicBypassInProot();
|
|
310
|
+
console.log('Repair complete!');
|
|
311
|
+
break;
|
|
312
|
+
|
|
313
|
+
case 'start':
|
|
314
|
+
case 'run':
|
|
315
|
+
startGateway();
|
|
316
|
+
break;
|
|
317
|
+
|
|
318
|
+
case 'shell':
|
|
319
|
+
case 'ubuntu':
|
|
320
|
+
openShell();
|
|
321
|
+
break;
|
|
322
|
+
|
|
323
|
+
case 'help':
|
|
324
|
+
case '--help':
|
|
325
|
+
case '-h':
|
|
326
|
+
printHelp();
|
|
327
|
+
break;
|
|
328
|
+
|
|
329
|
+
default:
|
|
330
|
+
// Pass any other command to openclaw in proot
|
|
331
|
+
runOpenclawCommand(args);
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
package/lib/installer.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClawDroid Installer - Handles environment setup for Termux
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { execSync, spawn } from 'child_process';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { installBypass, getBypassScriptPath, getNodeOptions } from './bionic-bypass.js';
|
|
9
|
+
|
|
10
|
+
const HOME = process.env.HOME || '/data/data/com.termux/files/home';
|
|
11
|
+
const BASHRC = path.join(HOME, '.bashrc');
|
|
12
|
+
const ZSHRC = path.join(HOME, '.zshrc');
|
|
13
|
+
const PROOT_ROOTFS = '/data/data/com.termux/files/usr/var/lib/proot-distro/installed-rootfs';
|
|
14
|
+
const PROOT_UBUNTU_ROOT = path.join(PROOT_ROOTFS, 'ubuntu', 'root');
|
|
15
|
+
|
|
16
|
+
export function checkDependencies() {
|
|
17
|
+
const deps = {
|
|
18
|
+
node: false,
|
|
19
|
+
npm: false,
|
|
20
|
+
git: false,
|
|
21
|
+
proot: false
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
execSync('node --version', { stdio: 'pipe' });
|
|
26
|
+
deps.node = true;
|
|
27
|
+
} catch { /* not installed */ }
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
execSync('npm --version', { stdio: 'pipe' });
|
|
31
|
+
deps.npm = true;
|
|
32
|
+
} catch { /* not installed */ }
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
execSync('git --version', { stdio: 'pipe' });
|
|
36
|
+
deps.git = true;
|
|
37
|
+
} catch { /* not installed */ }
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
execSync('which proot-distro', { stdio: 'pipe' });
|
|
41
|
+
deps.proot = true;
|
|
42
|
+
} catch { /* not installed */ }
|
|
43
|
+
|
|
44
|
+
return deps;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function installTermuxDeps() {
|
|
48
|
+
console.log('Installing Termux dependencies...');
|
|
49
|
+
|
|
50
|
+
const packages = ['nodejs-lts', 'git', 'openssh'];
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
execSync('pkg update -y', { stdio: 'inherit' });
|
|
54
|
+
execSync(`pkg install -y ${packages.join(' ')}`, { stdio: 'inherit' });
|
|
55
|
+
return true;
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.error('Failed to install Termux packages:', err.message);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function setupBionicBypass() {
|
|
63
|
+
console.log('Setting up Bionic Bypass...');
|
|
64
|
+
|
|
65
|
+
const scriptPath = installBypass();
|
|
66
|
+
const nodeOptions = getNodeOptions();
|
|
67
|
+
const exportLine = `export NODE_OPTIONS="${nodeOptions}"`;
|
|
68
|
+
|
|
69
|
+
// Add to shell configs
|
|
70
|
+
for (const rcFile of [BASHRC, ZSHRC]) {
|
|
71
|
+
if (fs.existsSync(rcFile)) {
|
|
72
|
+
const content = fs.readFileSync(rcFile, 'utf8');
|
|
73
|
+
if (!content.includes('bionic-bypass')) {
|
|
74
|
+
fs.appendFileSync(rcFile, `\n# ClawDroid Bionic Bypass\n${exportLine}\n`);
|
|
75
|
+
console.log(`Updated ${path.basename(rcFile)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Also set for current session
|
|
81
|
+
process.env.NODE_OPTIONS = nodeOptions;
|
|
82
|
+
|
|
83
|
+
return scriptPath;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function installOpenClaw() {
|
|
87
|
+
console.log('Installing OpenClaw...');
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
execSync('npm install -g openclaw', { stdio: 'inherit' });
|
|
91
|
+
return true;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error('Failed to install OpenClaw:', err.message);
|
|
94
|
+
console.log('You may need to install it manually: npm install -g openclaw');
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function configureTermux() {
|
|
100
|
+
console.log('Configuring Termux for background operation...');
|
|
101
|
+
|
|
102
|
+
// Create wake-lock script
|
|
103
|
+
const wakeLockScript = path.join(HOME, '.clawdroid', 'wakelock.sh');
|
|
104
|
+
const wakeLockContent = `#!/bin/bash
|
|
105
|
+
# Keep Termux awake while OpenClaw runs
|
|
106
|
+
termux-wake-lock
|
|
107
|
+
trap "termux-wake-unlock" EXIT
|
|
108
|
+
exec "$@"
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
fs.writeFileSync(wakeLockScript, wakeLockContent, 'utf8');
|
|
112
|
+
fs.chmodSync(wakeLockScript, '755');
|
|
113
|
+
|
|
114
|
+
console.log('Wake-lock script created');
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log('IMPORTANT: Disable battery optimization for Termux in Android settings!');
|
|
117
|
+
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function getInstallStatus() {
|
|
122
|
+
const PROOT_ROOTFS = '/data/data/com.termux/files/usr/var/lib/proot-distro/installed-rootfs';
|
|
123
|
+
|
|
124
|
+
// Check proot-distro
|
|
125
|
+
let hasProot = false;
|
|
126
|
+
try {
|
|
127
|
+
execSync('command -v proot-distro', { stdio: 'pipe' });
|
|
128
|
+
hasProot = true;
|
|
129
|
+
} catch { /* not installed */ }
|
|
130
|
+
|
|
131
|
+
// Check if ubuntu is installed by checking rootfs directory
|
|
132
|
+
let hasUbuntu = false;
|
|
133
|
+
try {
|
|
134
|
+
hasUbuntu = fs.existsSync(path.join(PROOT_ROOTFS, 'ubuntu'));
|
|
135
|
+
} catch { /* check failed */ }
|
|
136
|
+
|
|
137
|
+
// Check if openclaw exists in proot ubuntu
|
|
138
|
+
let hasOpenClawInProot = false;
|
|
139
|
+
if (hasUbuntu) {
|
|
140
|
+
try {
|
|
141
|
+
execSync('proot-distro login ubuntu -- bash -c "command -v openclaw"', { stdio: 'pipe', timeout: 15000 });
|
|
142
|
+
hasOpenClawInProot = true;
|
|
143
|
+
} catch { /* not installed */ }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check bionic bypass in proot
|
|
147
|
+
let hasBionicBypassInProot = false;
|
|
148
|
+
try {
|
|
149
|
+
const prootBypassPath = path.join(PROOT_ROOTFS, 'ubuntu', 'root', '.clawdroid', 'bionic-bypass.js');
|
|
150
|
+
hasBionicBypassInProot = fs.existsSync(prootBypassPath);
|
|
151
|
+
} catch { /* check failed */ }
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
proot: hasProot,
|
|
155
|
+
ubuntu: hasUbuntu,
|
|
156
|
+
openClawInProot: hasOpenClawInProot,
|
|
157
|
+
bionicBypassInProot: hasBionicBypassInProot,
|
|
158
|
+
// Legacy (for termux-native mode)
|
|
159
|
+
bionicBypass: fs.existsSync(getBypassScriptPath()),
|
|
160
|
+
nodeOptions: process.env.NODE_OPTIONS?.includes('bionic-bypass') || false,
|
|
161
|
+
openClaw: (() => {
|
|
162
|
+
try {
|
|
163
|
+
execSync('command -v openclaw', { stdio: 'pipe' });
|
|
164
|
+
return true;
|
|
165
|
+
} catch { return false; }
|
|
166
|
+
})()
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function installProot() {
|
|
171
|
+
console.log('Installing proot-distro...');
|
|
172
|
+
try {
|
|
173
|
+
execSync('pkg install -y proot-distro', { stdio: 'inherit' });
|
|
174
|
+
return true;
|
|
175
|
+
} catch (err) {
|
|
176
|
+
console.error('Failed to install proot-distro:', err.message);
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function installUbuntu() {
|
|
182
|
+
console.log('Installing Ubuntu in proot (this may take a while)...');
|
|
183
|
+
try {
|
|
184
|
+
execSync('proot-distro install ubuntu', { stdio: 'inherit' });
|
|
185
|
+
return true;
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.error('Failed to install Ubuntu:', err.message);
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function setupProotUbuntu() {
|
|
193
|
+
console.log('Setting up Node.js and OpenClaw in Ubuntu...');
|
|
194
|
+
|
|
195
|
+
// Optimized setup script with better memory management and error handling
|
|
196
|
+
const setupScript = `
|
|
197
|
+
set -e
|
|
198
|
+
|
|
199
|
+
# Optimize apt
|
|
200
|
+
echo 'Acquire::ForceIPv4 "true";' > /etc/apt/apt.conf.d/99force-ipv4
|
|
201
|
+
|
|
202
|
+
echo "Updating package lists..."
|
|
203
|
+
apt update && apt upgrade -y
|
|
204
|
+
|
|
205
|
+
echo "Installing core dependencies..."
|
|
206
|
+
apt install -y curl wget git build-essential python3
|
|
207
|
+
|
|
208
|
+
echo "Setting up Node.js 22..."
|
|
209
|
+
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
|
210
|
+
apt install -y nodejs
|
|
211
|
+
|
|
212
|
+
# Verify Node.js
|
|
213
|
+
node -v
|
|
214
|
+
npm -v
|
|
215
|
+
|
|
216
|
+
echo "Installing OpenClaw..."
|
|
217
|
+
npm install -g openclaw --loglevel=error
|
|
218
|
+
|
|
219
|
+
# Cleanup to save space
|
|
220
|
+
apt autoremove -y
|
|
221
|
+
apt clean
|
|
222
|
+
`;
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
execSync(`proot-distro login ubuntu -- bash -c '${setupScript}'`, { stdio: 'inherit' });
|
|
226
|
+
return true;
|
|
227
|
+
} catch (err) {
|
|
228
|
+
console.error('Failed to setup Ubuntu:', err.message);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function setupBionicBypassInProot() {
|
|
234
|
+
console.log('Setting up Bionic Bypass in proot Ubuntu...');
|
|
235
|
+
|
|
236
|
+
const bypassScript = `
|
|
237
|
+
const os = require('os');
|
|
238
|
+
const originalNetworkInterfaces = os.networkInterfaces;
|
|
239
|
+
os.networkInterfaces = function() {
|
|
240
|
+
try {
|
|
241
|
+
const interfaces = originalNetworkInterfaces.call(os);
|
|
242
|
+
if (interfaces && Object.keys(interfaces).length > 0) {
|
|
243
|
+
return interfaces;
|
|
244
|
+
}
|
|
245
|
+
} catch (e) {}
|
|
246
|
+
return {
|
|
247
|
+
lo: [{
|
|
248
|
+
address: '127.0.0.1',
|
|
249
|
+
netmask: '255.0.0.0',
|
|
250
|
+
family: 'IPv4',
|
|
251
|
+
mac: '00:00:00:00:00:00',
|
|
252
|
+
internal: true,
|
|
253
|
+
cidr: '127.0.0.1/8'
|
|
254
|
+
}]
|
|
255
|
+
};
|
|
256
|
+
};
|
|
257
|
+
`;
|
|
258
|
+
|
|
259
|
+
const prootBypassPath = path.join(PROOT_UBUNTU_ROOT, '.clawdroid', 'bionic-bypass.js');
|
|
260
|
+
const prootBypassDir = path.dirname(prootBypassPath);
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
if (!fs.existsSync(prootBypassDir)) {
|
|
264
|
+
fs.mkdirSync(prootBypassDir, { recursive: true });
|
|
265
|
+
}
|
|
266
|
+
fs.writeFileSync(prootBypassPath, bypassScript, 'utf8');
|
|
267
|
+
|
|
268
|
+
// Add to bashrc in proot
|
|
269
|
+
const prootBashrc = path.join(PROOT_UBUNTU_ROOT, '.bashrc');
|
|
270
|
+
const exportLine = 'export NODE_OPTIONS="--require /root/.clawdroid/bionic-bypass.js"';
|
|
271
|
+
|
|
272
|
+
let bashrcContent = '';
|
|
273
|
+
if (fs.existsSync(prootBashrc)) {
|
|
274
|
+
bashrcContent = fs.readFileSync(prootBashrc, 'utf8');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!bashrcContent.includes('bionic-bypass')) {
|
|
278
|
+
fs.appendFileSync(prootBashrc, `\n# ClawDroid Bionic Bypass\n${exportLine}\n`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
console.log('Bionic Bypass configured in proot Ubuntu');
|
|
282
|
+
return true;
|
|
283
|
+
} catch (err) {
|
|
284
|
+
console.error('Failed to setup Bionic Bypass in proot:', err.message);
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function runInProot(command) {
|
|
290
|
+
const nodeOptions = '--require /root/.clawdroid/bionic-bypass.js';
|
|
291
|
+
return spawn('proot-distro', ['login', 'ubuntu', '--', 'bash', '-c', `export NODE_OPTIONS="${nodeOptions}" && ${command}`], {
|
|
292
|
+
stdio: 'inherit'
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function runInProotWithCallback(command, onFirstOutput) {
|
|
297
|
+
const nodeOptions = '--require /root/.clawdroid/bionic-bypass.js';
|
|
298
|
+
let firstOutput = true;
|
|
299
|
+
|
|
300
|
+
const proc = spawn('proot-distro', ['login', 'ubuntu', '--', 'bash', '-c', `export NODE_OPTIONS="${nodeOptions}" && ${command}`], {
|
|
301
|
+
stdio: ['inherit', 'pipe', 'pipe']
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
proc.stdout.on('data', (data) => {
|
|
305
|
+
if (firstOutput) {
|
|
306
|
+
firstOutput = false;
|
|
307
|
+
onFirstOutput();
|
|
308
|
+
}
|
|
309
|
+
process.stdout.write(data);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
proc.stderr.on('data', (data) => {
|
|
313
|
+
if (firstOutput) {
|
|
314
|
+
firstOutput = false;
|
|
315
|
+
onFirstOutput();
|
|
316
|
+
}
|
|
317
|
+
// Filter out harmless proot warnings
|
|
318
|
+
const str = data.toString();
|
|
319
|
+
if (!str.includes('proot warning') && !str.includes("can't sanitize")) {
|
|
320
|
+
process.stderr.write(data);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
return proc;
|
|
325
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-install script - runs after npm install
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { isAndroid, installBypass, getNodeOptions } from './bionic-bypass.js';
|
|
6
|
+
|
|
7
|
+
function main() {
|
|
8
|
+
console.log('\n📱 ClawDroid post-install\n');
|
|
9
|
+
|
|
10
|
+
if (!isAndroid()) {
|
|
11
|
+
console.log('Not running on Android/Termux - skipping Bionic Bypass setup.');
|
|
12
|
+
console.log('You can still use this package on other systems.\n');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Install the bypass script
|
|
17
|
+
try {
|
|
18
|
+
const scriptPath = installBypass();
|
|
19
|
+
console.log(`✓ Bionic Bypass installed at: ${scriptPath}`);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error('✗ Failed to install Bionic Bypass:', err.message);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Show instructions
|
|
26
|
+
const nodeOptions = getNodeOptions();
|
|
27
|
+
|
|
28
|
+
console.log('\n' + '═'.repeat(50));
|
|
29
|
+
console.log('IMPORTANT: Add this to your shell config (~/.bashrc):');
|
|
30
|
+
console.log('═'.repeat(50));
|
|
31
|
+
console.log(`\nexport NODE_OPTIONS="${nodeOptions}"\n`);
|
|
32
|
+
console.log('Or run: clawdroid setup');
|
|
33
|
+
console.log('═'.repeat(50) + '\n');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clawdroid",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ClawDroid - Optimized OpenClaw AI Gateway for Android Termux",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"clawdroid": "bin/clawdroid"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"postinstall": "node lib/postinstall.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"openclaw",
|
|
15
|
+
"termux",
|
|
16
|
+
"android",
|
|
17
|
+
"ai",
|
|
18
|
+
"gateway",
|
|
19
|
+
"gemini",
|
|
20
|
+
"claude"
|
|
21
|
+
],
|
|
22
|
+
"author": "NOSYTLABS",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/NOSYTLABS/clawdroid.git"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/NOSYTLABS/clawdroid#readme",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/NOSYTLABS/clawdroid/issues"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
},
|
|
35
|
+
"os": [
|
|
36
|
+
"android",
|
|
37
|
+
"linux"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"chalk": "^5.3.0",
|
|
41
|
+
"inquirer": "^9.2.12",
|
|
42
|
+
"ora": "^7.0.1"
|
|
43
|
+
}
|
|
44
|
+
}
|