feat(qa): publish the Telegram Desktop variant AMI

Bakes the catalog-only Crabbox variant image from the same pinned Telegram
Desktop the local Docker image uses, then proves selection: an --image-sdk
lease gets the new AMI and a plain desktop lease keeps the generic default.

--prep-only validates the recipe on one lease without creating an image or
needing coordinator admin, which is how the AMI contract is testable by anyone.

Three defects only a real AWS lease could show:
- the version marker lived in Crabbox's state dir, which the lease user cannot
  traverse on AWS desktop images, so the recorder preflight could not read it
- liveness matched the process name, missing the setsid fork-to-exec window on
  a cold 234MB binary
- remote payloads ran through a login shell, whose ~/.bash_logout clear_console
  failure replaced the script's exit status
This commit is contained in:
Ayaan Zaidi
2026-08-19 10:15:03 +02:00
parent 0151ef17b3
commit 286f158349
6 changed files with 606 additions and 11 deletions
+6 -4
View File
@@ -241,10 +241,12 @@ bash scripts/mantis/build-telegram-desktop-image.sh
`--provider aws` targets a Crabbox catalog-only Telegram variant image
(`--image-sdk telegram-desktop=7.0.9`) so the generic desktop image never
carries the client. Publishing that variant needs Crabbox coordinator admin
(`crabbox image create` / `image promote`) and is not part of this repository
yet; until it is published, use the local Docker image above. Either image must
provide an
carries the client. `scripts/mantis/bake-telegram-desktop-image.sh` bakes and
publishes it; `crabbox image create` and `image promote` are admin-only, so a
Crabbox coordinator admin runs it with `--run`. Anyone can validate the recipe
first with `--prep-only --run`, which warms one lease, installs the pinned
client, runs the recorder's own contract check, and stops without creating an
image. Either image must provide an
executable Telegram Desktop at `/opt/Telegram/Telegram`, a readable desktop
version marker, `wmctrl`, `xdotool`, `scrot`, `ffmpeg`, `zbarimg`, and
`xdpyinfo`, plus a reachable `DISPLAY=:99`. Crabbox refuses the lease when no
+7 -2
View File
@@ -93,7 +93,9 @@ export function renderGoldenImagePreflight(): string {
contract="Telegram Desktop recorder golden image contract"
fail() { echo "$contract failed: $1" >&2; exit 1; }
test -x ${TELEGRAM_BINARY} || fail "${TELEGRAM_BINARY} is not executable"
test "$(cat /var/lib/crabbox/telegram-desktop-version 2>/dev/null)" = "${TELEGRAM_DESKTOP_VERSION}" || fail "/var/lib/crabbox/telegram-desktop-version is not ${TELEGRAM_DESKTOP_VERSION}"
# The marker sits beside the app, not in Crabbox's state dir: that directory is
# not traversable by the lease user on AWS desktop images.
test "$(cat /opt/Telegram/openclaw-image-version 2>/dev/null)" = "${TELEGRAM_DESKTOP_VERSION}" || fail "/opt/Telegram/openclaw-image-version is not ${TELEGRAM_DESKTOP_VERSION}"
for command in wmctrl xdotool scrot ffmpeg zbarimg xdpyinfo; do
command -v "$command" >/dev/null 2>&1 || fail "$command is not on PATH"
done
@@ -112,8 +114,11 @@ rm -rf ${shellQuote(TELEGRAM_WORKDIR)}
# setsid plus closed stdin detaches Telegram from this SSH session: container sshd
# tears down the session process group on exit, which kills a plain background child.
setsid ${TELEGRAM_BINARY} -noupdate -workdir ${shellQuote(TELEGRAM_WORKDIR)} </dev/null >${shellQuote(remotePaths.desktopLog)} 2>&1 &
# setsid execs in place, so this pid becomes Telegram; matching by name would miss
# the window between fork and exec on a cold, large binary.
telegram_pid=$!
for _ in $(seq 1 30); do
pgrep -x Telegram >/dev/null 2>&1 || { tail -c 262144 ${shellQuote(remotePaths.desktopLog)} >&2 || true; echo "Telegram Desktop exited before opening a window." >&2; exit 1; }
kill -0 "$telegram_pid" 2>/dev/null || { tail -c 262144 ${shellQuote(remotePaths.desktopLog)} >&2 || true; echo "Telegram Desktop exited before opening a window." >&2; exit 1; }
wmctrl -lx | awk 'tolower($0) ~ /telegramdesktop/ {found=1} END {exit !found}' && exit 0
sleep 1
done
+507
View File
@@ -0,0 +1,507 @@
#!/usr/bin/env bash
set -euo pipefail
telegram_version="7.0.9"
telegram_url="https://github.com/telegramdesktop/tdesktop/releases/download/v7.0.9/tsetup.7.0.9.tar.xz"
telegram_sha256="d3c05df0259ab116d11d8c1cdc1403019d2a3be303ad3b46d16a84e19df6615f"
variant_sdk="telegram-desktop=${telegram_version}"
region="eu-west-1"
server_class="standard"
server_type=""
image_name=""
output_file=""
crabbox_bin="crabbox"
run=0
promote=1
keep_lease=0
prep_only=0
usage() {
cat <<'USAGE'
Usage: scripts/mantis/bake-telegram-desktop-image.sh [flags]
Bake and optionally publish the catalog-only Telegram Desktop Crabbox image.
By default this prints the plan and exits. Add --run to create paid AWS
leases and an AMI.
Flags:
--region REGION AWS region. Default: eu-west-1.
--class CLASS Crabbox class. Default: standard.
--type TYPE Optional AWS instance type.
--name NAME Image name. Default: openclaw-telegram-desktop-<UTC timestamp>.
--output FILE JSON summary path. Default: .artifacts/mantis/telegram-desktop-image/<name>.json.
--run Create leases and the AMI.
--no-promote Smoke the candidate without publishing it.
--prep-only Prove the recipe on one lease (prep + smoke), then stop.
Creates no image and needs no coordinator admin.
--keep-lease Keep every lease created by this run.
--crabbox-bin PATH Crabbox executable. Default: crabbox from PATH.
-h, --help Show this help.
Required with --run:
Crabbox coordinator admin: run from a crabbox login listed in the
coordinator's CRABBOX_GITHUB_ADMIN_OWNERS, or set CRABBOX_COORDINATOR plus
CRABBOX_COORDINATOR_ADMIN_TOKEN for headless use. Image create/promote are
admin-only; the script probes for admin before paying for any lease.
Optional coordinator access:
CRABBOX_ACCESS_CLIENT_ID
CRABBOX_ACCESS_CLIENT_SECRET
USAGE
}
require_value() {
local value="${2:-}"
if [[ -z "$value" || "$value" == --* ]]; then
printf '%s requires a value\n' "$1" >&2
exit 2
fi
}
while [[ "$#" -gt 0 ]]; do
case "$1" in
--region)
require_value "$@"
region="$2"
shift 2
;;
--class)
require_value "$@"
server_class="$2"
shift 2
;;
--type)
require_value "$@"
server_type="$2"
shift 2
;;
--name)
require_value "$@"
image_name="$2"
shift 2
;;
--output)
require_value "$@"
output_file="$2"
shift 2
;;
--run)
run=1
shift
;;
--prep-only)
prep_only=1
shift
;;
--no-promote)
promote=0
shift
;;
--keep-lease)
keep_lease=1
shift
;;
--crabbox-bin)
require_value "$@"
crabbox_bin="$2"
shift 2
;;
-h | --help)
usage
exit 0
;;
*)
printf 'unknown argument: %s\n' "$1" >&2
usage >&2
exit 2
;;
esac
done
if [[ -z "$image_name" ]]; then
image_name="openclaw-telegram-desktop-$(date -u +%Y%m%d-%H%M)"
fi
if [[ -z "$output_file" ]]; then
output_file=".artifacts/mantis/telegram-desktop-image/${image_name}.json"
fi
cat <<EOF
Telegram Desktop image bake
image: $image_name
region: $region
class: $server_class
type: ${server_type:-auto}
version: $telegram_version
promote: $promote
prep only: $prep_only
keep leases: $keep_lease
output: $output_file
paid run: $run
EOF
if [[ "$run" != "1" ]]; then
printf 'dry plan only; add --run to create leases and an AMI.\n'
exit 0
fi
if ! crabbox_resolved="$(command -v "$crabbox_bin")"; then
printf 'Crabbox executable not found: %s\n' "$crabbox_bin" >&2
exit 2
fi
# Admin probe before any paid lease. image create/promote are admin-only, so a
# prep-only run — which creates no image — skips it. The
# probe asks the coordinator about a non-existent AMI: only an admin login gets
# through to the coordinator, which answers "not found"; a non-admin login is
# refused by the CLI before any request. Anything else (outage, auth, contract
# drift) fails closed rather than paying for leases first.
if [[ "$prep_only" == "1" ]]; then
admin_probe="not found"
else
admin_probe="$("$crabbox_resolved" image fsr-status ami-00000000000000000 --provider aws 2>&1 || true)"
fi
if [[ "$admin_probe" == *"not found"* ]]; then
:
elif [[ "$admin_probe" == *"admin command requires"* ]]; then
printf 'Crabbox coordinator admin is required with --run (CRABBOX_GITHUB_ADMIN_OWNERS or CRABBOX_COORDINATOR_ADMIN_TOKEN).\n' >&2
exit 2
else
printf 'Could not verify Crabbox coordinator admin access; refusing to start paid leases. Probe output:\n%s\n' "$admin_probe" >&2
exit 2
fi
if ! command -v jq >/dev/null 2>&1; then
printf 'jq is required with --run.\n' >&2
exit 2
fi
artifact_dir="$(dirname "$output_file")"
log_dir="$artifact_dir/logs"
mkdir -p "$log_dir"
prep_script="$(mktemp "${TMPDIR:-/tmp}/openclaw-telegram-image-prep.XXXXXX.sh")"
source_lease=""
candidate_lease=""
variant_lease=""
default_lease=""
cleanup() {
local status=$?
trap - EXIT
rm -f "$prep_script"
if [[ "$keep_lease" != "1" ]]; then
local lease
for lease in "$default_lease" "$variant_lease" "$candidate_lease" "$source_lease"; do
[[ -n "$lease" ]] || continue
"$crabbox_resolved" stop --provider aws --target linux "$lease" || true
done
fi
exit "$status"
}
trap cleanup EXIT
cat >"$prep_script" <<PREP
#!/usr/bin/env bash
set -euo pipefail
telegram_version="$telegram_version"
telegram_url="$telegram_url"
telegram_sha256="$telegram_sha256"
if [[ "\$(id -u)" -ne 0 ]]; then
if ! command -v sudo >/dev/null 2>&1; then
printf 'sudo is required to prepare the Telegram Desktop image.\\n' >&2
exit 2
fi
exec sudo -H env DEBIAN_FRONTEND=noninteractive bash "\$0"
fi
export DEBIAN_FRONTEND=noninteractive
retry() {
local attempt=1
until "\$@"; do
if [[ "\$attempt" -ge 5 ]]; then
return 1
fi
sleep "\$((attempt * 3))"
attempt="\$((attempt + 1))"
done
}
if [[ "\$(dpkg --print-architecture)" != "amd64" ]]; then
printf 'Telegram Desktop image requires amd64.\\n' >&2
exit 2
fi
retry apt-get update
retry apt-get install -y --no-install-recommends \
x11-utils \
zbar-tools \
libopengl0 \
libxcb-cursor0 \
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-randr0 \
libxcb-render-util0 \
libxcb-shape0 \
libxcb-xfixes0 \
libxcb-xinerama0 \
libxkbcommon-x11-0
marker=/opt/Telegram/openclaw-image-version
if [[ -x /opt/Telegram/Telegram && -f "\$marker" && "\$(<"\$marker")" == "\$telegram_version" ]]; then
rm -f /opt/Telegram/Updater
test -f /var/lib/crabbox/image-ready
exit 0
fi
tmp_dir="\$(mktemp -d /tmp/telegram-desktop-install.XXXXXX)"
trap 'rm -rf "\$tmp_dir"' EXIT
archive="\$tmp_dir/telegram.tar.xz"
retry curl -fL --retry 5 --retry-all-errors -o "\$archive" "\$telegram_url"
printf '%s %s\\n' "\$telegram_sha256" "\$archive" | sha256sum --check --status
tar -xJf "\$archive" -C "\$tmp_dir"
test -x "\$tmp_dir/Telegram/Telegram"
rm -rf /opt/Telegram
install -d -o root -g root -m 0755 /opt/Telegram
cp -a "\$tmp_dir/Telegram/." /opt/Telegram/
chown -R root:root /opt/Telegram
chmod 0755 /opt/Telegram /opt/Telegram/Telegram
rm -f /opt/Telegram/Updater
printf '%s\\n' "\$telegram_version" >"\$marker"
chmod 0644 "\$marker"
test -f /var/lib/crabbox/image-ready
PREP
chmod 0755 "$prep_script"
run_cmd() {
printf '+'
printf ' %q' "$@"
printf '\n'
"$@"
}
warmup() {
local label="$1"
local image_override="${2:-}"
local image_selector="${3:-}"
local log="$log_dir/${label}.log"
local -a args=(
warmup
--provider aws
--target linux
--desktop
--class "$server_class"
--market on-demand
--ttl 1h
--idle-timeout 20m
--timing-json
)
if [[ -n "$server_type" ]]; then
args+=(--type "$server_type")
fi
if [[ -n "$image_selector" ]]; then
args+=(--image-sdk "$image_selector")
fi
printf 'warming %s lease; log=%s\n' "$label" "$log" >&2
local status=0
if [[ -n "$image_override" ]]; then
run_cmd env CRABBOX_AWS_REGION="$region" CRABBOX_AWS_AMI="$image_override" \
"$crabbox_resolved" "${args[@]}" 2>&1 | tee "$log" >&2 || status=$?
else
run_cmd env CRABBOX_AWS_REGION="$region" \
"$crabbox_resolved" "${args[@]}" 2>&1 | tee "$log" >&2 || status=$?
fi
local lease
lease="$(grep -oE 'cbx_[A-Za-z0-9_-]+' "$log" | tail -n 1 || true)"
if [[ "$status" -ne 0 ]]; then
if [[ -n "$lease" && "$keep_lease" != "1" ]]; then
"$crabbox_resolved" stop --provider aws --target linux "$lease" || true
fi
return "$status"
fi
if [[ -z "$lease" ]]; then
printf 'warmup did not return a cbx lease id; log=%s\n' "$log" >&2
return 1
fi
printf '%s\n' "$lease"
}
selected_image_id() {
local log="$1"
sed -nE 's/.*image selected id=([^[:space:]]+) source=[^[:space:]]+.*/\1/p' "$log" | tail -n 1
}
assert_selected_image() {
local log="$1"
local image_id="$2"
local source="$3"
if ! grep -Fq "image selected id=${image_id} source=${source}" "$log"; then
printf 'warmup did not prove image selected id=%s source=%s; log=%s\n' \
"$image_id" "$source" "$log" >&2
return 1
fi
}
smoke() {
local lease="$1"
local smoke_script
read -r -d '' smoke_script <<'SMOKE' || true
set -euo pipefail
test -x /opt/Telegram/Telegram
test ! -e /opt/Telegram/Updater
test "$(</opt/Telegram/openclaw-image-version)" = "7.0.9"
for tool in wmctrl xdotool scrot ffmpeg zbarimg xdpyinfo; do
command -v "$tool" >/dev/null
done
DISPLAY=:99 xdpyinfo >/dev/null
rm -rf /tmp/tg-smoke
# Same launch contract the recorder uses: setsid with closed stdin survives the
# SSH session, and process-name matching never matches this script's own shell.
pkill -x Telegram >/dev/null 2>&1 || true
DISPLAY=:99 setsid /opt/Telegram/Telegram -noupdate -workdir /tmp/tg-smoke </dev/null >/tmp/tg-smoke.log 2>&1 &
# setsid execs in place, so this pid becomes Telegram; name matching would miss
# the window between fork and exec on a cold, large binary.
telegram_pid=$!
for _ in $(seq 1 30); do
kill -0 "$telegram_pid" 2>/dev/null || { tail -c 4096 /tmp/tg-smoke.log >&2 || true; echo "Telegram Desktop exited before opening a window." >&2; exit 1; }
DISPLAY=:99 wmctrl -lx | awk 'tolower($0) ~ /telegramdesktop/ {found=1} END {exit !found}' && { pkill -x Telegram >/dev/null 2>&1 || true; exit 0; }
sleep 1
done
tail -c 4096 /tmp/tg-smoke.log >&2 || true
echo "Telegram Desktop window did not open." >&2
exit 1
SMOKE
# --script, not --shell: a login shell runs ~/.bash_logout, whose clear_console
# fails without a console and replaces our exit status with 1.
local smoke_file
smoke_file="$(mktemp "${TMPDIR:-/tmp}/openclaw-telegram-image-smoke.XXXXXX.sh")"
printf '%s\n' "$smoke_script" >"$smoke_file"
chmod 0755 "$smoke_file"
run_cmd "$crabbox_resolved" run --provider aws --target linux --id "$lease" \
--no-sync --script "$smoke_file"
rm -f "$smoke_file"
}
stop_lease() {
local lease="$1"
if [[ "$keep_lease" != "1" ]]; then
run_cmd "$crabbox_resolved" stop --provider aws --target linux "$lease"
fi
}
source_lease="$(warmup source)"
run_cmd "$crabbox_resolved" run --provider aws --target linux --id "$source_lease" \
--no-sync --script "$prep_script"
smoke "$source_lease"
if [[ "$prep_only" == "1" ]]; then
printf 'prep-only run passed: the recipe boots Telegram Desktop %s on a real lease.\n' "$telegram_version"
jq -n \
--arg name "$image_name" \
--arg region "$region" \
--arg version "$telegram_version" \
--arg lease "$source_lease" \
'{name: $name, region: $region, telegramDesktopVersion: $version, lease: $lease, mode: "prep-only", proofs: {prep: "passed", smoke: "passed", imageCreated: false, promoted: false}}' \
| tee "$output_file"
exit 0
fi
image_json="$log_dir/image-create.json"
env CRABBOX_AWS_REGION="$region" "$crabbox_resolved" image create \
--id "$source_lease" --name "$image_name" --wait --json | tee "$image_json"
ami_id="$(jq -er '.id | select(type == "string" and startswith("ami-"))' "$image_json")"
if [[ "$keep_lease" != "1" ]]; then
stop_lease "$source_lease"
source_lease=""
fi
candidate_lease="$(warmup candidate "$ami_id")"
assert_selected_image "$log_dir/candidate.log" "$ami_id" explicit
smoke "$candidate_lease"
if [[ "$keep_lease" != "1" ]]; then
stop_lease "$candidate_lease"
candidate_lease=""
fi
promotion_status="skipped"
variant_selection="skipped"
variant_smoke="skipped"
default_unchanged="skipped"
if [[ "$promote" == "1" ]]; then
promote_json="$log_dir/image-promote.json"
env CRABBOX_AWS_REGION="$region" "$crabbox_resolved" image promote "$ami_id" \
--provider aws --target linux --region "$region" --desktop \
--catalog-only --variant-sdk "$variant_sdk" --json | tee "$promote_json"
jq -e --arg ami "$ami_id" '.id == $ami and .catalogOnly == true' "$promote_json" >/dev/null
promotion_status="catalog-only"
variant_lease="$(warmup variant "" "$variant_sdk")"
assert_selected_image "$log_dir/variant.log" "$ami_id" promoted
variant_selection="promoted"
smoke "$variant_lease"
variant_smoke="passed"
if [[ "$keep_lease" != "1" ]]; then
stop_lease "$variant_lease"
variant_lease=""
fi
default_lease="$(warmup default)"
default_image="$(selected_image_id "$log_dir/default.log")"
if [[ -z "$default_image" ]]; then
printf 'default warmup did not report its selected image; log=%s\n' "$log_dir/default.log" >&2
exit 1
fi
if [[ "$default_image" == "$ami_id" ]]; then
printf 'generic desktop selection changed to catalog-only AMI %s.\n' "$ami_id" >&2
exit 1
fi
default_unchanged="passed:${default_image}"
if [[ "$keep_lease" != "1" ]]; then
stop_lease "$default_lease"
default_lease=""
fi
fi
jq -n \
--arg amiId "$ami_id" \
--arg name "$image_name" \
--arg region "$region" \
--arg version "$telegram_version" \
--arg promotion "$promotion_status" \
--arg variantSelection "$variant_selection" \
--arg variantSmoke "$variant_smoke" \
--arg defaultUnchanged "$default_unchanged" \
'{
amiId: $amiId,
name: $name,
region: $region,
telegramDesktopVersion: $version,
proofs: {
sourceSmoke: "passed",
candidateSelection: "explicit",
candidateSmoke: "passed",
promotion: $promotion,
variantSelection: $variantSelection,
variantSmoke: $variantSmoke,
defaultUnchanged: $defaultUnchanged
}
}' >"$output_file"
cat <<EOF
Telegram Desktop image bake complete
AMI: $ami_id
name: $image_name
region: $region
source smoke: passed
candidate selection: explicit
candidate smoke: passed
promotion: $promotion_status
variant selection: $variant_selection
variant smoke: $variant_smoke
default unchanged: $default_unchanged
summary: $output_file
EOF
@@ -65,13 +65,13 @@ RUN set -eux; \
printf '%s %s\n' "$telegram_desktop_sha256" "$install_root/telegram.tar.xz" | sha256sum --check --status; \
tar -xJf "$install_root/telegram.tar.xz" -C "$install_root"; \
test -x "$install_root/Telegram/Telegram"; \
install -d -o root -g root -m 0755 /opt/Telegram /var/lib/crabbox; \
install -d -o root -g root -m 0755 /opt/Telegram; \
cp -a "$install_root/Telegram/." /opt/Telegram/; \
chown -R root:root /opt/Telegram; \
chmod 0755 /opt/Telegram /opt/Telegram/Telegram; \
rm -f /opt/Telegram/Updater; \
printf '%s\n' "$telegram_desktop_version" >/var/lib/crabbox/telegram-desktop-version; \
chmod 0644 /var/lib/crabbox/telegram-desktop-version; \
printf '%s\n' "$telegram_desktop_version" >/opt/Telegram/openclaw-image-version; \
chmod 0644 /opt/Telegram/openclaw-image-version; \
rm -rf "$install_root" /var/lib/apt/lists/*
# Fail the build if the base ever drops the account this image defaults to.
@@ -0,0 +1,79 @@
import { spawnSync } from "node:child_process";
import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
const SCRIPT = "scripts/mantis/bake-telegram-desktop-image.sh";
describe("Mantis Telegram Desktop image bake", () => {
it("pins Telegram and proves explicit, promoted, and unchanged-default selection", () => {
const script = readFileSync(SCRIPT, "utf8");
expect(script).toContain('telegram_version="7.0.9"');
expect(script).toContain(
'telegram_url="https://github.com/telegramdesktop/tdesktop/releases/download/v7.0.9/tsetup.7.0.9.tar.xz"',
);
expect(script).toContain(
'telegram_sha256="d3c05df0259ab116d11d8c1cdc1403019d2a3be303ad3b46d16a84e19df6615f"',
);
expect(script).toContain('--catalog-only --variant-sdk "$variant_sdk"');
expect(script).toContain('args+=(--image-sdk "$image_selector")');
expect(script).toContain('assert_selected_image "$log_dir/variant.log" "$ami_id" promoted');
expect(script).toContain('if [[ "$default_image" == "$ami_id" ]]');
expect(script).toContain("Crabbox coordinator admin is required with --run");
expect(script).toContain(
"Could not verify Crabbox coordinator admin access; refusing to start paid leases",
);
expect(script).not.toContain("ADMIN_TOKEN is required");
});
it("proves the image contract the way the recorder asserts it", () => {
const script = readFileSync(SCRIPT, "utf8");
// The marker lives with the app: Crabbox's state dir is not traversable by
// the lease user on AWS desktop images.
expect(script).toContain("marker=/opt/Telegram/openclaw-image-version");
expect(script).not.toContain("/var/lib/crabbox/telegram-desktop-version");
// setsid execs in place, so the launcher tracks its pid instead of matching
// by process name, which misses the fork-to-exec window on a cold binary.
expect(script).toContain("setsid /opt/Telegram/Telegram");
expect(script).toContain('kill -0 "$telegram_pid"');
// A login shell runs ~/.bash_logout, whose clear_console failure replaces the
// script's exit status, so remote payloads are uploaded, never inlined.
expect(script).toContain("--no-sync --script");
expect(script).not.toContain("--no-sync --shell");
});
it("runs prep-only without an image, a promotion, or coordinator admin", () => {
const script = readFileSync(SCRIPT, "utf8");
expect(script).toContain("--prep-only");
expect(script).toContain('if [[ "$prep_only" == "1" ]]; then\n admin_probe="not found"');
expect(script).toContain("prep-only run passed");
expect(script).toContain("imageCreated: false");
});
it("parses as Bash and performs no Crabbox work without --run", () => {
const syntax = spawnSync("bash", ["-n", SCRIPT], { encoding: "utf8" });
expect(syntax.stderr).toBe("");
expect(syntax.status).toBe(0);
const tempRoot = mkdtempSync(path.join(tmpdir(), "telegram-image-dry-run-"));
try {
const crabbox = path.join(tempRoot, "crabbox");
const marker = path.join(tempRoot, "called");
writeFileSync(crabbox, `#!/usr/bin/env bash\nprintf called >${JSON.stringify(marker)}\n`);
chmodSync(crabbox, 0o755);
const dryRun = spawnSync("bash", [SCRIPT, "--crabbox-bin", crabbox], {
encoding: "utf8",
});
expect(dryRun.status).toBe(0);
expect(dryRun.stdout).toContain("dry plan only");
expect(existsSync(marker)).toBe(false);
} finally {
rmSync(tempRoot, { recursive: true });
}
});
});
@@ -323,7 +323,7 @@ describe("Telegram Desktop recorder remote contract", () => {
expect(scripts).toContain("Telegram Desktop recorder golden image contract");
expect(scripts).toContain("/opt/Telegram/Telegram");
expect(scripts).toContain(
'test "$(cat /var/lib/crabbox/telegram-desktop-version 2>/dev/null)" = "7.0.9"',
'test "$(cat /opt/Telegram/openclaw-image-version 2>/dev/null)" = "7.0.9"',
);
expect(scripts).toContain("DISPLAY=:99 xdpyinfo");
expect(scripts).toContain("wmctrl xdotool scrot ffmpeg zbarimg xdpyinfo");
@@ -331,7 +331,9 @@ describe("Telegram Desktop recorder remote contract", () => {
// -f patterns also match this script's own shell (its command line contains the
// binary path), so a -f pkill kills the launcher instead of a stale Telegram.
expect(scripts).toContain("pkill -x Telegram");
expect(scripts).toContain("pgrep -x Telegram");
// setsid execs in place, so liveness follows the pid; name matching would miss
// the fork-to-exec window on a cold binary.
expect(scripts).toContain('kill -0 "$telegram_pid"');
expect(scripts).not.toMatch(/p(kill|grep) -f [^\n]*Telegram/u);
// Container sshd tears down the session process group; the client must detach.
expect(scripts).toContain("setsid /opt/Telegram/Telegram");