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 +21 -0
- package/README.md +117 -109
- package/bin/fireclaw +32 -22
- package/bin/vm-common.sh +254 -31
- package/bin/vm-ctl +229 -40
- package/bin/vm-provision +152 -18
- package/bin/vm-setup +237 -94
- package/package.json +5 -4
- package/scripts/provision-guest.sh +134 -25
|
@@ -5,18 +5,79 @@ set -euo pipefail
|
|
|
5
5
|
VARS_FILE="$1"
|
|
6
6
|
[[ -f "$VARS_FILE" ]] || { echo "Vars file missing: $VARS_FILE" >&2; exit 1; }
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
cleanup_guest_tmp() {
|
|
9
|
+
if [[ "$VARS_FILE" == /tmp/provision.vars ]]; then
|
|
10
|
+
rm -f "$VARS_FILE"
|
|
11
|
+
fi
|
|
12
|
+
rm -f /tmp/provision-guest.sh
|
|
13
|
+
}
|
|
14
|
+
trap cleanup_guest_tmp EXIT
|
|
15
|
+
|
|
16
|
+
legacy_unquote_value() {
|
|
17
|
+
local value="$1"
|
|
18
|
+
local out="" ch inner
|
|
19
|
+
if [[ "$value" == "''" ]]; then
|
|
20
|
+
printf ''
|
|
21
|
+
return
|
|
22
|
+
fi
|
|
23
|
+
if [[ "$value" == \$\'*\' && ${#value} -ge 3 ]]; then
|
|
24
|
+
inner="${value:2:${#value}-3}"
|
|
25
|
+
printf '%b' "$inner"
|
|
26
|
+
return
|
|
27
|
+
fi
|
|
28
|
+
while [[ -n "$value" ]]; do
|
|
29
|
+
ch="${value:0:1}"
|
|
30
|
+
if [[ "$ch" == "\\" && ${#value} -gt 1 ]]; then
|
|
31
|
+
value="${value:1}"
|
|
32
|
+
out="${out}${value:0:1}"
|
|
33
|
+
else
|
|
34
|
+
out="${out}${ch}"
|
|
35
|
+
fi
|
|
36
|
+
value="${value:1}"
|
|
37
|
+
done
|
|
38
|
+
printf '%s' "$out"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
load_vars_file() {
|
|
42
|
+
local format line key value
|
|
43
|
+
format="$(grep '^FIRECLAW_STATE_FORMAT=' "$VARS_FILE" | tail -n 1 | cut -d= -f2- || true)"
|
|
44
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
45
|
+
[[ -n "$line" && "$line" != \#* ]] || continue
|
|
46
|
+
[[ "$line" == *=* ]] || { echo "Invalid vars entry in $VARS_FILE: $line" >&2; exit 1; }
|
|
47
|
+
key="${line%%=*}"
|
|
48
|
+
value="${line#*=}"
|
|
49
|
+
if [[ "$key" == "FIRECLAW_STATE_FORMAT" ]]; then
|
|
50
|
+
continue
|
|
51
|
+
fi
|
|
52
|
+
if [[ "$format" != "plain-v1" ]]; then
|
|
53
|
+
value="$(legacy_unquote_value "$value")"
|
|
54
|
+
fi
|
|
55
|
+
[[ "$value" != *$'\n'* && "$value" != *$'\r'* ]] || { echo "Invalid newline in vars value: $key" >&2; exit 1; }
|
|
56
|
+
case "$key" in
|
|
57
|
+
INSTANCE_ID|TELEGRAM_TOKEN|TELEGRAM_USERS|MODEL|SKILLS|GATEWAY_TOKEN|OPENCLAW_IMAGE|SKIP_BROWSER_INSTALL|DISK_SIZE|ANTHROPIC_API_KEY|OPENAI_API_KEY|MINIMAX_API_KEY)
|
|
58
|
+
printf -v "$key" '%s' "$value"
|
|
59
|
+
export "$key"
|
|
60
|
+
;;
|
|
61
|
+
*)
|
|
62
|
+
echo "Unknown vars key in $VARS_FILE: $key" >&2
|
|
63
|
+
exit 1
|
|
64
|
+
;;
|
|
65
|
+
esac
|
|
66
|
+
done < "$VARS_FILE"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
load_vars_file
|
|
11
70
|
|
|
12
71
|
require() { [[ -n "${!1:-}" ]] || { echo "Missing required var: $1" >&2; exit 1; }; }
|
|
13
72
|
|
|
14
73
|
require INSTANCE_ID
|
|
15
|
-
require TELEGRAM_TOKEN
|
|
16
74
|
require MODEL
|
|
17
75
|
require SKILLS
|
|
18
76
|
require GATEWAY_TOKEN
|
|
19
77
|
require OPENCLAW_IMAGE
|
|
78
|
+
if [[ -n "${TELEGRAM_TOKEN:-}" ]]; then
|
|
79
|
+
require TELEGRAM_USERS
|
|
80
|
+
fi
|
|
20
81
|
|
|
21
82
|
[[ "$INSTANCE_ID" =~ ^[a-z0-9_-]+$ ]] || {
|
|
22
83
|
echo "INSTANCE_ID must match [a-z0-9_-]+" >&2
|
|
@@ -30,10 +91,25 @@ TOOLS_DIR="/home/ubuntu/openclaw-${INSTANCE_ID}/tools"
|
|
|
30
91
|
ENV_FILE="$CONFIG_ROOT/openclaw.env"
|
|
31
92
|
GUEST_SERVICE="openclaw-${INSTANCE_ID}.service"
|
|
32
93
|
HEALTH_SCRIPT="/usr/local/bin/openclaw-health-${INSTANCE_ID}.sh"
|
|
94
|
+
PLAYWRIGHT_FALLBACK_PACKAGE="${PLAYWRIGHT_FALLBACK_PACKAGE:-playwright@1.44.1}"
|
|
33
95
|
|
|
34
96
|
log() { printf '==> %s\n' "$*"; }
|
|
35
97
|
warn() { printf 'Warning: %s\n' "$*" >&2; }
|
|
36
98
|
|
|
99
|
+
csv_values() {
|
|
100
|
+
printf '%s' "$1" | tr ',' '\n' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; /^$/d'
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
csv_json_array() {
|
|
104
|
+
local values
|
|
105
|
+
values="$(csv_values "$1")"
|
|
106
|
+
if [[ -z "$values" ]]; then
|
|
107
|
+
printf '[]\n'
|
|
108
|
+
else
|
|
109
|
+
printf '%s\n' "$values" | jq -R . | jq -s .
|
|
110
|
+
fi
|
|
111
|
+
}
|
|
112
|
+
|
|
37
113
|
wait_for_cloud_init() {
|
|
38
114
|
if command -v cloud-init >/dev/null 2>&1; then
|
|
39
115
|
log "Waiting for cloud-init to finish"
|
|
@@ -167,6 +243,18 @@ if ! command -v jq >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then
|
|
|
167
243
|
fi
|
|
168
244
|
ensure_docker_daemon_config
|
|
169
245
|
|
|
246
|
+
skills_json="$(csv_json_array "${SKILLS:-}")"
|
|
247
|
+
|
|
248
|
+
telegram_allow_from_json="[]"
|
|
249
|
+
if [[ -n "${TELEGRAM_TOKEN:-}" ]]; then
|
|
250
|
+
telegram_users_values="$(csv_values "${TELEGRAM_USERS:-}")"
|
|
251
|
+
if [[ -z "$telegram_users_values" ]]; then
|
|
252
|
+
echo "TELEGRAM_USERS must include at least one allowed Telegram user ID; refusing to create an unreachable allowlist bot" >&2
|
|
253
|
+
exit 1
|
|
254
|
+
fi
|
|
255
|
+
telegram_allow_from_json="$(printf '%s\n' "$telegram_users_values" | jq -R . | jq -s .)"
|
|
256
|
+
fi
|
|
257
|
+
|
|
170
258
|
mkdir -p "$CONFIG_DIR" "$WORKSPACE_DIR" "$TOOLS_DIR"
|
|
171
259
|
chown -R ubuntu:ubuntu "$CONFIG_ROOT" "/home/ubuntu/openclaw-${INSTANCE_ID}"
|
|
172
260
|
|
|
@@ -190,7 +278,7 @@ run_openclaw_cli() {
|
|
|
190
278
|
--network host \
|
|
191
279
|
-e HOME=/home/node \
|
|
192
280
|
-e OPENCLAW_GATEWAY_TOKEN="$GATEWAY_TOKEN" \
|
|
193
|
-
-e OPENCLAW_TELEGRAM_TOKEN="$TELEGRAM_TOKEN" \
|
|
281
|
+
-e OPENCLAW_TELEGRAM_TOKEN="${TELEGRAM_TOKEN:-}" \
|
|
194
282
|
-e OPENCLAW_MODEL="$MODEL" \
|
|
195
283
|
-e OPENCLAW_TELEGRAM_ALLOW_FROM_JSON="$telegram_allow_from_json" \
|
|
196
284
|
-e OPENCLAW_SKILLS_JSON="$skills_json" \
|
|
@@ -206,18 +294,6 @@ run_openclaw_cli() {
|
|
|
206
294
|
"$OPENCLAW_IMAGE" -se
|
|
207
295
|
}
|
|
208
296
|
|
|
209
|
-
skills_json="[]"
|
|
210
|
-
IFS=',' read -r -a skills_arr <<< "${SKILLS:-}"
|
|
211
|
-
if (( ${#skills_arr[@]} > 0 )); then
|
|
212
|
-
skills_json="$(printf '%s\n' "${skills_arr[@]}" | sed '/^$/d' | jq -R . | jq -s .)"
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
telegram_allow_from_json="[]"
|
|
216
|
-
IFS=',' read -r -a users_arr <<< "${TELEGRAM_USERS:-}"
|
|
217
|
-
if (( ${#users_arr[@]} > 0 )); then
|
|
218
|
-
telegram_allow_from_json="$(printf '%s\n' "${users_arr[@]}" | sed '/^$/d' | jq -R . | jq -s .)"
|
|
219
|
-
fi
|
|
220
|
-
|
|
221
297
|
run_openclaw_cli <<'EOF'
|
|
222
298
|
set -euo pipefail
|
|
223
299
|
OPENCLAW='node /app/openclaw.mjs'
|
|
@@ -228,14 +304,28 @@ $OPENCLAW config set gateway.auth.token "$OPENCLAW_GATEWAY_TOKEN"
|
|
|
228
304
|
$OPENCLAW config set gateway.tailscale.mode off
|
|
229
305
|
$OPENCLAW config set agents.defaults.model.primary "$OPENCLAW_MODEL"
|
|
230
306
|
$OPENCLAW config set agents.defaults.skipBootstrap false --json
|
|
307
|
+
$OPENCLAW config set skills.allowBundled "$OPENCLAW_SKILLS_JSON" --json
|
|
308
|
+
EOF
|
|
309
|
+
|
|
310
|
+
if [[ -n "${TELEGRAM_TOKEN:-}" ]]; then
|
|
311
|
+
run_openclaw_cli <<'EOF'
|
|
312
|
+
set -euo pipefail
|
|
313
|
+
OPENCLAW='node /app/openclaw.mjs'
|
|
231
314
|
$OPENCLAW config set channels.telegram.enabled true --json
|
|
232
315
|
$OPENCLAW config set channels.telegram.botToken "$OPENCLAW_TELEGRAM_TOKEN"
|
|
233
316
|
$OPENCLAW config set channels.telegram.dmPolicy allowlist
|
|
234
317
|
$OPENCLAW config set channels.telegram.groupPolicy disabled
|
|
235
318
|
$OPENCLAW config set channels.telegram.allowFrom "$OPENCLAW_TELEGRAM_ALLOW_FROM_JSON" --json
|
|
236
319
|
$OPENCLAW config set plugins.entries.telegram.enabled true --json
|
|
237
|
-
$OPENCLAW config set skills.allowBundled "$OPENCLAW_SKILLS_JSON" --json
|
|
238
320
|
EOF
|
|
321
|
+
else
|
|
322
|
+
run_openclaw_cli <<'EOF'
|
|
323
|
+
set -euo pipefail
|
|
324
|
+
OPENCLAW='node /app/openclaw.mjs'
|
|
325
|
+
$OPENCLAW config set channels.telegram.enabled false --json
|
|
326
|
+
$OPENCLAW config set plugins.entries.telegram.enabled false --json
|
|
327
|
+
EOF
|
|
328
|
+
fi
|
|
239
329
|
|
|
240
330
|
if [[ "$MODEL" == minimax/* ]]; then
|
|
241
331
|
OPENCLAW_MINIMAX_PROVIDER_JSON="$(jq -cn --arg api_key "${MINIMAX_API_KEY:-}" '
|
|
@@ -263,15 +353,33 @@ EOF
|
|
|
263
353
|
fi
|
|
264
354
|
|
|
265
355
|
if [[ "${SKIP_BROWSER_INSTALL:-false}" != "true" ]]; then
|
|
266
|
-
docker run --rm \
|
|
356
|
+
docker run --rm -i \
|
|
267
357
|
--network host \
|
|
358
|
+
-e HOME=/home/node \
|
|
268
359
|
-e PLAYWRIGHT_BROWSERS_PATH=/home/node/clawd/tools/.playwright \
|
|
360
|
+
-e PLAYWRIGHT_FALLBACK_PACKAGE="$PLAYWRIGHT_FALLBACK_PACKAGE" \
|
|
269
361
|
-v "$TOOLS_DIR:/home/node/clawd/tools" \
|
|
270
362
|
--entrypoint /bin/bash \
|
|
271
|
-
"$OPENCLAW_IMAGE" -
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
363
|
+
"$OPENCLAW_IMAGE" -se <<'EOF'
|
|
364
|
+
set -euo pipefail
|
|
365
|
+
|
|
366
|
+
if playwright_cli="$(node <<'NODE'
|
|
367
|
+
const path = require("path");
|
|
368
|
+
try {
|
|
369
|
+
const pkg = require.resolve("playwright/package.json");
|
|
370
|
+
process.stdout.write(path.join(path.dirname(pkg), "cli.js"));
|
|
371
|
+
} catch {
|
|
372
|
+
process.exit(1);
|
|
373
|
+
}
|
|
374
|
+
NODE
|
|
375
|
+
)"; then
|
|
376
|
+
echo "Using Playwright from image: $playwright_cli"
|
|
377
|
+
node "$playwright_cli" install chromium
|
|
378
|
+
else
|
|
379
|
+
echo "Using fallback Playwright package: $PLAYWRIGHT_FALLBACK_PACKAGE"
|
|
380
|
+
npx --yes "$PLAYWRIGHT_FALLBACK_PACKAGE" install chromium
|
|
381
|
+
fi
|
|
382
|
+
EOF
|
|
275
383
|
|
|
276
384
|
chrome_host_path="$(
|
|
277
385
|
find "$TOOLS_DIR/.playwright" -type f \
|
|
@@ -319,7 +427,7 @@ Requires=docker.service
|
|
|
319
427
|
[Service]
|
|
320
428
|
Type=simple
|
|
321
429
|
ExecStartPre=-/usr/bin/docker rm -f openclaw-$INSTANCE_ID
|
|
322
|
-
ExecStart=/usr/bin/docker run --rm --name openclaw-$INSTANCE_ID --init --network host --env-file $ENV_FILE -v $CONFIG_DIR:/home/node/.openclaw -v $WORKSPACE_DIR:/home/node/.openclaw/workspace -v $TOOLS_DIR:/home/node/clawd/tools $OPENCLAW_IMAGE node dist/index.js gateway --bind lan --port 18789
|
|
430
|
+
ExecStart=/usr/bin/docker run --rm --name openclaw-$INSTANCE_ID --init --network host --env-file $ENV_FILE -e HOME=/home/node -v $CONFIG_DIR:/home/node/.openclaw -v $WORKSPACE_DIR:/home/node/.openclaw/workspace -v $TOOLS_DIR:/home/node/clawd/tools $OPENCLAW_IMAGE node dist/index.js gateway --bind lan --port 18789
|
|
323
431
|
ExecStop=/usr/bin/docker stop openclaw-$INSTANCE_ID
|
|
324
432
|
Restart=always
|
|
325
433
|
RestartSec=5
|
|
@@ -329,6 +437,7 @@ WantedBy=multi-user.target
|
|
|
329
437
|
EOF
|
|
330
438
|
|
|
331
439
|
systemctl daemon-reload
|
|
332
|
-
systemctl enable
|
|
440
|
+
systemctl enable "$GUEST_SERVICE"
|
|
441
|
+
systemctl restart "$GUEST_SERVICE"
|
|
333
442
|
|
|
334
443
|
echo "Guest provisioning complete for $INSTANCE_ID"
|