AnyBot/Makefile

141 lines
6.3 KiB
Makefile
Raw Normal View History

# AnyBot — build and install from source. make help lists everything.
#
# git clone https://code.30cm.net/daniel.w/AnyBot.git && cd AnyBot
# make deps everything the build needs (system packages, Rust) — once
# make install build and install: ~/Applications/AnyBot.app on a Mac, ~/.local/bin on Linux
# make run open it without installing
#
# The one-step version of all of this is ./install.sh (README.md).
# Rust installed by rustup lives in ~/.cargo/bin, which a fresh shell may not have on PATH yet.
export PATH := $(HOME)/.cargo/bin:$(PATH)
CARGO ?= cargo
UNAME := $(shell uname -s)
# `git describe` prints nothing outside a repo; sed passes the empty string through with
# status 0, so "|| echo dev" never fired and VERSION came out blank ("AnyBot ", dist files
# named ".tar.gz"). grep . fails on empty input, which makes the fallback work.
VERSION ?= $(shell git describe --tags --always 2>/dev/null | sed 's/^v//' | grep . || echo dev)
TARGET := $(or $(CARGO_TARGET_DIR),target)
APPDIR ?= $(HOME)/Applications
BIN := $(TARGET)/release/anybot
RELAY := $(if $(CARGO_TARGET_DIR),$(CARGO_TARGET_DIR),relay/target)/release/anybot-relay
.PHONY: all build app relay run serve setup relay-run test deps doctor install uninstall hands server dist clean help check-rust lint-sh
all: build
help:
@echo "AnyBot $(VERSION)"
@echo ""
@echo " make 建置 AnyBot 和帳號伺服器(正式版)"
@echo " make run 直接打開 AnyBot(不安裝)"
@echo " make install 安裝:Mac → $(APPDIR)/AnyBot.app;Linux → ~/.local/bin/anybot 和選單"
@echo " make uninstall 移除安裝的程式(你的 Bot 和聊天不會刪)"
@echo " make deps 裝好建置需要的東西:系統套件和 Rust(Linux 會問 sudo 密碼)"
@echo " make doctor 檢查這台缺什麼"
@echo " make test 跑測試"
@echo " make lint-sh 檢查 shell 腳本(語法、中文標點黏變數)"
@echo ""
@echo " make serve 不開視窗執行(伺服器、一直開著的電腦)"
@echo " make setup 把這台接到你的 AnyBot(終端機裡的引導畫面)"
@echo " make hands Linux:裝好虛擬螢幕等,再接成大腦的手腳"
@echo " make relay-run 在這台跑帳號伺服器(埠 8080,資料在 ./relay-data)"
@echo " make server 在伺服器上架好帳號伺服器 + 大腦 + HTTPS(Docker)"
@echo ""
@echo " make dist 打包發行檔到 dist/(Mac:.app 的 zip;Linux:三個 tar.gz)"
@echo " make clean 清掉建置結果"
build: app relay
check-rust:
@command -v $(CARGO) >/dev/null 2>&1 || { echo; echo " ✗ 找不到 Rust(cargo)。先執行:make deps"; echo; exit 1; }
@set -- $$(rustc --version | sed -n 's/^rustc \([0-9][0-9]*\)\.\([0-9][0-9]*\).*/\1 \2/p'); { [ "$${1:-0}" -gt 1 ] || { [ "$${1:-0}" -eq 1 ] && [ "$${2:-0}" -ge 95 ]; }; } || { echo; echo " ✗ Rust 太舊($$(rustc --version)),要 1.95 以上。執行:rustup update stable(或 make deps)"; echo; exit 1; }
app: check-rust
$(CARGO) build --release --bin anybot
relay: check-rust
cd relay && $(CARGO) build --release
run: app
$(BIN)
serve: app
$(BIN) --serve
setup: app
$(BIN) setup
relay-run: relay
$(RELAY) --port 8080 --data relay-data
test: check-rust
$(CARGO) test --bin anybot
cd relay && $(CARGO) test
lint-sh:
@sh deploy/ci/lint-sh.sh
deps:
sh ./install.sh deps
doctor:
@echo "AnyBot $(VERSION) · $(UNAME) $$(uname -m)"
@command -v git >/dev/null && echo " ✓ git" || echo " ✗ git:make deps"
@command -v cc >/dev/null && echo " ✓ C 編譯器" || echo " ✗ C 編譯器:make deps"
@command -v $(CARGO) >/dev/null && echo " ✓ $$(rustc --version)" || echo " ✗ Rust:make deps"
ifeq ($(UNAME),Linux)
@pkg-config --exists alsa 2>/dev/null && echo " ✓ ALSA(libasound2-dev)" || echo " ✗ ALSA 開發套件:make deps"
@command -v docker >/dev/null && echo " ✓ $$(docker --version)" || echo " · 沒有 Docker(只有伺服器和 Bot 自己的電腦需要;make server 會自動裝)"
endif
ifeq ($(UNAME),Darwin)
@xcode-select -p >/dev/null 2>&1 && echo " ✓ Xcode Command Line Tools" || echo " ✗ Xcode Command Line Tools:make deps"
endif
@test -x $(BIN) && echo " ✓ 已建置 $(BIN)" || echo " · 還沒建置:make"
Bots do a planned sequence in one step; the box's screen settles by what it shows; no login is ever the only copy - computer_use_batch: up to 8 planned actions run in one docker exec (steps chained with && and a done-marker each), the first failure stops the rest, one picture comes back at the end. Typed text rides inside the script quoted (one stdin for the chain). The prompt tells the model when to use it (a form whose fields are all visible) and when not to (step 2 needs step 1's screen). - The box waits for the screen to stop changing, not the clock: two matching small probes (~60 ms each) mean settled — ~160 ms on a still screen where every step paid a fixed 500 ms, and a page mid-load is waited for (up to ~0.9 s) instead of captured mid-change. The picture is JPEG, and the a11y read runs in parallel with the capture (look ~307 ms; two steps with pictures 1102 ms). (SETTLE_SH is a group command: dash needs ';' after it — found by box_screen_is_exact.) - The home volume (every bot's logins) is never one command from loss: snapshots keep generations (last 7 days, then one per week; the old single snapshot migrates by file date), Reset and Restore save the running box first and refuse to wipe if saving fails, a box built on an empty volume re-fills itself from the newest snapshot, and a sleeping holder container keeps the volume attached so `docker volume prune` can't take it. make doctor shows volume and snapshot state; the 電腦 card shows the box's average step times and says resets save first. - box_batch_in_one_round_trip (ignored): three typed steps, ASCII and Chinese, land in one trip. On the real box after upgrading v3→v5: box_update_keeps_files (files survive, snapshot 2 代), box_numbers_are_exact, box_screen_is_exact, box_end_to_end, box_traffic_via_mac pass; 155 unit tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 01:47:37 +00:00
@command -v docker >/dev/null 2>&1 && { docker volume inspect anybot-box-home >/dev/null 2>&1 && echo " ✓ Bot 的電腦的資料碟 anybot-box-home(檔案和登入都在裡面)" || echo " · 沒有 anybot-box-home 資料碟(Bot 的電腦還沒建立過,或被清除過——有快照會在下次建立時自動還原)"; } || true
@d="$${HOME}/Library/Application Support/AnyBot/box/snapshots"; [ -d "$$d" ] || d="$${HOME}/.local/share/AnyBot/box/snapshots"; [ -d "$$d" ] && echo " ✓ 快照 $$(ls "$$d" 2>/dev/null | grep -c '^snapshot-') 份(留最近 7 天+之前每週一份;重設前也會先存)" || echo " · 還沒有快照(Bot 的電腦每天自動存,重設前也會先存)"
install: app
ifeq ($(UNAME),Darwin)
KEEP_APP=1 sh deploy/mac/bundle.sh $(VERSION)
mkdir -p "$(APPDIR)"
rm -rf "$(APPDIR)/AnyBot.app"
mv dist/AnyBot.app "$(APPDIR)/AnyBot.app"
mkdir -p "$(HOME)/.local/bin"
ln -sfn "$(APPDIR)/AnyBot.app/Contents/MacOS/AnyBot" "$(HOME)/.local/bin/anybot"
@echo "已安裝 $(APPDIR)/AnyBot.app(第一次打開:按右鍵 › 打開)"
else ifneq ($(findstring MINGW,$(UNAME)),)
powershell.exe -NoProfile -ExecutionPolicy Bypass -File install.ps1 app
else ifeq ($(OS),Windows_NT)
powershell.exe -NoProfile -ExecutionPolicy Bypass -File install.ps1 app
else
sh deploy/linux/install.sh
endif
uninstall:
ifeq ($(UNAME),Darwin)
rm -rf "$(APPDIR)/AnyBot.app"
else
rm -f "$(HOME)/.local/bin/anybot" "$(HOME)/.local/share/applications/anybot.desktop"
rm -f "$(HOME)/.local/share/icons/hicolor/scalable/apps/anybot.svg" "$(HOME)/.local/share/icons/hicolor/256x256/apps/anybot.png" "$(HOME)/.local/share/icons/hicolor/128x128/apps/anybot.png"
endif
@echo "已移除程式。你的 Bot、聊天和設定還在(要全部刪掉請看 docs/install.md)。"
hands: app
sh deploy/linux/hands.sh --deps
server:
sh ./install.sh server
dist: build
ifeq ($(UNAME),Darwin)
sh deploy/mac/bundle.sh $(VERSION)
else
sh deploy/ci/package-linux.sh $(VERSION)
endif
clean:
$(CARGO) clean
cd relay && $(CARGO) clean
rm -rf dist