fireclaw 0.1.1 → 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bchewy
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 CHANGED
@@ -1,149 +1,157 @@
1
1
  # fireclaw
2
2
 
3
- Run [OpenClaw](https://github.com/openclaw/openclaw) instances inside Firecracker microVMs, each fully isolated with its own filesystem, network, and process tree.
4
-
5
- ## Why
6
-
7
- Running OpenClaw directly on a host (bare metal or in Docker) means every instance shares the kernel, network namespace, and often the Docker socket. That's fine for a single bot, but problematic when you want:
8
-
9
- - **Isolation** — one misbehaving instance can't interfere with others or the host
10
- - **Reproducibility** — each VM boots from a clean rootfs image, no drift
11
- - **Security** — no Docker socket mount into the container; the guest runs its own Docker daemon
12
- - **Density** — Firecracker VMs boot in ~125ms and use ~5MB overhead per VM, so you can pack many instances on a single host
13
-
14
- This repo is a minimal control plane that wires up Firecracker VM lifecycle, networking, and OpenClaw provisioning with plain bash and systemd. No orchestrator, no Kubernetes, no extra daemons.
3
+ Run [OpenClaw](https://github.com/openclaw/openclaw) agents inside Firecracker microVMs — one VM per instance, each with its own kernel, filesystem, Docker daemon, and network. A small Bash CLI drives Firecracker + systemd directly: no Kubernetes, no always-on daemon, no hidden state.
15
4
 
16
5
  ## How it works
17
6
 
7
+ `fireclaw setup` builds four things on the host, then provisions the guest over SSH:
8
+
9
+ 1. **Instance state** — `/var/lib/fireclaw/.vm-<id>/` holds `.env` (allocation + config), `.token` (gateway auth token), `provision.vars` (guest provisioning input, including secrets), and `known_hosts` (the guest SSH host key, pinned after the first successful login). The directory is root-only (`0700`); the secret files are mode `0600`.
10
+ 2. **VM assets** — `/srv/firecracker/vm-demo/<id>/`: a copy of the base rootfs resized to `--disk-size`, kernel (+ optional initrd), a cloud-init seed image (ubuntu user, SSH key, static IP by MAC match), the Firecracker JSON config, and generated `start-vm.sh` / `stop-vm.sh`.
11
+ 3. **Two systemd units** — `firecracker-vmdemo-<id>.service` runs the VM: `Restart=always` (an in-guest reboot or VMM crash comes back on its own) and an `ExecStop` that asks the guest to shut down cleanly through the Firecracker API socket before the VMM exits. `vmdemo-proxy-<id>.service` runs socat as an unprivileged dynamic user, forwarding `127.0.0.1:<HOST_PORT>` → `<VM_IP>:18789`, and starts/stops with the VM unit.
12
+ 4. **Network** — one tap per VM on the `fc-br0` bridge (`172.16.0.1/24`). Each tap is an *isolated* bridge port, so VMs cannot exchange IP or ARP traffic with each other — only with the host and, via a MASQUERADE rule, the internet. Host port and VM IP are allocated under a lock, so concurrent setups cannot collide.
13
+
14
+ Guest provisioning (`scripts/provision-guest.sh`, SCP'd in and run once over SSH) installs Docker with bridge/iptables disabled, pulls the OpenClaw image, writes the gateway/model/skills config (Telegram is enabled only when a token was provided, otherwise explicitly disabled), installs a health script, and creates `openclaw-<id>.service`, which runs the container with `--network host` serving the gateway on `:18789`. The copied secrets are removed from guest `/tmp` when the script exits.
15
+
16
+ ```mermaid
17
+ flowchart LR
18
+ cli["fireclaw CLI<br/>sudo fireclaw ..."]
19
+
20
+ subgraph host["Host"]
21
+ state["state<br/>/var/lib/fireclaw/.vm-id"]
22
+ assets["assets<br/>/srv/firecracker/vm-demo/id"]
23
+ vmUnit["firecracker-vmdemo-id.service"]
24
+ proxyUnit["vmdemo-proxy-id.service<br/>socat 127.0.0.1:HOST_PORT"]
25
+ net["fc-br0 bridge + NAT<br/>isolated tap per VM"]
26
+ end
27
+
28
+ subgraph guest["Firecracker VM"]
29
+ cloudInit["cloud-init<br/>ubuntu user, SSH, static IP"]
30
+ docker["Docker (bridge off)"]
31
+ guestUnit["openclaw-id.service"]
32
+ container["OpenClaw container<br/>gateway :18789"]
33
+ end
34
+
35
+ cli --> state
36
+ cli --> assets
37
+ cli --> vmUnit
38
+ vmUnit --> net
39
+ net --> guest
40
+ cli -->|"SCP + SSH provision"| docker
41
+ docker --> guestUnit
42
+ guestUnit --> container
43
+ proxyUnit -->|"forwards to VM_IP:18789"| container
44
+ cli -->|"health gates"| proxyUnit
18
45
  ```
19
- Host
20
- ├── systemd: firecracker-vmdemo-<id>.service ← runs the VM
21
- ├── systemd: vmdemo-proxy-<id>.service ← socat: localhost:<port> → VM:18789
22
- ├── bridge: fcbr0 (172.16.0.0/24) ← shared bridge for all VMs
23
-
24
- └── Firecracker VM (172.16.0.x)
25
- ├── cloud-init: ubuntu user, SSH key, Docker install
26
- ├── Docker: pulls OpenClaw image
27
- ├── systemd: openclaw-<id>.service ← docker run --network host ... gateway --bind lan --port 18789
28
- └── Browser binaries (Playwright Chromium, installed at provision time)
29
- ```
30
-
31
- 1. **`fireclaw setup`** creates a new instance: copies the base rootfs, optionally resizes it (default `40G`), generates a cloud-init seed image, allocates an IP + host port, writes a Firecracker config, creates systemd units, boots the VM with a per-instance Firecracker API socket, waits for SSH, then SCPs `provision-guest.sh` into the guest and runs it.
32
-
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
-
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
46
 
37
- All state lives in two places:
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
47
+ Lifecycle semantics:
40
48
 
41
- ## Prerequisites
49
+ - `setup`, `provision`, and `start` return success only after **both** health gates pass: the guest health script (container running + `/health` inside the VM) and the localhost proxy `/health`. `start` never reprovisions. `list`/`status` are looser inspection views (health may read up when either the proxy or the guest check responds).
50
+ - A `setup` failure *before* guest provisioning completes rolls back only that instance (units, tap, API socket, state, assets). After guest provisioning succeeds, failures keep the instance — inspect with `status`, retry with `provision`.
51
+ - `stop` shuts down proxy → guest service → VM (clean guest shutdown via the API socket) and disables the units, so a stopped instance stays stopped across host reboots until `start`.
52
+ - `provision <id> [flags]` updates the saved config and reruns guest provisioning on the existing VM — no new IP, port, or disk. Overrides are validated first; a rejected run changes nothing.
53
+ - `destroy` removes units, tap, API socket, state, and assets; `--force` also cleans up instances with unreadable state (units and directories only — a leftover tap or socket then needs manual removal).
42
54
 
43
- - Linux host with KVM support (`/dev/kvm` accessible)
44
- - `firecracker` binary at `/usr/local/bin/firecracker` ([install guide](https://github.com/firecracker-microvm/firecracker/blob/main/docs/getting-started.md))
45
- - `qemu-img` (from `qemu-utils`) for rootfs resizing
46
- - `cloud-localds` (from `cloud-image-utils`), `socat`, `jq`, `iptables`, `iproute2`, `ssh`, `scp`, `curl`, `openssl`
47
- - Base VM images: a Linux kernel (`vmlinux`) and an ext4 rootfs with cloud-init support. Optionally an initrd.
48
-
49
- Set `BASE_IMAGES_DIR` or pass `--base-kernel`/`--base-rootfs`/`--base-initrd` to point at your images.
55
+ Naming note: runtime paths keep the historical `vmdemo` names (`/srv/firecracker/vm-demo`, `firecracker-vmdemo-*`, `vmdemo-proxy-*`) so existing instances keep working.
50
56
 
51
57
  ## Install
52
58
 
59
+ Host needs Linux with KVM (`/dev/kvm`), root access, `firecracker` on `PATH`, the usual tools (`systemctl`, `cloud-localds`, `qemu-img`, `iptables`, `ip`, `bridge`, `socat`, `jq`, `curl`, `openssl`, `ssh`, `scp`, `install`, `flock`), and base images (kernel + cloud-init-capable ext4 rootfs, default dir `/srv/firecracker/base/images`). `fireclaw doctor` checks all of this.
60
+
53
61
  ```bash
54
62
  npm install -g fireclaw
55
63
  ```
56
64
 
57
- ## Setup
65
+ If `sudo fireclaw` reports `command not found` (nvm/user-prefix installs aren't on root's `secure_path`):
58
66
 
59
67
  ```bash
60
- sudo fireclaw setup \
61
- --instance my-bot \
62
- --telegram-token "<your-bot-token>" \
63
- --telegram-users "<your-telegram-user-id>" \
64
- --model "anthropic/claude-opus-4-6" \
65
- --anthropic-api-key "<key>"
68
+ sudo ln -s "$(command -v fireclaw)" /usr/local/bin/fireclaw
66
69
  ```
67
70
 
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).
71
+ ## Quick start
69
72
 
70
- ### Options
73
+ ```bash
74
+ sudo fireclaw doctor
71
75
 
72
- | Flag | Default | Description |
73
- |------|---------|-------------|
74
- | `--instance` | (required) | Instance ID (`[a-z0-9_-]+`) |
75
- | `--telegram-token` | (required) | Telegram bot token |
76
- | `--telegram-users` | | Comma-separated Telegram user IDs for allowlist |
77
- | `--model` | `anthropic/claude-opus-4-6` | Model ID |
78
- | `--skills` | `github,tmux,coding-agent,session-logs,skill-creator` | Comma-separated skill list |
79
- | `--openclaw-image` | `ghcr.io/openclaw/openclaw:latest` | Docker image for OpenClaw |
80
- | `--vm-vcpu` | `4` | vCPUs per VM |
81
- | `--vm-mem-mib` | `8192` | Memory per VM (MiB) |
82
- | `--disk-size` | `40G` | Resize copied rootfs image to this virtual size before boot |
83
- | `--api-sock` | `<fc-dir>/firecracker.socket` | Firecracker API socket path (must be unique per VM) |
84
- | `--anthropic-api-key` | | Anthropic API key |
85
- | `--openai-api-key` | | OpenAI API key |
86
- | `--minimax-api-key` | | MiniMax API key |
87
- | `--skip-browser-install` | `false` | Skip Playwright Chromium install |
88
-
89
- ## Usage
76
+ # Local-only gateway (Telegram disabled):
77
+ sudo fireclaw setup --instance my-bot --model "openai/gpt-5.5" --openai-api-key "<key>"
78
+
79
+ # Telegram bot (DM allowlist, groups disabled):
80
+ sudo fireclaw setup --instance my-bot \
81
+ --telegram-token "<bot-token>" --telegram-users "<comma-separated-user-ids>" \
82
+ --model "openai/gpt-5.5" --openai-api-key "<key>"
83
+ ```
84
+
85
+ Setup fails fast if the model's provider key is missing (`openai/*` OpenAI key, `anthropic/*` Anthropic, `minimax/*` MiniMax). Keys can come from the environment instead of flags, keeping them out of `ps` output:
90
86
 
91
87
  ```bash
92
- # List all instances
93
- sudo fireclaw list
88
+ sudo OPENAI_API_KEY="<key>" fireclaw setup --instance my-bot
89
+ ```
94
90
 
95
- # Status of one instance
91
+ Setup prints the instance's IP, proxy port, and health on success. Verify any time with:
92
+
93
+ ```bash
96
94
  sudo fireclaw status my-bot
95
+ curl -fsS http://127.0.0.1:<HOST_PORT>/health
96
+ ```
97
+
98
+ ## Commands
97
99
 
98
- # Stop / start / restart
99
- sudo fireclaw stop my-bot
100
- sudo fireclaw start my-bot
101
- sudo fireclaw restart my-bot
100
+ ```bash
101
+ sudo fireclaw doctor # preflight: commands, /dev/kvm, base images, bridge, capacity
102
+ sudo fireclaw setup <flags...> # create + provision a new instance (flags below)
103
+ sudo fireclaw provision <id> [flags...] # update saved config + rerun guest provisioning
104
+ sudo fireclaw list # fleet table with health
105
+ sudo fireclaw status [id] # detail for one instance (or the fleet)
106
+ sudo fireclaw start|stop|restart <id> # lifecycle; stop persists across host reboots
107
+ sudo fireclaw logs <id> [guest|host] # tail the guest service or host units
108
+ sudo fireclaw shell <id> ["command"] # SSH into the VM, or run one command
109
+ sudo fireclaw token <id> # print the gateway auth token
110
+ sudo fireclaw destroy <id> [--force] # full teardown
111
+ ```
102
112
 
103
- # Tail guest logs (OpenClaw service)
104
- sudo fireclaw logs my-bot
113
+ `provision` accepts `--telegram-token`, `--no-telegram`, `--telegram-users`, `--model`, `--skills`, `--openclaw-image`, the three API-key flags, `--skip-browser-install`, and `--browser-install` — e.g. rotate a key with `provision my-bot --openai-api-key "<new>"`, or enable Telegram on a local-only instance later.
105
114
 
106
- # Tail host logs (Firecracker + proxy)
107
- sudo fireclaw logs my-bot host
115
+ ## Setup flags
108
116
 
109
- # SSH into the VM
110
- sudo fireclaw shell my-bot
117
+ | Flag | Default | Description |
118
+ |------|---------|-------------|
119
+ | `--instance <id>` | required | Instance ID (`[a-z0-9_-]+`) |
120
+ | `--telegram-token <token>` | none | Omit for a local-only gateway with Telegram disabled |
121
+ | `--telegram-users <csv>` | none | Allowed Telegram user IDs; required with `--telegram-token` |
122
+ | `--model <id>` | `openai/gpt-5.5` | OpenClaw model; its provider API key must be set |
123
+ | `--skills <csv>` | `github,tmux,coding-agent,session-logs,skill-creator` | Skill set |
124
+ | `--openclaw-image <image>` | `ghcr.io/openclaw/openclaw:latest` | Container image |
125
+ | `--host-port <n>` | first free port above `BASE_PORT` | Localhost proxy port (>= 1024) |
126
+ | `--vm-vcpu <n>` / `--vm-mem-mib <n>` | `4` / `8192` | VM sizing |
127
+ | `--disk-size <size>` | `40G` | Rootfs resize target |
128
+ | `--api-sock <path>` | `<fc-instance-dir>/firecracker.socket` | Firecracker API socket |
129
+ | `--base-kernel/-rootfs/-initrd <path>` | `<BASE_IMAGES_DIR>/...` | Base image paths |
130
+ | `--anthropic/openai/minimax-api-key <key>` | env var or empty | Provider keys |
131
+ | `--skip-browser-install` | off | Skip Playwright Chromium in the guest |
111
132
 
112
- # Run a command inside the VM
113
- sudo fireclaw shell my-bot "docker ps"
133
+ ## Defaults and state
114
134
 
115
- # Get the gateway token
116
- sudo fireclaw token my-bot
135
+ Networking: bridge `fc-br0` at `172.16.0.1/24`, VM subnet `172.16.0.0/24` (auto-allocation requires `/24`), guest gateway `:18789`, first auto host port `18891` (`BASE_PORT`+1). The proxy is the intended access path; the guest gateway itself binds `0.0.0.0:18789` inside the VM, so keep bridge/subnet reachability private.
117
136
 
118
- # Health check
119
- curl -fsS http://127.0.0.1:<HOST_PORT>/health
120
- # If proxy health is flaky, inspect VM-side health too:
121
- sudo fireclaw status my-bot
137
+ Overridable via environment: `STATE_ROOT` (`/var/lib/fireclaw`), `FC_ROOT` (`/srv/firecracker/vm-demo`), `BASE_PORT` (`18890`), `BRIDGE_NAME`, `BRIDGE_ADDR`, `SUBNET_CIDR`, `SSH_KEY_PATH` (`/home/ubuntu/.ssh/vmdemo_vm`), `BASE_IMAGES_DIR`, `DISK_SIZE`, `API_SOCK`, `OPENCLAW_IMAGE_DEFAULT`.
122
138
 
123
- # Destroy (interactive confirmation)
124
- sudo fireclaw destroy my-bot
139
+ Per instance on disk: state in `STATE_ROOT/.vm-<id>/` (`.env`, `.token`, `provision.vars`, `known_hosts`), runtime in `FC_ROOT/<id>/` (`images/`, `config/`, `logs/`, `start-vm.sh`, `stop-vm.sh`), units in `/etc/systemd/system/firecracker-vmdemo-<id>.service` and `vmdemo-proxy-<id>.service`.
125
140
 
126
- # Destroy (skip confirmation)
127
- sudo fireclaw destroy my-bot --force
128
- ```
141
+ ## Security model
129
142
 
130
- ## Networking
143
+ - The isolation boundary is the VM, not a container namespace; no host Docker socket is mounted anywhere.
144
+ - VMs cannot reach each other (isolated bridge ports block IP and ARP between guests, plus an intra-bridge iptables `FORWARD` drop as defense-in-depth where `br_netfilter` is active); each sees only the host gateway and NATed egress.
145
+ - The host proxy is localhost-only and runs unprivileged; guest SSH host keys are pinned per instance and mismatches are reported.
146
+ - Secrets live root-only under `STATE_ROOT` (mode `0600`). The provisioning copies are removed from guest `/tmp` afterwards; the runtime secrets the service needs persist inside the guest in a `0600` env file and the OpenClaw config. The gateway token is never echoed — use `fireclaw token <id>`. Prefer env vars over flags for API keys.
131
147
 
132
- 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.
148
+ ## Troubleshooting
133
149
 
134
- ## Environment variables
150
+ - VM won't start: `sudo journalctl -u firecracker-vmdemo-<id>.service -xe`; check `/dev/kvm` and `sudo fireclaw doctor`.
151
+ - SSH unreachable: `sudo fireclaw status <id>`; a host-key mismatch is reported explicitly — remove `STATE_ROOT/.vm-<id>/known_hosts` if the change is expected. If SSH never comes up, read the boot/console output in `/srv/firecracker/vm-demo/<id>/logs/`.
152
+ - Proxy health down: `curl -v http://127.0.0.1:<port>/health`, then `sudo fireclaw shell <id> "curl -fsS http://127.0.0.1:18789/health"`.
153
+ - Disk pressure in the guest: recreate with a larger `--disk-size`.
135
154
 
136
- All scripts respect these overrides:
155
+ ## Development
137
156
 
138
- | Variable | Default |
139
- |----------|---------|
140
- | `STATE_ROOT` | `/var/lib/fireclaw` |
141
- | `FC_ROOT` | `/srv/firecracker/vm-demo` |
142
- | `BASE_PORT` | `18890` |
143
- | `BRIDGE_NAME` | `fcbr0` |
144
- | `BRIDGE_ADDR` | `172.16.0.1/24` |
145
- | `SUBNET_CIDR` | `172.16.0.0/24` |
146
- | `SSH_KEY_PATH` | `~/.ssh/vmdemo_vm` |
147
- | `BASE_IMAGES_DIR` | `/srv/firecracker/base/images` |
148
- | `DISK_SIZE` | `40G` |
149
- | `API_SOCK` | `<FC_ROOT>/<instance>/firecracker.socket` |
157
+ `npm test` runs shell syntax checks plus unit tests for `bin/vm-common.sh`; `npm run pack:check` validates package contents. Contributor conventions live in [AGENTS.md](AGENTS.md).
package/bin/fireclaw CHANGED
@@ -4,30 +4,37 @@ set -euo pipefail
4
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 fireclaw setup --instance my-bot --telegram-token <token> --telegram-users <uid>
29
- sudo fireclaw list
30
- sudo fireclaw status my-bot
15
+ ${bold}COMMANDS${reset}
16
+ ${cyan}doctor${reset} Check host prerequisites
17
+ ${cyan}setup${reset} <flags...> Create and provision a new VM instance
18
+ ${cyan}provision${reset} <id> [flags...] Update saved config and re-run guest provisioning
19
+ ${cyan}list${reset} List all instances
20
+ ${cyan}status${reset} [id] Show instance status (all or one)
21
+ ${cyan}start${reset} <id> Start an instance
22
+ ${cyan}stop${reset} <id> Stop an instance (stays stopped across host reboots)
23
+ ${cyan}restart${reset} <id> Restart an instance
24
+ ${cyan}logs${reset} <id> [guest|host] Stream logs
25
+ ${cyan}shell${reset} <id> [command...] SSH into VM or run a command
26
+ ${cyan}token${reset} <id> Print gateway token
27
+ ${cyan}destroy${reset} <id> [--force] Destroy an instance
28
+
29
+ ${bold}EXAMPLES${reset}
30
+ ${dim}\$${reset} sudo fireclaw doctor
31
+ ${dim}\$${reset} sudo fireclaw setup --instance my-bot --openai-api-key <key>
32
+ ${dim}\$${reset} sudo fireclaw setup --instance my-bot --telegram-token <tok> --telegram-users <uid> --openai-api-key <key>
33
+ ${dim}\$${reset} sudo fireclaw provision my-bot --model openai/gpt-5.5
34
+ ${dim}\$${reset} sudo fireclaw list
35
+ ${dim}\$${reset} sudo fireclaw status my-bot
36
+ ${dim}\$${reset} sudo fireclaw shell my-bot
37
+ ${dim}\$${reset} sudo fireclaw logs my-bot guest
31
38
  EOF
32
39
  }
33
40
 
@@ -41,9 +48,12 @@ shift || true
41
48
 
42
49
  case "$cmd" in
43
50
  setup|create|provision)
51
+ if [[ "$cmd" == "provision" ]]; then
52
+ VM_PROVISION_CMD_NAME="fireclaw provision" exec "$SCRIPT_DIR/vm-provision" "$@"
53
+ fi
44
54
  VM_SETUP_CMD_NAME="fireclaw setup" exec "$SCRIPT_DIR/vm-setup" "$@"
45
55
  ;;
46
- list|status|start|stop|restart|logs|shell|token|destroy)
56
+ doctor|list|status|start|stop|restart|logs|shell|token|destroy)
47
57
  if [[ "${1:-}" == "-h" || "${1:-}" == "--help" || "${1:-}" == "help" ]]; then
48
58
  VM_CTL_CMD_NAME="fireclaw" exec "$SCRIPT_DIR/vm-ctl" --help
49
59
  fi