fireclaw 0.1.0 → 0.1.2
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 +14 -43
- package/bin/fireclaw +24 -22
- package/bin/vm-common.sh +21 -5
- package/bin/vm-ctl +64 -19
- package/bin/vm-provision +54 -0
- package/bin/vm-setup +4 -3
- package/package.json +1 -1
- package/scripts/provision-guest.sh +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# fireclaw
|
|
1
|
+
# fireclaw
|
|
2
2
|
|
|
3
3
|
Run [OpenClaw](https://github.com/openclaw/openclaw) instances inside Firecracker microVMs, each fully isolated with its own filesystem, network, and process tree.
|
|
4
4
|
|
|
@@ -19,7 +19,7 @@ This repo is a minimal control plane that wires up Firecracker VM lifecycle, net
|
|
|
19
19
|
Host
|
|
20
20
|
├── systemd: firecracker-vmdemo-<id>.service ← runs the VM
|
|
21
21
|
├── systemd: vmdemo-proxy-<id>.service ← socat: localhost:<port> → VM:18789
|
|
22
|
-
├── bridge:
|
|
22
|
+
├── bridge: fc-br0 (172.16.0.0/24) ← shared bridge for all VMs
|
|
23
23
|
│
|
|
24
24
|
└── Firecracker VM (172.16.0.x)
|
|
25
25
|
├── cloud-init: ubuntu user, SSH key, Docker install
|
|
@@ -32,11 +32,11 @@ Host
|
|
|
32
32
|
|
|
33
33
|
2. **`provision-guest.sh`** runs inside the VM as root: waits for cloud-init/apt locks, expands the guest ext4 filesystem (`resize2fs`), configures Docker for Firecracker (`iptables=false`, `ip6tables=false`, `bridge=none`), pulls the OpenClaw image, runs the OpenClaw CLI (`doctor --fix` included), installs Playwright Chromium, writes browser path + health-check script, then creates and starts the guest systemd service.
|
|
34
34
|
|
|
35
|
-
3. **`fireclaw`** manages lifecycle after setup: start/stop/restart VMs, tail logs (guest or host side), open an SSH shell, show status, or destroy an instance.
|
|
35
|
+
3. **`fireclaw`** manages the lifecycle after setup: start/stop/restart VMs, tail logs (guest or host side), open an SSH shell, show status, or destroy an instance.
|
|
36
36
|
|
|
37
37
|
All state lives in two places:
|
|
38
|
-
- **
|
|
39
|
-
- **
|
|
38
|
+
- **Instance state** `/var/lib/fireclaw/.vm-<id>/` — env file, token, provision vars
|
|
39
|
+
- **VM runtime** `/srv/firecracker/vm-demo/<id>/` — VM images, Firecracker config, logs
|
|
40
40
|
|
|
41
41
|
## Prerequisites
|
|
42
42
|
|
|
@@ -48,17 +48,15 @@ All state lives in two places:
|
|
|
48
48
|
|
|
49
49
|
Set `BASE_IMAGES_DIR` or pass `--base-kernel`/`--base-rootfs`/`--base-initrd` to point at your images.
|
|
50
50
|
|
|
51
|
-
##
|
|
51
|
+
## Install
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
cd fireclaw-vm-demo
|
|
54
|
+
npm install -g fireclaw
|
|
55
|
+
```
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
sudo install -m 0755 ./bin/fireclaw /usr/local/bin/fireclaw
|
|
57
|
+
## Setup
|
|
60
58
|
|
|
61
|
-
|
|
59
|
+
```bash
|
|
62
60
|
sudo fireclaw setup \
|
|
63
61
|
--instance my-bot \
|
|
64
62
|
--telegram-token "<your-bot-token>" \
|
|
@@ -67,34 +65,7 @@ sudo fireclaw setup \
|
|
|
67
65
|
--anthropic-api-key "<key>"
|
|
68
66
|
```
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
# Global install
|
|
74
|
-
sudo npm install -g fireclaw
|
|
75
|
-
|
|
76
|
-
# Then use:
|
|
77
|
-
sudo fireclaw --help
|
|
78
|
-
sudo fireclaw list
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Publish To npm
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
# 1. Login/check auth
|
|
85
|
-
npm login
|
|
86
|
-
npm whoami
|
|
87
|
-
|
|
88
|
-
# 2. Validate package contents and scripts
|
|
89
|
-
npm test
|
|
90
|
-
npm pack --dry-run
|
|
91
|
-
|
|
92
|
-
# 3. Bump version and publish
|
|
93
|
-
npm version patch
|
|
94
|
-
npm publish
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
If `npm publish` fails because the package name is unavailable, change `"name"` in `package.json` (for example to a scoped package like `@<your-scope>/fireclaw`) and publish again.
|
|
68
|
+
This will generate an SSH keypair (if needed), copy + configure the rootfs, boot the VM via systemd, wait for SSH, provision OpenClaw inside the guest, start the localhost proxy, and print the instance details (IP, port, token).
|
|
98
69
|
|
|
99
70
|
### Options
|
|
100
71
|
|
|
@@ -158,7 +129,7 @@ sudo fireclaw destroy my-bot --force
|
|
|
158
129
|
|
|
159
130
|
## Networking
|
|
160
131
|
|
|
161
|
-
Each VM gets a static IP on a bridge (`
|
|
132
|
+
Each VM gets a static IP on a bridge (`fc-br0`, `172.16.0.0/24`). The host acts as the gateway at `172.16.0.1` with NAT for outbound traffic. A `socat` proxy on the host forwards `127.0.0.1:<HOST_PORT>` to the VM's gateway port (`18789`), so the OpenClaw API is only reachable from localhost.
|
|
162
133
|
|
|
163
134
|
## Environment variables
|
|
164
135
|
|
|
@@ -166,10 +137,10 @@ All scripts respect these overrides:
|
|
|
166
137
|
|
|
167
138
|
| Variable | Default |
|
|
168
139
|
|----------|---------|
|
|
169
|
-
| `STATE_ROOT` |
|
|
140
|
+
| `STATE_ROOT` | `/var/lib/fireclaw` |
|
|
170
141
|
| `FC_ROOT` | `/srv/firecracker/vm-demo` |
|
|
171
142
|
| `BASE_PORT` | `18890` |
|
|
172
|
-
| `BRIDGE_NAME` | `
|
|
143
|
+
| `BRIDGE_NAME` | `fc-br0` |
|
|
173
144
|
| `BRIDGE_ADDR` | `172.16.0.1/24` |
|
|
174
145
|
| `SUBNET_CIDR` | `172.16.0.0/24` |
|
|
175
146
|
| `SSH_KEY_PATH` | `~/.ssh/vmdemo_vm` |
|
package/bin/fireclaw
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
|
5
5
|
|
|
6
6
|
usage() {
|
|
7
|
-
|
|
8
|
-
Usage: fireclaw <command> [args...]
|
|
7
|
+
local bold=$'\033[1m' dim=$'\033[2m' reset=$'\033[0m' cyan=$'\033[36m'
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
list List instances
|
|
13
|
-
status [id] Show instance status
|
|
14
|
-
start <id> Start instance
|
|
15
|
-
stop <id> Stop instance
|
|
16
|
-
restart <id> Restart instance
|
|
17
|
-
logs <id> [guest|host] Stream logs
|
|
18
|
-
shell <id> [command...] SSH shell or run command in VM
|
|
19
|
-
token <id> Show gateway token
|
|
20
|
-
destroy <id> [--force] Destroy instance
|
|
9
|
+
cat <<EOF
|
|
10
|
+
${bold}fireclaw${reset} — Firecracker microVM control plane
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
vm-setup <flags...> Pass through to vm-setup
|
|
25
|
-
vm-ctl <args...> Pass through to vm-ctl
|
|
12
|
+
${bold}USAGE${reset}
|
|
13
|
+
fireclaw <command> [args...]
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
15
|
+
${bold}COMMANDS${reset}
|
|
16
|
+
${cyan}setup${reset} <flags...> Create and provision a new VM instance
|
|
17
|
+
${cyan}list${reset} List all instances
|
|
18
|
+
${cyan}status${reset} [id] Show instance status (all or one)
|
|
19
|
+
${cyan}start${reset} <id> Start an instance
|
|
20
|
+
${cyan}stop${reset} <id> Stop an instance
|
|
21
|
+
${cyan}restart${reset} <id> Restart an instance
|
|
22
|
+
${cyan}logs${reset} <id> [guest|host] Stream logs
|
|
23
|
+
${cyan}shell${reset} <id> [command...] SSH into VM or run a command
|
|
24
|
+
${cyan}token${reset} <id> Print gateway token
|
|
25
|
+
${cyan}destroy${reset} <id> [--force] Destroy an instance
|
|
26
|
+
|
|
27
|
+
${bold}EXAMPLES${reset}
|
|
28
|
+
${dim}\$${reset} sudo fireclaw setup --instance my-bot --telegram-token <tok> --telegram-users <uid>
|
|
29
|
+
${dim}\$${reset} sudo fireclaw list
|
|
30
|
+
${dim}\$${reset} sudo fireclaw status my-bot
|
|
31
|
+
${dim}\$${reset} sudo fireclaw shell my-bot
|
|
32
|
+
${dim}\$${reset} sudo fireclaw logs my-bot guest
|
|
31
33
|
EOF
|
|
32
34
|
}
|
|
33
35
|
|
package/bin/vm-common.sh
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
5
|
-
STATE_ROOT="${STATE_ROOT:-$REPO_ROOT/.vm-demo}"
|
|
4
|
+
REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)}"
|
|
6
5
|
FC_ROOT="${FC_ROOT:-/srv/firecracker/vm-demo}"
|
|
6
|
+
STATE_ROOT="${STATE_ROOT:-/var/lib/fireclaw}"
|
|
7
7
|
BASE_PORT="${BASE_PORT:-18890}"
|
|
8
8
|
|
|
9
|
-
BRIDGE_NAME="${BRIDGE_NAME:-
|
|
9
|
+
BRIDGE_NAME="${BRIDGE_NAME:-fc-br0}"
|
|
10
10
|
BRIDGE_ADDR="${BRIDGE_ADDR:-172.16.0.1/24}"
|
|
11
11
|
SUBNET_CIDR="${SUBNET_CIDR:-172.16.0.0/24}"
|
|
12
12
|
|
|
13
13
|
OPENCLAW_IMAGE_DEFAULT="${OPENCLAW_IMAGE_DEFAULT:-ghcr.io/openclaw/openclaw:latest}"
|
|
14
|
-
SSH_KEY_PATH="${SSH_KEY_PATH
|
|
14
|
+
SSH_KEY_PATH="${SSH_KEY_PATH:-/home/ubuntu/.ssh/vmdemo_vm}"
|
|
15
15
|
|
|
16
16
|
log() { printf '==> %s\n' "$*"; }
|
|
17
17
|
warn() { printf 'Warning: %s\n' "$*" >&2; }
|
|
@@ -96,14 +96,30 @@ wait_for_ssh() {
|
|
|
96
96
|
local ip="$1"
|
|
97
97
|
local key="${2:-$SSH_KEY_PATH}"
|
|
98
98
|
local retries="${3:-120}"
|
|
99
|
+
|
|
100
|
+
if [[ ! -r "$key" ]]; then
|
|
101
|
+
if [[ $EUID -ne 0 ]]; then
|
|
102
|
+
die "Cannot read SSH key: $key (try: sudo fireclaw ...)"
|
|
103
|
+
else
|
|
104
|
+
die "SSH key not found: $key"
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
local vm_svc vm_state
|
|
109
|
+
vm_svc="$(vm_service "${INSTANCE_ID:-}")"
|
|
110
|
+
|
|
99
111
|
local i
|
|
100
112
|
for ((i=1; i<=retries; i++)); do
|
|
101
113
|
if ssh -i "$key" -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -o ConnectTimeout=3 "ubuntu@$ip" true >/dev/null 2>&1; then
|
|
102
114
|
return 0
|
|
103
115
|
fi
|
|
116
|
+
vm_state="$(systemctl is-active "$vm_svc" 2>/dev/null)" || vm_state="inactive"
|
|
117
|
+
if [[ "$vm_state" != "active" ]]; then
|
|
118
|
+
die "VM is not running ($(printf '\033[31m%s\033[0m' "$vm_state")). Start it with: sudo fireclaw start ${INSTANCE_ID:-<id>}"
|
|
119
|
+
fi
|
|
104
120
|
sleep 2
|
|
105
121
|
done
|
|
106
|
-
|
|
122
|
+
die "VM is running but SSH did not become reachable at ubuntu@$ip after $((retries * 2))s"
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
check_guest_health() {
|
package/bin/vm-ctl
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
|
5
5
|
source "$SCRIPT_DIR/vm-common.sh"
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
CMD_NAME="${VM_CTL_CMD_NAME:-fireclaw}"
|
|
7
8
|
|
|
8
9
|
usage() {
|
|
9
10
|
cat <<EOF
|
|
@@ -28,11 +29,45 @@ ssh_run() {
|
|
|
28
29
|
ssh -i "$key" -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null "ubuntu@$ip" "$@"
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
_color() {
|
|
33
|
+
local val="$1"
|
|
34
|
+
local green=$'\033[32m' red=$'\033[31m' yellow=$'\033[33m' reset=$'\033[0m'
|
|
35
|
+
case "$val" in
|
|
36
|
+
active|up) printf '%s%s%s' "$green" "$val" "$reset" ;;
|
|
37
|
+
inactive|down) printf '%s%s%s' "$red" "$val" "$reset" ;;
|
|
38
|
+
failed) printf '%s%s%s' "$red" "$val" "$reset" ;;
|
|
39
|
+
*) printf '%s%s%s' "$yellow" "$val" "$reset" ;;
|
|
40
|
+
esac
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_print_status_table() {
|
|
44
|
+
local -a ids=() ips=() ports=() vms=() proxies=() healths=()
|
|
45
|
+
local id ip port vm proxy health
|
|
46
|
+
|
|
47
|
+
while IFS='|' read -r id ip port vm proxy health; do
|
|
48
|
+
ids+=("$id"); ips+=("$ip"); ports+=("$port")
|
|
49
|
+
vms+=("$vm"); proxies+=("$proxy"); healths+=("$health")
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
[[ ${#ids[@]} -gt 0 ]] || { echo "(no instances)"; return; }
|
|
53
|
+
|
|
54
|
+
local hdr=$'\033[1;37m' reset=$'\033[0m' dim=$'\033[2m'
|
|
55
|
+
printf "${hdr}%-14s %-14s %-7s %-10s %-10s %-8s${reset}\n" \
|
|
56
|
+
"INSTANCE" "IP" "PORT" "VM" "PROXY" "HEALTH"
|
|
57
|
+
printf "${dim}%-14s %-14s %-7s %-10s %-10s %-8s${reset}\n" \
|
|
58
|
+
"--------" "----------" "-----" "------" "-------" "------"
|
|
59
|
+
|
|
60
|
+
for i in "${!ids[@]}"; do
|
|
61
|
+
printf "%-14s %-14s %-7s %-10b %-10b %-8b\n" \
|
|
62
|
+
"${ids[$i]}" "${ips[$i]}" "${ports[$i]}" \
|
|
63
|
+
"$(_color "${vms[$i]}")" "$(_color "${proxies[$i]}")" "$(_color "${healths[$i]}")"
|
|
64
|
+
done
|
|
65
|
+
}
|
|
66
|
+
|
|
31
67
|
cmd_list() {
|
|
32
68
|
shopt -s nullglob
|
|
33
|
-
local
|
|
69
|
+
local rows=()
|
|
34
70
|
for d in "$STATE_ROOT"/.vm-*/; do
|
|
35
|
-
found="true"
|
|
36
71
|
local id
|
|
37
72
|
id="$(basename "$d" | sed 's/^\.vm-//')"
|
|
38
73
|
if [[ ! "$id" =~ ^[a-z0-9_-]+$ ]]; then
|
|
@@ -52,12 +87,12 @@ cmd_list() {
|
|
|
52
87
|
health="up"
|
|
53
88
|
fi
|
|
54
89
|
local vm_state proxy_state
|
|
55
|
-
vm_state="$(systemctl is-active "$(vm_service "$id")" 2>/dev/null ||
|
|
56
|
-
proxy_state="$(systemctl is-active "$(proxy_service "$id")" 2>/dev/null ||
|
|
57
|
-
|
|
90
|
+
vm_state="$(systemctl is-active "$(vm_service "$id")" 2>/dev/null)" || vm_state="inactive"
|
|
91
|
+
proxy_state="$(systemctl is-active "$(proxy_service "$id")" 2>/dev/null)" || proxy_state="inactive"
|
|
92
|
+
rows+=("${id}|${VM_IP}|${HOST_PORT}|${vm_state}|${proxy_state}|${health}")
|
|
58
93
|
done
|
|
59
94
|
shopt -u nullglob
|
|
60
|
-
|
|
95
|
+
printf '%s\n' "${rows[@]}" | _print_status_table
|
|
61
96
|
}
|
|
62
97
|
|
|
63
98
|
cmd_status_one() {
|
|
@@ -66,15 +101,15 @@ cmd_status_one() {
|
|
|
66
101
|
load_instance_env "$id"
|
|
67
102
|
local ssh_key="${SSH_KEY_PATH:-$HOME/.ssh/vmdemo_vm}"
|
|
68
103
|
local vm_state proxy_state health host_health guest_health guest
|
|
69
|
-
vm_state="$(systemctl is-active "$(vm_service "$id")" 2>/dev/null ||
|
|
70
|
-
proxy_state="$(systemctl is-active "$(proxy_service "$id")" 2>/dev/null ||
|
|
104
|
+
vm_state="$(systemctl is-active "$(vm_service "$id")" 2>/dev/null)" || vm_state="inactive"
|
|
105
|
+
proxy_state="$(systemctl is-active "$(proxy_service "$id")" 2>/dev/null)" || proxy_state="inactive"
|
|
71
106
|
health="down"
|
|
72
107
|
host_health="down"
|
|
73
108
|
guest_health="down"
|
|
74
109
|
curl -fsS "http://127.0.0.1:$HOST_PORT/health" >/dev/null 2>&1 && host_health="up"
|
|
75
110
|
guest="unknown"
|
|
76
111
|
if wait_for_ssh "$VM_IP" "$ssh_key" 1; then
|
|
77
|
-
guest="$(ssh_run "$VM_IP" "systemctl is-active openclaw-$id.service" 2>/dev/null ||
|
|
112
|
+
guest="$(ssh_run "$VM_IP" "systemctl is-active openclaw-$id.service" 2>/dev/null)" || guest="unknown"
|
|
78
113
|
if check_guest_health "$id" "$VM_IP" "$ssh_key"; then
|
|
79
114
|
guest_health="up"
|
|
80
115
|
fi
|
|
@@ -82,7 +117,17 @@ cmd_status_one() {
|
|
|
82
117
|
if [[ "$host_health" == "up" || "$guest_health" == "up" ]]; then
|
|
83
118
|
health="up"
|
|
84
119
|
fi
|
|
85
|
-
|
|
120
|
+
|
|
121
|
+
local bold=$'\033[1m' dim=$'\033[2m' reset=$'\033[0m'
|
|
122
|
+
printf "${bold}%s${reset}\n" "$id"
|
|
123
|
+
printf " %-16s %s\n" "IP" "$VM_IP"
|
|
124
|
+
printf " %-16s %s\n" "Proxy port" "$HOST_PORT"
|
|
125
|
+
printf " %-16s %b\n" "VM" "$(_color "$vm_state")"
|
|
126
|
+
printf " %-16s %b\n" "Proxy" "$(_color "$proxy_state")"
|
|
127
|
+
printf " %-16s %b\n" "Guest service" "$(_color "$guest")"
|
|
128
|
+
printf " %-16s %b\n" "Health" "$(_color "$health")"
|
|
129
|
+
printf " %-16s %b\n" " Host health" "$(_color "$host_health")"
|
|
130
|
+
printf " %-16s %b\n" " Guest health" "$(_color "$guest_health")"
|
|
86
131
|
}
|
|
87
132
|
|
|
88
133
|
cmd_status() {
|
|
@@ -196,13 +241,13 @@ shift || true
|
|
|
196
241
|
case "$cmd" in
|
|
197
242
|
list) cmd_list ;;
|
|
198
243
|
status) cmd_status "$@" ;;
|
|
199
|
-
start) [[ $# -eq 1 ]] || die "Usage:
|
|
200
|
-
stop) [[ $# -eq 1 ]] || die "Usage:
|
|
201
|
-
restart) [[ $# -eq 1 ]] || die "Usage:
|
|
202
|
-
logs) [[ $# -ge 1 ]] || die "Usage:
|
|
203
|
-
shell) [[ $# -ge 1 ]] || die "Usage:
|
|
204
|
-
token) [[ $# -eq 1 ]] || die "Usage:
|
|
205
|
-
destroy) [[ $# -ge 1 ]] || die "Usage:
|
|
244
|
+
start) [[ $# -eq 1 ]] || die "Usage: $CMD_NAME start <id>"; cmd_start "$1" ;;
|
|
245
|
+
stop) [[ $# -eq 1 ]] || die "Usage: $CMD_NAME stop <id>"; cmd_stop "$1" ;;
|
|
246
|
+
restart) [[ $# -eq 1 ]] || die "Usage: $CMD_NAME restart <id>"; cmd_restart "$1" ;;
|
|
247
|
+
logs) [[ $# -ge 1 ]] || die "Usage: $CMD_NAME logs <id> [guest|host]"; cmd_logs "$@" ;;
|
|
248
|
+
shell) [[ $# -ge 1 ]] || die "Usage: $CMD_NAME shell <id> [command...]"; id="$1"; shift; cmd_shell "$id" "$@" ;;
|
|
249
|
+
token) [[ $# -eq 1 ]] || die "Usage: $CMD_NAME token <id>"; cmd_token "$1" ;;
|
|
250
|
+
destroy) [[ $# -ge 1 ]] || die "Usage: $CMD_NAME destroy <id> [--force]"; cmd_destroy "$@" ;;
|
|
206
251
|
-h|--help|help) usage ;;
|
|
207
252
|
*) die "Unknown command: $cmd" ;;
|
|
208
253
|
esac
|
package/bin/vm-provision
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
|
5
|
+
source "$SCRIPT_DIR/vm-common.sh"
|
|
6
|
+
|
|
7
|
+
usage() {
|
|
8
|
+
cat <<EOF
|
|
9
|
+
Usage: fireclaw provision <instance>
|
|
10
|
+
EOF
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[[ $# -eq 1 ]] || { usage; exit 1; }
|
|
14
|
+
|
|
15
|
+
INSTANCE="$1"
|
|
16
|
+
validate_instance_id "$INSTANCE"
|
|
17
|
+
require_root
|
|
18
|
+
|
|
19
|
+
load_instance_env "$INSTANCE"
|
|
20
|
+
|
|
21
|
+
PROVISION_VARS="$(instance_dir "$INSTANCE")/provision.vars"
|
|
22
|
+
[[ -f "$PROVISION_VARS" ]] || die "Missing provision vars: $PROVISION_VARS"
|
|
23
|
+
[[ -f "$REPO_ROOT/scripts/provision-guest.sh" ]] || die "Missing: $REPO_ROOT/scripts/provision-guest.sh"
|
|
24
|
+
|
|
25
|
+
if ! wait_for_ssh "$VM_IP" "$SSH_KEY_PATH" 180; then
|
|
26
|
+
die "VM SSH unreachable. Start it first: fireclaw start $INSTANCE"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
scp -i "$SSH_KEY_PATH" -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null \
|
|
30
|
+
"$REPO_ROOT/scripts/provision-guest.sh" "ubuntu@$VM_IP:/tmp/provision-guest.sh"
|
|
31
|
+
scp -i "$SSH_KEY_PATH" -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null \
|
|
32
|
+
"$PROVISION_VARS" "ubuntu@$VM_IP:/tmp/provision.vars"
|
|
33
|
+
|
|
34
|
+
ssh -i "$SSH_KEY_PATH" -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null \
|
|
35
|
+
"ubuntu@$VM_IP" "sudo bash /tmp/provision-guest.sh /tmp/provision.vars"
|
|
36
|
+
|
|
37
|
+
systemctl enable --now "$(proxy_service "$INSTANCE")" >/dev/null 2>&1 || true
|
|
38
|
+
|
|
39
|
+
health_ok="false"
|
|
40
|
+
for _ in {1..30}; do
|
|
41
|
+
if curl -fsS "http://127.0.0.1:$HOST_PORT/health" >/dev/null 2>&1; then
|
|
42
|
+
health_ok="true"
|
|
43
|
+
break
|
|
44
|
+
fi
|
|
45
|
+
sleep 2
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
echo "✓ VM provisioning complete"
|
|
49
|
+
echo " Instance: $INSTANCE"
|
|
50
|
+
echo " VM IP: $VM_IP"
|
|
51
|
+
echo " Port: $HOST_PORT"
|
|
52
|
+
if [[ "$health_ok" != "true" ]]; then
|
|
53
|
+
echo " Health: pending (service may still be warming up)"
|
|
54
|
+
fi
|
package/bin/vm-setup
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
|
5
5
|
source "$SCRIPT_DIR/vm-common.sh"
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
CMD_NAME="${VM_SETUP_CMD_NAME:-fireclaw setup}"
|
|
7
8
|
|
|
8
9
|
usage() {
|
|
9
10
|
cat <<EOF
|
|
@@ -158,11 +159,11 @@ SSH_PUB_KEY="$(cat "${SSH_KEY_PATH}.pub")"
|
|
|
158
159
|
cat > "$fc_dir/config/user-data" <<EOF
|
|
159
160
|
#cloud-config
|
|
160
161
|
users:
|
|
161
|
-
- default
|
|
162
162
|
- name: ubuntu
|
|
163
163
|
groups: [sudo, docker]
|
|
164
164
|
shell: /bin/bash
|
|
165
165
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
|
166
|
+
lock_passwd: true
|
|
166
167
|
ssh_authorized_keys:
|
|
167
168
|
- $SSH_PUB_KEY
|
|
168
169
|
package_update: true
|
package/package.json
CHANGED
|
@@ -186,7 +186,7 @@ chmod 600 "$ENV_FILE"
|
|
|
186
186
|
docker pull "$OPENCLAW_IMAGE"
|
|
187
187
|
|
|
188
188
|
run_openclaw_cli() {
|
|
189
|
-
docker run --rm -i
|
|
189
|
+
docker run --rm -i \
|
|
190
190
|
--network host \
|
|
191
191
|
-e HOME=/home/node \
|
|
192
192
|
-e OPENCLAW_GATEWAY_TOKEN="$GATEWAY_TOKEN" \
|
|
@@ -263,7 +263,7 @@ EOF
|
|
|
263
263
|
fi
|
|
264
264
|
|
|
265
265
|
if [[ "${SKIP_BROWSER_INSTALL:-false}" != "true" ]]; then
|
|
266
|
-
docker run --rm
|
|
266
|
+
docker run --rm \
|
|
267
267
|
--network host \
|
|
268
268
|
-e PLAYWRIGHT_BROWSERS_PATH=/home/node/clawd/tools/.playwright \
|
|
269
269
|
-v "$TOOLS_DIR:/home/node/clawd/tools" \
|