fix(installer): handle headless onboarding tty

This commit is contained in:
Peter Steinberger
2026-05-21 21:15:46 +01:00
parent b77f36fb1c
commit 504f0dfa36
5 changed files with 32 additions and 9 deletions
+1 -3
View File
@@ -76,11 +76,9 @@ jobs:
- name: install.sh in Docker
run: |
docker run --rm \
-e OPENCLAW_NO_ONBOARD=1 \
-e OPENCLAW_NO_PROMPT=1 \
-v "$PWD/scripts/install.sh:/tmp/install.sh:ro" \
node:24-bookworm-slim \
bash -lc 'bash /tmp/install.sh --no-prompt --no-onboard --version latest && openclaw --version'
bash -lc 'bash /tmp/install.sh --version latest && openclaw --version'
- name: install-cli.sh in Docker
run: |
+1
View File
@@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
- Providers/Ollama: treat Docker/OrbStack host aliases as local Ollama endpoints so `ollama-local` marker auth works when OpenClaw runs inside a VM/container and Ollama runs on the host. Fixes #84875.
- QA-Lab: keep explicitly searchable/deferred OpenClaw dynamic tool rows report-only by default so tool-coverage gates do not treat mock discovery gaps as hard product failures. (#80319) Thanks @100yenadmin.
- Agents/config: keep non-Google provider model refs from being rewritten by Google Gemini preview-id normalization. (#84762) Thanks @zhangguiping-xydt.
- Installer: require a real controlling terminal before launching onboarding so headless `curl | bash` installs finish cleanly after installing the CLI.
- Agents: cap heartbeat model bleed context hints by the stored session window when runtime model metadata is unavailable, so overflow recovery advice does not suggest a larger window than the active session actually has.
- Control UI/Web Push: use `https://openclaw.ai` as the generated default VAPID subject instead of the old localhost mailbox so iOS PWA push setup uses an Apple-acceptable subject when `OPENCLAW_VAPID_SUBJECT` is unset. Fixes #83134. (#83317) Thanks @IWhatsskill.
- Agents/Pi: keep embedded session transcript writes from tripping false takeover detection after packaged npm onboarding agent turns.
+15 -5
View File
@@ -90,6 +90,16 @@ is_non_interactive_shell() {
return 1
}
has_controlling_tty() {
if [[ ! -r /dev/tty || ! -w /dev/tty ]]; then
return 1
fi
if ! { : </dev/tty; } 2>/dev/null; then
return 1
fi
return 0
}
gum_is_tty() {
if [[ -n "${NO_COLOR:-}" ]]; then
return 1
@@ -100,7 +110,7 @@ gum_is_tty() {
if [[ -t 2 || -t 1 ]]; then
return 0
fi
if [[ -r /dev/tty && -w /dev/tty ]]; then
if has_controlling_tty; then
return 0
fi
return 1
@@ -1152,7 +1162,7 @@ is_promptable() {
if [[ "$NO_PROMPT" == "1" ]]; then
return 1
fi
if [[ -r /dev/tty && -w /dev/tty ]]; then
if has_controlling_tty; then
return 0
fi
return 1
@@ -2577,7 +2587,7 @@ run_bootstrap_onboarding_if_needed() {
return
fi
if [[ ! -r /dev/tty || ! -w /dev/tty ]]; then
if ! is_promptable; then
local user_claw
user_claw="$(openclaw_command_for_user "${OPENCLAW_BIN:-}")"
ui_info "BOOTSTRAP.md found but no TTY; run ${user_claw} onboard to finish setup"
@@ -2962,7 +2972,7 @@ main() {
ui_kv "Switch to npm" "curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash -s -- --install-method npm"
elif [[ "$is_upgrade" == "true" ]]; then
ui_info "Upgrade complete"
if [[ -r /dev/tty && -w /dev/tty ]]; then
if has_controlling_tty || [[ "$NO_ONBOARD" == "1" || "$NO_PROMPT" == "1" ]]; then
local claw="${OPENCLAW_BIN:-}"
if [[ -z "$claw" ]]; then
claw="$(resolve_openclaw_bin || true)"
@@ -3010,7 +3020,7 @@ main() {
fi
ui_info "Starting setup"
echo ""
if [[ -r /dev/tty && -w /dev/tty ]]; then
if is_promptable; then
local claw="${OPENCLAW_BIN:-}"
if [[ -z "$claw" ]]; then
claw="$(resolve_openclaw_bin || true)"
+13
View File
@@ -431,6 +431,19 @@ describe("install.sh", () => {
expect(script).toContain('corepack prepare "pnpm@${version}" --activate');
expect(script).toContain('activate_repo_pnpm_version "$repo_dir"');
});
it("does not treat /dev/tty permissions as a controlling terminal", () => {
const result = runInstallShell(`
set -euo pipefail
source "${SCRIPT_PATH}"
if has_controlling_tty; then echo "has_tty=1"; else echo "has_tty=0"; fi
if is_promptable; then echo "promptable=1"; else echo "promptable=0"; fi
`);
expect(result.status).toBe(0);
expect(result.stdout).toContain("has_tty=0");
expect(result.stdout).toContain("promptable=0");
});
});
describe("install.sh macOS Homebrew Node behavior", () => {
@@ -23,7 +23,8 @@ describe("website installer sync workflow", () => {
it("verifies installers on Linux Docker plus native macOS and Windows runners", () => {
expect(workflow).toContain("linux-docker:");
expect(workflow).toContain("docker run --rm");
expect(workflow).toContain("bash /tmp/install.sh --no-prompt --no-onboard");
expect(workflow).toContain("bash /tmp/install.sh --version latest && openclaw --version");
expect(workflow).not.toContain("bash /tmp/install.sh --no-prompt --no-onboard");
expect(workflow).toContain("bash /tmp/install-cli.sh --prefix /tmp/openclaw");
expect(workflow).toContain("macos-installer:");
expect(workflow).toContain("runs-on: macos-latest");