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 CHANGED
@@ -1,4 +1,4 @@
1
- # fireclaw-vm-demo
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: fcbr0 (172.16.0.0/24) ← shared bridge for all VMs
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
- - **Repo-local** `.vm-demo/.vm-<id>/` — env file, token, provision vars
39
- - **Host filesystem** `/srv/firecracker/vm-demo/<id>/` — VM images, Firecracker config, logs
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
- ## Setup
51
+ ## Install
52
52
 
53
53
  ```bash
54
- # 1. Clone
55
- git clone https://github.com/bchewy/fireclaw-vm-demo.git
56
- cd fireclaw-vm-demo
54
+ npm install -g fireclaw
55
+ ```
57
56
 
58
- # 2. (Optional) install global CLI
59
- sudo install -m 0755 ./bin/fireclaw /usr/local/bin/fireclaw
57
+ ## Setup
60
58
 
61
- # 3. Create an instance
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
- ## Install From npm
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 (`fcbr0`, `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.
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` | `<repo>/.vm-demo` |
140
+ | `STATE_ROOT` | `/var/lib/fireclaw` |
170
141
  | `FC_ROOT` | `/srv/firecracker/vm-demo` |
171
142
  | `BASE_PORT` | `18890` |
172
- | `BRIDGE_NAME` | `fcbr0` |
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
- cat <<'EOF'
8
- Usage: fireclaw <command> [args...]
7
+ local bold=$'\033[1m' dim=$'\033[2m' reset=$'\033[0m' cyan=$'\033[36m'
9
8
 
10
- Primary commands:
11
- setup <flags...> Create and provision a new VM instance
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
- Compatibility commands:
23
- ctl <vm-ctl-args...> Pass through to vm-ctl
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
- Examples:
28
- sudo ./bin/fireclaw setup --instance my-bot --telegram-token <token> --telegram-users <uid>
29
- sudo ./bin/fireclaw list
30
- sudo ./bin/fireclaw status my-bot
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:-fcbr0}"
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:-$HOME/.ssh/vmdemo_vm}"
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
- return 1
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
- CMD_NAME="${VM_CTL_CMD_NAME:-$(basename "$0")}"
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 found="false"
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 || echo inactive)"
56
- proxy_state="$(systemctl is-active "$(proxy_service "$id")" 2>/dev/null || echo inactive)"
57
- echo "$id ip=$VM_IP port=$HOST_PORT vm=$vm_state proxy=$proxy_state health=$health host_health=$host_health guest_health=$guest_health"
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
- [[ "$found" == "true" ]] || echo "(no instances)"
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 || echo inactive)"
70
- proxy_state="$(systemctl is-active "$(proxy_service "$id")" 2>/dev/null || echo inactive)"
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 || echo unknown)"
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
- echo "$id ip=$VM_IP port=$HOST_PORT vm=$vm_state proxy=$proxy_state guest=$guest health=$health host_health=$host_health guest_health=$guest_health"
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: vm-ctl start <id>"; cmd_start "$1" ;;
200
- stop) [[ $# -eq 1 ]] || die "Usage: vm-ctl stop <id>"; cmd_stop "$1" ;;
201
- restart) [[ $# -eq 1 ]] || die "Usage: vm-ctl restart <id>"; cmd_restart "$1" ;;
202
- logs) [[ $# -ge 1 ]] || die "Usage: vm-ctl logs <id> [guest|host]"; cmd_logs "$@" ;;
203
- shell) [[ $# -ge 1 ]] || die "Usage: vm-ctl shell <id> [command...]"; id="$1"; shift; cmd_shell "$id" "$@" ;;
204
- token) [[ $# -eq 1 ]] || die "Usage: vm-ctl token <id>"; cmd_token "$1" ;;
205
- destroy) [[ $# -ge 1 ]] || die "Usage: vm-ctl destroy <id> [--force]"; cmd_destroy "$@" ;;
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
@@ -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
- CMD_NAME="${VM_SETUP_CMD_NAME:-$(basename "$0")}"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fireclaw",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Firecracker microVM control plane for isolated OpenClaw instances",
5
5
  "bin": {
6
6
  "fireclaw": "bin/fireclaw"
@@ -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 -T \
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 -T \
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" \