@kritchoff/agent-browser 0.9.52 โ 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/README.md +82 -849
- package/bin/agent-browser.js +2 -1
- package/package.json +1 -3
- package/README.sdk.md +0 -129
- package/scripts/fast_reset.sh +0 -117
- package/scripts/snapshot_manager.sh +0 -293
- package/scripts/vaccine-run +0 -26
- package/sdk.sh +0 -176
- package/start.sh +0 -109
package/bin/agent-browser.js
CHANGED
|
@@ -57,7 +57,8 @@ async function main() {
|
|
|
57
57
|
default:
|
|
58
58
|
// Pass through arbitrary commands to the agent daemon
|
|
59
59
|
// e.g. "agent-browser open https://google.com"
|
|
60
|
-
await agent.command(...filteredArgs);
|
|
60
|
+
const result = await agent.command(...filteredArgs);
|
|
61
|
+
console.log(result);
|
|
61
62
|
break;
|
|
62
63
|
}
|
|
63
64
|
} catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kritchoff/agent-browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Headless browser automation CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
"bin",
|
|
11
11
|
"scripts",
|
|
12
12
|
"skills",
|
|
13
|
-
"sdk.sh",
|
|
14
|
-
"start.sh",
|
|
15
13
|
"docker-compose.sdk.yml"
|
|
16
14
|
],
|
|
17
15
|
"bin": {
|
package/README.sdk.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# @wootzapp/agent-browser SDK
|
|
2
|
-
|
|
3
|
-
The official Node.js SDK for controlling the WootzApp Agent Browser environment.
|
|
4
|
-
|
|
5
|
-
This SDK provides a **Real Android Browser** (WootzApp) wrapped in a Docker container, controlled by a high-speed Playwright daemon. It is specifically designed for AI Agents to navigate the mobile web, bypassing bot detection, and generating LLM-friendly semantic trees (AXTree).
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
|
-
- **Real Mobile Environment**: Full Android 14 OS with Touch Events and Mobile Viewports.
|
|
10
|
-
- **Zero-Config Setup**: The SDK automatically downloads and orchestrates the required Docker containers.
|
|
11
|
-
- **Hyper-Speed Warm Boots**: Uses advanced VDI Volume Mounting to boot the environment in **< 5 seconds** after the first run.
|
|
12
|
-
- **Fast Resets**: Cleans the browser state via Android userspace reboot in **~15 seconds**.
|
|
13
|
-
- **Playwright Parity**: Control the mobile browser using standard Playwright commands (`click`, `type`, `waitForSelector`).
|
|
14
|
-
- **Semantic AXTree**: Built-in `snapshot()` method generates a clean, text-based UI tree optimized for LLM reasoning.
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Prerequisites
|
|
19
|
-
|
|
20
|
-
1. **Docker Engine**: Must be installed and running.
|
|
21
|
-
- *Linux Users*: Ensure your user is in the `docker` group (`sudo usermod -aG docker $USER`).
|
|
22
|
-
2. **Node.js**: v18+ is required.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Installation
|
|
27
|
-
|
|
28
|
-
Install the SDK in your project:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npm install @kritchoff/agent-browser
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
*(Optional but recommended)* Install `tsx` to run TypeScript files natively:
|
|
35
|
-
```bash
|
|
36
|
-
npm install -D tsx
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Quick Start Guide
|
|
42
|
-
|
|
43
|
-
Create a file named `agent.ts`:
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
import { WootzAgent } from '@kritchoff/agent-browser';
|
|
47
|
-
|
|
48
|
-
async function main() {
|
|
49
|
-
// 1. Initialize the controller
|
|
50
|
-
const agent = new WootzAgent();
|
|
51
|
-
|
|
52
|
-
console.log('๐ Booting Environment...');
|
|
53
|
-
// First run: Downloads 3GB image and cold boots (~90s).
|
|
54
|
-
// Next run: Instant Hyper-Speed Warm Boot (~5s).
|
|
55
|
-
await agent.start();
|
|
56
|
-
|
|
57
|
-
console.log('๐ Navigating to Google...');
|
|
58
|
-
await agent.navigate('https://google.com');
|
|
59
|
-
|
|
60
|
-
console.log('๐ธ Capturing Semantic Tree for LLM...');
|
|
61
|
-
const uiTree = await agent.snapshot();
|
|
62
|
-
console.log(uiTree);
|
|
63
|
-
|
|
64
|
-
console.log('โจ๏ธ Typing and Searching...');
|
|
65
|
-
await agent.type('textarea[name="q"]', 'WootzApp AI');
|
|
66
|
-
await agent.press('Enter');
|
|
67
|
-
|
|
68
|
-
console.log('๐งน Fast Reset for next task...');
|
|
69
|
-
// Wipes all tabs, cookies, and cache in ~15s
|
|
70
|
-
await agent.reset();
|
|
71
|
-
|
|
72
|
-
console.log('๐ Shutting down...');
|
|
73
|
-
// Completely destroys containers and releases ports
|
|
74
|
-
await agent.stop();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
main().catch(console.error);
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Run your agent:
|
|
81
|
-
```bash
|
|
82
|
-
npx tsx agent.ts
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## CLI Usage (Global Install)
|
|
88
|
-
|
|
89
|
-
You can also use the SDK directly from your terminal to debug or control the browser manually.
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
npm install -g @kritchoff/agent-browser
|
|
93
|
-
|
|
94
|
-
# Start the environment
|
|
95
|
-
agent-browser start
|
|
96
|
-
|
|
97
|
-
# Run commands
|
|
98
|
-
agent-browser navigate https://news.ycombinator.com
|
|
99
|
-
agent-browser click ".titleline a"
|
|
100
|
-
agent-browser snapshot
|
|
101
|
-
|
|
102
|
-
# Clean the browser
|
|
103
|
-
agent-browser reset
|
|
104
|
-
|
|
105
|
-
# Stop
|
|
106
|
-
agent-browser stop
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## Troubleshooting
|
|
112
|
-
|
|
113
|
-
### `Error: Failed to connect to agent daemon (ECONNREFUSED)`
|
|
114
|
-
- **Cause**: The container failed to bind port `32001` to your host machine.
|
|
115
|
-
- **Fix**: Run `agent.stop()` or `docker rm -f $(docker ps -aq)` to clear old/stuck containers, then run `agent.start()` again. The SDK has built-in self-healing, but a manual hard reset always works.
|
|
116
|
-
|
|
117
|
-
### `net::ERR_NAME_NOT_RESOLVED`
|
|
118
|
-
- **Cause**: The Android Emulator temporarily lost its internet connection after a Warm Boot.
|
|
119
|
-
- **Fix**: The SDK automatically toggles Airplane Mode to fix this, but if it persists, ensure your host machine has a stable internet connection before starting the agent.
|
|
120
|
-
|
|
121
|
-
### `Selector "..." matched X elements (Strict Mode Violation)`
|
|
122
|
-
- **Cause**: Playwright requires selectors to point to exactly one element.
|
|
123
|
-
- **Fix**: Use more specific selectors, or use Playwright's `>> nth=0` pseudo-selector to pick the first match (e.g., `agent.click('a >> nth=0')`).
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
## Next Steps
|
|
128
|
-
|
|
129
|
-
For a complete list of all available commands (clicking, typing, tabbing, network interception), please read the [COMMANDS.md](./COMMANDS.md) file.
|
package/scripts/fast_reset.sh
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Fast Android environment reset using userspace reboot.
|
|
3
|
-
#
|
|
4
|
-
# This script resets the Android emulator state much faster (~15s) than
|
|
5
|
-
# a full container restart (~60s). It uses 'adb reboot userspace' to
|
|
6
|
-
# restart the Android framework while keeping the kernel running.
|
|
7
|
-
#
|
|
8
|
-
# Usage:
|
|
9
|
-
# ./scripts/fast_reset.sh
|
|
10
|
-
|
|
11
|
-
set -e
|
|
12
|
-
|
|
13
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
-
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
15
|
-
|
|
16
|
-
cd "$PROJECT_DIR"
|
|
17
|
-
|
|
18
|
-
# Colors for output
|
|
19
|
-
RED='\033[0;31m'
|
|
20
|
-
GREEN='\033[0;32m'
|
|
21
|
-
YELLOW='\033[1;33m'
|
|
22
|
-
BLUE='\033[0;34m'
|
|
23
|
-
NC='\033[0m' # No Color
|
|
24
|
-
|
|
25
|
-
log_info() {
|
|
26
|
-
echo -e "${BLUE}[INFO]${NC} $1"
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
log_success() {
|
|
30
|
-
echo -e "${GREEN}[OK]${NC} $1"
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
log_warn() {
|
|
34
|
-
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
log_error() {
|
|
38
|
-
echo -e "${RED}[ERROR]${NC} $1"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
# Respect COMPOSE_FILE from environment, or auto-detect
|
|
42
|
-
if [ -z "$COMPOSE_FILE" ]; then
|
|
43
|
-
if [ -f "$PROJECT_DIR/docker-compose.sdk.yml" ]; then
|
|
44
|
-
COMPOSE_FILE="$PROJECT_DIR/docker-compose.sdk.yml"
|
|
45
|
-
else
|
|
46
|
-
COMPOSE_FILE="$PROJECT_DIR/docker-compose.prod.yml"
|
|
47
|
-
fi
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# Detect container
|
|
51
|
-
CONTAINER=$(docker compose -f "$COMPOSE_FILE" ps -q android-service)
|
|
52
|
-
if [ -z "$CONTAINER" ]; then
|
|
53
|
-
log_error "android-service container not running."
|
|
54
|
-
exit 1
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
ADB_CMD="docker exec $CONTAINER adb"
|
|
58
|
-
|
|
59
|
-
log_info "Initiating fast reset (userspace reboot)..."
|
|
60
|
-
|
|
61
|
-
# 1. Trigger userspace reboot
|
|
62
|
-
# This command returns immediately and the device goes offline
|
|
63
|
-
$ADB_CMD shell reboot userspace || true
|
|
64
|
-
|
|
65
|
-
# 2. Wait for device to come back online
|
|
66
|
-
log_info "Waiting for device to come online..."
|
|
67
|
-
start_time=$(date +%s)
|
|
68
|
-
timeout=30
|
|
69
|
-
|
|
70
|
-
while true; do
|
|
71
|
-
current_time=$(date +%s)
|
|
72
|
-
elapsed=$((current_time - start_time))
|
|
73
|
-
|
|
74
|
-
if [ $elapsed -gt $timeout ]; then
|
|
75
|
-
log_error "Timeout waiting for device after ${timeout}s"
|
|
76
|
-
exit 1
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
# Check if device is visible to ADB and state is 'device'
|
|
80
|
-
if $ADB_CMD get-state 2>/dev/null | grep -q "device"; then
|
|
81
|
-
# Verify shell is responsive
|
|
82
|
-
if $ADB_CMD shell echo ok 2>/dev/null | grep -q "ok"; then
|
|
83
|
-
break
|
|
84
|
-
fi
|
|
85
|
-
fi
|
|
86
|
-
|
|
87
|
-
sleep 1
|
|
88
|
-
done
|
|
89
|
-
|
|
90
|
-
log_success "Device online (${elapsed}s)"
|
|
91
|
-
|
|
92
|
-
# 3. Wait for CDP (Chrome DevTools Protocol)
|
|
93
|
-
log_info "Waiting for browser CDP..."
|
|
94
|
-
cdp_timeout=30
|
|
95
|
-
cdp_start_time=$(date +%s)
|
|
96
|
-
|
|
97
|
-
while true; do
|
|
98
|
-
current_time=$(date +%s)
|
|
99
|
-
elapsed=$((current_time - cdp_start_time))
|
|
100
|
-
|
|
101
|
-
if [ $elapsed -gt $cdp_timeout ]; then
|
|
102
|
-
log_warn "Timeout waiting for CDP. Browser might not have autostarted."
|
|
103
|
-
log_info "Attempting to start browser manually..."
|
|
104
|
-
$ADB_CMD shell am start -n com.wootzapp.web/com.aspect.chromium.ChromiumMain -a android.intent.action.VIEW -d 'about:blank'
|
|
105
|
-
sleep 2
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
# Check CDP version endpoint
|
|
109
|
-
if docker exec $CONTAINER curl -s --connect-timeout 2 http://localhost:9224/json/version >/dev/null; then
|
|
110
|
-
break
|
|
111
|
-
fi
|
|
112
|
-
|
|
113
|
-
sleep 1
|
|
114
|
-
done
|
|
115
|
-
|
|
116
|
-
log_success "Browser CDP ready"
|
|
117
|
-
log_success "Fast reset complete!"
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Emulator snapshot import/export utility
|
|
3
|
-
#
|
|
4
|
-
# Manages Android emulator snapshots for agent-browser.
|
|
5
|
-
# Snapshots are stored in the emulator's AVD directory and can be
|
|
6
|
-
# exported as compressed tar.gz files for sharing or backup.
|
|
7
|
-
#
|
|
8
|
-
# Usage:
|
|
9
|
-
# ./scripts/snapshot_manager.sh export <name> <output.tar.gz>
|
|
10
|
-
# ./scripts/snapshot_manager.sh import <input.tar.gz> [name]
|
|
11
|
-
# ./scripts/snapshot_manager.sh list
|
|
12
|
-
# ./scripts/snapshot_manager.sh validate <name>
|
|
13
|
-
#
|
|
14
|
-
# Examples:
|
|
15
|
-
# # Export current snapshot for sharing
|
|
16
|
-
# ./scripts/snapshot_manager.sh export w8rl_clean ./my_snapshot.tar.gz
|
|
17
|
-
#
|
|
18
|
-
# # Import a snapshot from a file
|
|
19
|
-
# ./scripts/snapshot_manager.sh import ./my_snapshot.tar.gz
|
|
20
|
-
#
|
|
21
|
-
# # Import with a different name
|
|
22
|
-
# ./scripts/snapshot_manager.sh import ./my_snapshot.tar.gz imported_snapshot
|
|
23
|
-
#
|
|
24
|
-
# # List all available snapshots
|
|
25
|
-
# ./scripts/snapshot_manager.sh list
|
|
26
|
-
#
|
|
27
|
-
# # Validate a snapshot is not corrupt
|
|
28
|
-
# ./scripts/snapshot_manager.sh validate w8rl_clean
|
|
29
|
-
|
|
30
|
-
set -euo pipefail
|
|
31
|
-
|
|
32
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
33
|
-
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
34
|
-
|
|
35
|
-
cd "$PROJECT_DIR"
|
|
36
|
-
|
|
37
|
-
# Respect COMPOSE_FILE from environment
|
|
38
|
-
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.prod.yml}"
|
|
39
|
-
|
|
40
|
-
# Detect container name using docker compose
|
|
41
|
-
CONTAINER=$(docker compose -f "$COMPOSE_FILE" ps -q android-service)
|
|
42
|
-
if [ -z "$CONTAINER" ]; then
|
|
43
|
-
echo "Error: android-service container not running."
|
|
44
|
-
exit 1
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
AVD_NAME="${EMULATOR_NAME:-Pixel_6_API_34}"
|
|
48
|
-
SNAPSHOT_BASE="/root/.android/avd/${AVD_NAME}.avd/snapshots"
|
|
49
|
-
|
|
50
|
-
# Colors for output
|
|
51
|
-
RED='\033[0;31m'
|
|
52
|
-
GREEN='\033[0;32m'
|
|
53
|
-
YELLOW='\033[1;33m'
|
|
54
|
-
BLUE='\033[0;34m'
|
|
55
|
-
NC='\033[0m' # No Color
|
|
56
|
-
|
|
57
|
-
log_info() {
|
|
58
|
-
echo -e "${BLUE}[INFO]${NC} $1"
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
log_success() {
|
|
62
|
-
echo -e "${GREEN}[OK]${NC} $1"
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
log_warn() {
|
|
66
|
-
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
log_error() {
|
|
70
|
-
echo -e "${RED}[ERROR]${NC} $1"
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
# Check if container is running
|
|
74
|
-
check_container() {
|
|
75
|
-
# Use docker inspect for reliable status check (avoids SIGPIPE with grep -q)
|
|
76
|
-
if [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null)" != "true" ]; then
|
|
77
|
-
log_error "Container '${CONTAINER}' is not running"
|
|
78
|
-
log_info "Start it with: docker compose -f ${COMPOSE_FILE} up -d android-service"
|
|
79
|
-
exit 1
|
|
80
|
-
fi
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
# Validate a snapshot has all required files and is not corrupt
|
|
84
|
-
validate_snapshot_internal() {
|
|
85
|
-
local name="$1"
|
|
86
|
-
local snapshot_dir="${SNAPSHOT_BASE}/${name}"
|
|
87
|
-
local valid=true
|
|
88
|
-
|
|
89
|
-
log_info "Validating snapshot '${name}'..."
|
|
90
|
-
|
|
91
|
-
for file in ram.bin snapshot.pb hardware.ini; do
|
|
92
|
-
if docker exec "$CONTAINER" test -f "${snapshot_dir}/${file}"; then
|
|
93
|
-
local size
|
|
94
|
-
size=$(docker exec "$CONTAINER" stat -c%s "${snapshot_dir}/${file}")
|
|
95
|
-
echo -e " ${GREEN}โ${NC} ${file}: ${size} bytes"
|
|
96
|
-
else
|
|
97
|
-
echo -e " ${RED}โ${NC} ${file}: MISSING"
|
|
98
|
-
valid=false
|
|
99
|
-
fi
|
|
100
|
-
done
|
|
101
|
-
|
|
102
|
-
# Check ram.bin size (must be >= 1MB)
|
|
103
|
-
if docker exec "$CONTAINER" test -f "${snapshot_dir}/ram.bin"; then
|
|
104
|
-
local ram_size
|
|
105
|
-
ram_size=$(docker exec "$CONTAINER" stat -c%s "${snapshot_dir}/ram.bin")
|
|
106
|
-
if [ "$ram_size" -lt 1000000 ]; then
|
|
107
|
-
echo -e " ${RED}โ${NC} ram.bin too small (${ram_size} bytes, expected >= 1MB)"
|
|
108
|
-
valid=false
|
|
109
|
-
fi
|
|
110
|
-
fi
|
|
111
|
-
|
|
112
|
-
if [ "$valid" = true ]; then
|
|
113
|
-
log_success "Snapshot validation passed"
|
|
114
|
-
return 0
|
|
115
|
-
else
|
|
116
|
-
log_error "Snapshot validation failed"
|
|
117
|
-
return 1
|
|
118
|
-
fi
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
# Export a snapshot to a tar.gz file
|
|
122
|
-
cmd_export() {
|
|
123
|
-
local name="${1:?Usage: $0 export <name> <output.tar.gz>}"
|
|
124
|
-
local output="${2:?Usage: $0 export <name> <output.tar.gz>}"
|
|
125
|
-
|
|
126
|
-
check_container
|
|
127
|
-
|
|
128
|
-
# Validate snapshot exists
|
|
129
|
-
if ! docker exec "$CONTAINER" test -d "${SNAPSHOT_BASE}/${name}"; then
|
|
130
|
-
log_error "Snapshot '${name}' does not exist"
|
|
131
|
-
exit 1
|
|
132
|
-
fi
|
|
133
|
-
|
|
134
|
-
log_info "Exporting snapshot '${name}' to ${output}..."
|
|
135
|
-
|
|
136
|
-
# Validate before export
|
|
137
|
-
if ! validate_snapshot_internal "$name"; then
|
|
138
|
-
log_error "Cannot export invalid snapshot"
|
|
139
|
-
exit 1
|
|
140
|
-
fi
|
|
141
|
-
|
|
142
|
-
# Create tar.gz from snapshot directory
|
|
143
|
-
docker exec "$CONTAINER" tar -czf - -C "$SNAPSHOT_BASE" "$name" > "$output"
|
|
144
|
-
|
|
145
|
-
local size
|
|
146
|
-
size=$(ls -lh "$output" | awk '{print $5}')
|
|
147
|
-
log_success "Exported: ${output} (${size})"
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
# Import a snapshot from a tar.gz file
|
|
151
|
-
cmd_import() {
|
|
152
|
-
local input="${1:?Usage: $0 import <input.tar.gz> [name]}"
|
|
153
|
-
|
|
154
|
-
# Extract original name from tar
|
|
155
|
-
# Disable pipefail to avoid SIGPIPE from tar | head
|
|
156
|
-
set +o pipefail
|
|
157
|
-
local original_name
|
|
158
|
-
original_name=$(tar -tzf "$input" 2>/dev/null | head -1 | cut -d'/' -f1)
|
|
159
|
-
set -o pipefail
|
|
160
|
-
|
|
161
|
-
local name="${2:-$original_name}"
|
|
162
|
-
|
|
163
|
-
if [ -z "$name" ]; then
|
|
164
|
-
log_error "Could not determine snapshot name from archive"
|
|
165
|
-
exit 1
|
|
166
|
-
fi
|
|
167
|
-
|
|
168
|
-
check_container
|
|
169
|
-
|
|
170
|
-
log_info "Importing snapshot from ${input} as '${name}'..."
|
|
171
|
-
|
|
172
|
-
# Ensure snapshot directory exists
|
|
173
|
-
docker exec "$CONTAINER" mkdir -p "$SNAPSHOT_BASE"
|
|
174
|
-
|
|
175
|
-
# Remove existing snapshot if present
|
|
176
|
-
if docker exec "$CONTAINER" test -d "${SNAPSHOT_BASE}/${name}"; then
|
|
177
|
-
log_warn "Removing existing snapshot '${name}'..."
|
|
178
|
-
docker exec "$CONTAINER" rm -rf "${SNAPSHOT_BASE}/${name}"
|
|
179
|
-
fi
|
|
180
|
-
|
|
181
|
-
# Extract snapshot into container
|
|
182
|
-
# Use 'docker cp' to avoid stdin pipe issues
|
|
183
|
-
log_info "Copying archive to container..."
|
|
184
|
-
docker cp "$input" "$CONTAINER:/tmp/import_snapshot.tar.gz"
|
|
185
|
-
|
|
186
|
-
log_info "Extracting archive..."
|
|
187
|
-
if ! docker exec "$CONTAINER" tar -xzf /tmp/import_snapshot.tar.gz -C "$SNAPSHOT_BASE"; then
|
|
188
|
-
log_error "Failed to extract snapshot archive"
|
|
189
|
-
docker exec "$CONTAINER" rm -f /tmp/import_snapshot.tar.gz
|
|
190
|
-
exit 1
|
|
191
|
-
fi
|
|
192
|
-
|
|
193
|
-
# Clean up
|
|
194
|
-
docker exec "$CONTAINER" rm -f /tmp/import_snapshot.tar.gz
|
|
195
|
-
|
|
196
|
-
# If renaming, move the extracted directory
|
|
197
|
-
if [ "$name" != "$original_name" ]; then
|
|
198
|
-
docker exec "$CONTAINER" mv "${SNAPSHOT_BASE}/${original_name}" "${SNAPSHOT_BASE}/${name}"
|
|
199
|
-
fi
|
|
200
|
-
|
|
201
|
-
# Validate after import
|
|
202
|
-
if validate_snapshot_internal "$name"; then
|
|
203
|
-
log_success "Import completed successfully"
|
|
204
|
-
docker exec "$CONTAINER" ls -lh "${SNAPSHOT_BASE}/${name}/"
|
|
205
|
-
else
|
|
206
|
-
log_error "Import failed - snapshot validation failed"
|
|
207
|
-
exit 1
|
|
208
|
-
fi
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
# List all snapshots
|
|
212
|
-
cmd_list() {
|
|
213
|
-
check_container
|
|
214
|
-
|
|
215
|
-
log_info "Available snapshots in ${CONTAINER}:"
|
|
216
|
-
echo ""
|
|
217
|
-
|
|
218
|
-
if docker exec "$CONTAINER" test -d "$SNAPSHOT_BASE"; then
|
|
219
|
-
docker exec "$CONTAINER" ls -la "$SNAPSHOT_BASE" 2>/dev/null | tail -n +2 || echo " (none)"
|
|
220
|
-
|
|
221
|
-
echo ""
|
|
222
|
-
log_info "Snapshot details:"
|
|
223
|
-
for snapshot in $(docker exec "$CONTAINER" ls "$SNAPSHOT_BASE" 2>/dev/null); do
|
|
224
|
-
local snapshot_dir="${SNAPSHOT_BASE}/${snapshot}"
|
|
225
|
-
if docker exec "$CONTAINER" test -d "$snapshot_dir"; then
|
|
226
|
-
local ram_size
|
|
227
|
-
ram_size=$(docker exec "$CONTAINER" stat -c%s "${snapshot_dir}/ram.bin" 2>/dev/null || echo "0")
|
|
228
|
-
local ram_mb=$((ram_size / 1024 / 1024))
|
|
229
|
-
echo " ${snapshot}: ${ram_mb}MB RAM"
|
|
230
|
-
fi
|
|
231
|
-
done
|
|
232
|
-
else
|
|
233
|
-
echo " No snapshots found (snapshot directory does not exist)"
|
|
234
|
-
fi
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
# Validate a snapshot
|
|
238
|
-
cmd_validate() {
|
|
239
|
-
local name="${1:?Usage: $0 validate <name>}"
|
|
240
|
-
|
|
241
|
-
check_container
|
|
242
|
-
|
|
243
|
-
if ! docker exec "$CONTAINER" test -d "${SNAPSHOT_BASE}/${name}"; then
|
|
244
|
-
log_error "Snapshot '${name}' does not exist"
|
|
245
|
-
exit 1
|
|
246
|
-
fi
|
|
247
|
-
|
|
248
|
-
validate_snapshot_internal "$name"
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
# Show usage
|
|
252
|
-
usage() {
|
|
253
|
-
echo "Usage: $0 {export|import|list|validate} [args...]"
|
|
254
|
-
echo ""
|
|
255
|
-
echo "Commands:"
|
|
256
|
-
echo " export <name> <output.tar.gz> Export a snapshot to a file"
|
|
257
|
-
echo " import <input.tar.gz> [name] Import a snapshot from a file"
|
|
258
|
-
echo " list List all available snapshots"
|
|
259
|
-
echo " validate <name> Validate a snapshot is not corrupt"
|
|
260
|
-
echo ""
|
|
261
|
-
echo "Examples:"
|
|
262
|
-
echo " $0 export w8rl_clean ./my_snapshot.tar.gz"
|
|
263
|
-
echo " $0 import ./my_snapshot.tar.gz"
|
|
264
|
-
echo " $0 import ./my_snapshot.tar.gz custom_name"
|
|
265
|
-
echo " $0 list"
|
|
266
|
-
echo " $0 validate w8rl_clean"
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
# Main command dispatch
|
|
270
|
-
case "${1:-}" in
|
|
271
|
-
export)
|
|
272
|
-
shift
|
|
273
|
-
cmd_export "$@"
|
|
274
|
-
;;
|
|
275
|
-
import)
|
|
276
|
-
shift
|
|
277
|
-
cmd_import "$@"
|
|
278
|
-
;;
|
|
279
|
-
list)
|
|
280
|
-
cmd_list
|
|
281
|
-
;;
|
|
282
|
-
validate)
|
|
283
|
-
shift
|
|
284
|
-
cmd_validate "$@"
|
|
285
|
-
;;
|
|
286
|
-
-h|--help|help)
|
|
287
|
-
usage
|
|
288
|
-
;;
|
|
289
|
-
*)
|
|
290
|
-
usage
|
|
291
|
-
exit 1
|
|
292
|
-
;;
|
|
293
|
-
esac
|
package/scripts/vaccine-run
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
exec 2>&1
|
|
3
|
-
|
|
4
|
-
echo "[vaccine] Waiting for emulator..."
|
|
5
|
-
adb wait-for-device
|
|
6
|
-
|
|
7
|
-
echo "[vaccine] Waiting for boot completion..."
|
|
8
|
-
while [[ -z $(adb shell getprop sys.boot_completed) ]]; do sleep 1; done
|
|
9
|
-
|
|
10
|
-
echo "[vaccine] Waiting for Package Manager..."
|
|
11
|
-
while ! adb shell pm list packages > /dev/null 2>&1; do sleep 1; done
|
|
12
|
-
|
|
13
|
-
echo "[vaccine] Applying permissions to org.chromium.chrome (WootzApp)..."
|
|
14
|
-
adb shell pm grant org.chromium.chrome android.permission.ACCESS_FINE_LOCATION || true
|
|
15
|
-
adb shell pm grant org.chromium.chrome android.permission.CAMERA || true
|
|
16
|
-
adb shell pm grant org.chromium.chrome android.permission.RECORD_AUDIO || true
|
|
17
|
-
|
|
18
|
-
# Fallback for standard Chrome if present
|
|
19
|
-
adb shell pm grant com.android.chrome android.permission.ACCESS_FINE_LOCATION || true
|
|
20
|
-
adb shell pm grant com.android.chrome android.permission.CAMERA || true
|
|
21
|
-
adb shell pm grant com.android.chrome android.permission.RECORD_AUDIO || true
|
|
22
|
-
|
|
23
|
-
echo "[vaccine] Permissions granted."
|
|
24
|
-
|
|
25
|
-
# Prevent restart
|
|
26
|
-
touch /etc/services.d/vaccine/down
|