make, README and docs: build and install from source
- Makefile: make / run / install / uninstall / deps / test / serve / setup / hands / relay-run / server / dist / clean (macOS and Linux). - README.md and docs/: install (all platforms, data locations, troubleshooting), multi-device (brain, hands, screens, pairing, what the offline messages mean), architecture (layers, data, security, sync, keys, hands, picture helper), protocol (relay and AnyBot endpoints, WebSocket frames, sync ops), release (packaging, Gitea Actions, Linux and Mac runners). - install.sh --deps-only, bundle.sh KEEP_APP=1 (used by make deps / install). - FEATURES.md: the multi-device part is done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
This commit is contained in:
parent
87be66b016
commit
867732bd73
|
|
@ -2074,10 +2074,17 @@ computer_use_screenshot、click、mouse_move、mouse_button(按住/放開)
|
||||||
| 邊說邊插話的即時語音 | 原版用 xAI 即時語音服務;AnyBot 的通話模式是輪流說話 |
|
| 邊說邊插話的即時語音 | 原版用 xAI 即時語音服務;AnyBot 的通話模式是輪流說話 |
|
||||||
| 帳單、方案 | 不需要:AnyBot 用你自己的 AI 金鑰 |
|
| 帳單、方案 | 不需要:AnyBot 用你自己的 AI 金鑰 |
|
||||||
| 代你購物與付款 | 刻意不做:會動到你的錢和卡片 |
|
| 代你購物與付款 | 刻意不做:會動到你的錢和卡片 |
|
||||||
| 即時同步(同一個帳號的兩台電腦同時改同一個 Bot) | 帳號只做每日加密備份和還原;要 24 小時運作,請把 AnyBot 放在一直開著的主機(第 36 節) |
|
|
||||||
| 原生 iPhone/Android App | 用手機網頁版,可加到主畫面、收推播通知 |
|
| 原生 iPhone/Android App | 用手機網頁版,可加到主畫面、收推播通知 |
|
||||||
|
|
||||||
|
|
||||||
|
## 多台電腦:大腦、手腳、螢幕(2026-11-04)
|
||||||
|
|
||||||
|
- 帳號裡一台 AnyBot 當大腦(Bot、聊天、記憶、排程都在它上面),其他有視窗的電腦當螢幕(看到、改的都是大腦的 Bot),沒有螢幕的電腦當手腳;同一個帳號的電腦之間即時同步。
|
||||||
|
- 每台 AnyBot 和帳號伺服器之間保持一條長連線(WebSocket),斷了自動接回;連不到時畫面說明是哪一段。
|
||||||
|
- 「加一台電腦」給一行 `anybot setup 網址 配對碼`,新電腦不用密碼就接成那個大腦的手腳。
|
||||||
|
- 看不到圖片的模型:你帳號裡看得到圖的模型會先描述圖片(看圖小幫手)。
|
||||||
|
- 詳細說明:docs/multi-device.md;技術細節:docs/architecture.md、docs/protocol.md。
|
||||||
|
|
||||||
## 側邊欄:釘選與分區(照原版重做,2026-11-04)
|
## 側邊欄:釘選與分區(照原版重做,2026-11-04)
|
||||||
|
|
||||||
- 釘選的 Bot 放在最上面的大頭像格子裡(主要 Bot 第一個,有星星)。右鍵「釘選/取消釘選」,或把 Bot 拖進格子(第一次拖時出現「拖曳到此處以釘選」);格子內可拖曳排序;拖到分區會取消釘選並移過去。
|
- 釘選的 Bot 放在最上面的大頭像格子裡(主要 Bot 第一個,有星星)。右鍵「釘選/取消釘選」,或把 Bot 拖進格子(第一次拖時出現「拖曳到此處以釘選」);格子內可拖曳排序;拖到分區會取消釘選並移過去。
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
# AnyBot — build and install from source. make help lists everything.
|
||||||
|
#
|
||||||
|
# git clone git@git.30cm.net:daniel.w/AnyBot.git && cd AnyBot
|
||||||
|
# make build (release)
|
||||||
|
# make install install for you: ~/Applications/AnyBot.app on a Mac, ~/.local/bin on Linux
|
||||||
|
# make run open it without installing
|
||||||
|
|
||||||
|
CARGO ?= cargo
|
||||||
|
UNAME := $(shell uname -s)
|
||||||
|
VERSION ?= $(shell git describe --tags --always 2>/dev/null | sed 's/^v//' || 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 install uninstall hands server dist clean help
|
||||||
|
|
||||||
|
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 Linux:安裝系統套件(會問 sudo 密碼)"
|
||||||
|
@echo " make test 跑測試"
|
||||||
|
@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
|
||||||
|
|
||||||
|
app:
|
||||||
|
$(CARGO) build --release --bin anybot
|
||||||
|
|
||||||
|
relay:
|
||||||
|
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:
|
||||||
|
$(CARGO) test --bin anybot
|
||||||
|
cd relay && $(CARGO) test
|
||||||
|
|
||||||
|
deps:
|
||||||
|
ifeq ($(UNAME),Linux)
|
||||||
|
sh deploy/linux/install.sh --deps-only
|
||||||
|
else ifeq ($(UNAME),Darwin)
|
||||||
|
@xcode-select -p >/dev/null 2>&1 || xcode-select --install
|
||||||
|
@command -v cargo >/dev/null || echo "需要 Rust:curl --proto '=https' -sSf https://sh.rustup.rs | sh"
|
||||||
|
else
|
||||||
|
@echo "Windows:請照 docs/install.md 安裝 Rust 和 Visual Studio Build Tools"
|
||||||
|
endif
|
||||||
|
|
||||||
|
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"
|
||||||
|
@echo "已安裝 $(APPDIR)/AnyBot.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:
|
||||||
|
cd deploy/server && ./setup.sh
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
# AnyBot
|
||||||
|
|
||||||
|
你自己的 AI Bot:可以聊天,也能在它自己的電腦和你的電腦上幫你做事(檔案、指令、瀏覽器、螢幕操作、例行任務)。每個 Bot 可以用不同的 AI 服務:xAI Grok、OpenAI、Claude、Gemini、OpenRouter、OpenCode Go、Ollama(本機),或任何 OpenAI 相容的服務。
|
||||||
|
|
||||||
|
- 桌面 App:macOS、Linux(Windows 可以編譯,還沒有安裝包)
|
||||||
|
- 手機:網頁版,可加到主畫面、收推播通知
|
||||||
|
- 多台電腦:一台當**大腦**(保存 Bot、聊天、記憶和排程),其他電腦當**手腳**或**螢幕**
|
||||||
|
- 用 Rust 和 egui 寫成,一個執行檔就是全部(視窗、無視窗伺服器模式、設定工具)
|
||||||
|
|
||||||
|
## 從原始碼安裝
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone git@git.30cm.net:daniel.w/AnyBot.git
|
||||||
|
cd AnyBot
|
||||||
|
make deps # 只有 Linux 需要:安裝系統套件(會問 sudo 密碼)
|
||||||
|
make # 建置
|
||||||
|
make install # Mac → ~/Applications/AnyBot.app;Linux → ~/.local/bin/anybot 和選單
|
||||||
|
```
|
||||||
|
|
||||||
|
需要 Rust(`curl --proto '=https' -sSf https://sh.rustup.rs | sh`)。Mac 另外需要 Xcode Command Line Tools(`make deps` 會提示)。詳細步驟、各平台需求和常見問題:[docs/install.md](docs/install.md)。
|
||||||
|
|
||||||
|
常用指令(`make help` 列出全部):
|
||||||
|
|
||||||
|
| 指令 | 做什麼 |
|
||||||
|
|---|---|
|
||||||
|
| `make run` | 不安裝,直接打開 |
|
||||||
|
| `make test` | 跑測試 |
|
||||||
|
| `make serve` | 不開視窗執行(伺服器、一直開著的電腦) |
|
||||||
|
| `make setup` | 把這台接到你的 AnyBot(終端機裡的引導畫面) |
|
||||||
|
| `make server` | 在伺服器上架好帳號伺服器、大腦和 HTTPS(Docker) |
|
||||||
|
| `make dist` | 打包發行檔到 `dist/` |
|
||||||
|
| `make uninstall` | 移除程式(Bot 和聊天不會刪) |
|
||||||
|
|
||||||
|
## 多台電腦一起用
|
||||||
|
|
||||||
|
```
|
||||||
|
手機(網頁) 筆電(螢幕) 沒螢幕的 Linux(手腳)
|
||||||
|
\ | /
|
||||||
|
└──── 帳號伺服器(一條一直開著的連線)────┘
|
||||||
|
|
|
||||||
|
大腦:Bot、聊天、記憶、排程
|
||||||
|
```
|
||||||
|
|
||||||
|
1. **架帳號伺服器和大腦**:在伺服器上執行 `make server`(只問網域、帳號、密碼);或先用一台 Mac 當大腦([docs/multi-device.md](docs/multi-device.md))。
|
||||||
|
2. **其他電腦當螢幕**:打開 AnyBot › 設定 › 手機與帳號,登入同一個帳號,在「大腦」選它。
|
||||||
|
3. **加一台手腳**:手機 AnyBot 右上「⋯ › 加一台電腦」,把出現的那一行貼到那台的終端機:
|
||||||
|
```sh
|
||||||
|
anybot setup https://anybot.example.com 469615
|
||||||
|
```
|
||||||
|
不用帳號密碼,會設成開機自動執行;沒有螢幕就開一個虛擬螢幕。
|
||||||
|
|
||||||
|
## 文件
|
||||||
|
|
||||||
|
| 文件 | 內容 |
|
||||||
|
|---|---|
|
||||||
|
| [docs/install.md](docs/install.md) | 從原始碼建置和安裝(macOS、Linux、Windows)、更新、移除、常見問題 |
|
||||||
|
| [docs/multi-device.md](docs/multi-device.md) | 大腦、手腳、螢幕:怎麼架、怎麼接、連不上時看什麼 |
|
||||||
|
| [docs/architecture.md](docs/architecture.md) | 技術架構:程式分層、資料存放、安全設計、同步怎麼運作 |
|
||||||
|
| [docs/protocol.md](docs/protocol.md) | 帳號伺服器和 AnyBot 之間的介面與長連線格式 |
|
||||||
|
| [docs/release.md](docs/release.md) | 發行:打包、Gitea Actions 自動建置、runner 設定 |
|
||||||
|
| [FEATURES.md](FEATURES.md) | 所有功能的完整說明 |
|
||||||
|
| [relay/README.md](relay/README.md) | 帳號伺服器的架設細節 |
|
||||||
|
|
||||||
|
## 專案結構
|
||||||
|
|
||||||
|
```
|
||||||
|
src/ AnyBot(桌面 App、無視窗模式、anybot setup)
|
||||||
|
app/ 視窗介面(聊天、設定、電腦、大腦同步的畫面)
|
||||||
|
relay/ 帳號伺服器(獨立的小程式)
|
||||||
|
mobile/ 手機網頁(由 AnyBot 和帳號伺服器提供)
|
||||||
|
box/ Bot 自己的電腦(Docker 容器)
|
||||||
|
deploy/ 安裝與部署:mac/、linux/、server/、ci/
|
||||||
|
docs/ 技術文件
|
||||||
|
```
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#
|
#
|
||||||
# ./deploy/linux/install.sh build from source, then install
|
# ./deploy/linux/install.sh build from source, then install
|
||||||
# ./deploy/linux/install.sh --deps also install the system packages (asks for sudo)
|
# ./deploy/linux/install.sh --deps also install the system packages (asks for sudo)
|
||||||
|
# ./deploy/linux/install.sh --deps-only only the system packages (make deps)
|
||||||
#
|
#
|
||||||
# Puts the app in ~/.local/bin/anybot, the menu entry in ~/.local/share/applications and the
|
# Puts the app in ~/.local/bin/anybot, the menu entry in ~/.local/share/applications and the
|
||||||
# icon in ~/.local/share/icons. Run it from the app/ folder.
|
# icon in ~/.local/share/icons. Run it from the app/ folder.
|
||||||
|
|
@ -21,7 +22,7 @@ DEPS_FEDORA="alsa-lib libxkbcommon-x11 mesa-libGL xdg-utils libnotify libsecret
|
||||||
DEPS_ARCH="alsa-lib libxkbcommon-x11 mesa xdg-utils libnotify libsecret espeak-ng zenity xclip wl-clipboard ffmpeg docker noto-fonts-cjk"
|
DEPS_ARCH="alsa-lib libxkbcommon-x11 mesa xdg-utils libnotify libsecret espeak-ng zenity xclip wl-clipboard ffmpeg docker noto-fonts-cjk"
|
||||||
BUILD_DEBIAN="build-essential pkg-config libasound2-dev"
|
BUILD_DEBIAN="build-essential pkg-config libasound2-dev"
|
||||||
|
|
||||||
if [ "$1" = "--deps" ]; then
|
if [ "$1" = "--deps" ] || [ "$1" = "--deps-only" ]; then
|
||||||
if command -v apt-get >/dev/null; then
|
if command -v apt-get >/dev/null; then
|
||||||
sudo apt-get update && sudo apt-get install -y $DEPS_DEBIAN $BUILD_DEBIAN
|
sudo apt-get update && sudo apt-get install -y $DEPS_DEBIAN $BUILD_DEBIAN
|
||||||
elif command -v dnf >/dev/null; then
|
elif command -v dnf >/dev/null; then
|
||||||
|
|
@ -37,6 +38,8 @@ if [ "$1" = "--deps" ]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
[ "$1" = "--deps-only" ] && exit 0
|
||||||
|
|
||||||
OUT="${CARGO_TARGET_DIR:-target}/release/anybot"
|
OUT="${CARGO_TARGET_DIR:-target}/release/anybot"
|
||||||
if [ -x "$OUT" ] && [ "$1" != "--rebuild" ]; then
|
if [ -x "$OUT" ] && [ "$1" != "--rebuild" ]; then
|
||||||
echo "Using the existing build ($OUT)."
|
echo "Using the existing build ($OUT)."
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Make AnyBot.app from the release build and zip it (run from app/ on a Mac, after
|
# Make AnyBot.app from the release build and zip it (run from app/ on a Mac, after
|
||||||
# `cargo build --release`): deploy/mac/bundle.sh 1.2.0 → dist/AnyBot-1.2.0-macos-<arch>.zip
|
# `cargo build --release`): deploy/mac/bundle.sh 1.2.0 → dist/AnyBot-1.2.0-macos-<arch>.zip
|
||||||
|
# With KEEP_APP=1 it leaves dist/AnyBot.app instead of zipping it (make install).
|
||||||
# Signed ad hoc (no Apple developer account): the first time, right-click › 打開.
|
# Signed ad hoc (no Apple developer account): the first time, right-click › 打開.
|
||||||
set -e
|
set -e
|
||||||
VERSION=${1:?version}
|
VERSION=${1:?version}
|
||||||
|
|
@ -39,6 +40,9 @@ cat > "$APP/Contents/Info.plist" <<PLIST
|
||||||
</dict></plist>
|
</dict></plist>
|
||||||
PLIST
|
PLIST
|
||||||
codesign --force --deep -s - "$APP"
|
codesign --force --deep -s - "$APP"
|
||||||
|
if [ "${KEEP_APP:-}" = 1 ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
ditto -c -k --norsrc --keepParent "$APP" "$OUT/AnyBot-$VERSION-macos-$ARCH.zip"
|
ditto -c -k --norsrc --keepParent "$APP" "$OUT/AnyBot-$VERSION-macos-$ARCH.zip"
|
||||||
rm -rf "$APP"
|
rm -rf "$APP"
|
||||||
ls -la "$OUT"
|
ls -la "$OUT"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
# 技術架構
|
||||||
|
|
||||||
|
AnyBot 是一個 Rust 程式(`anybot`),用 egui/eframe 畫介面。同一個執行檔有三種跑法:
|
||||||
|
|
||||||
|
| 跑法 | 指令 | 用途 |
|
||||||
|
|---|---|---|
|
||||||
|
| 視窗 | `anybot` | 桌面 App |
|
||||||
|
| 無視窗 | `anybot --serve` | 伺服器、一直開著的電腦、手腳:介面邏輯照跑,只是不畫畫面 |
|
||||||
|
| 設定 | `anybot setup …` | 終端機裡把這台接到帳號(`src/setup.rs`) |
|
||||||
|
|
||||||
|
另一個小程式 `anybot-relay`(`relay/`)是帳號伺服器。
|
||||||
|
|
||||||
|
## 程式分層
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────── 介面(src/app.rs、src/app/*)───────────────┐
|
||||||
|
│ 聊天、側邊欄、設定、電腦、檔案預覽、大腦同步(mirror_ui) │
|
||||||
|
│ 每個畫面一個 App 方法;狀態全部在 App.state(model::State) │
|
||||||
|
└───────┬───────────────────────────────┬────────────────────┘
|
||||||
|
│ 送出訊息 │ 手機/其他 AnyBot 的要求
|
||||||
|
┌───────▼──────────┐ ┌────────▼───────────────────┐
|
||||||
|
│ 回合(agent.rs) │ │ 遠端(remote.rs) │
|
||||||
|
│ 呼叫模型、跑工具、 │ │ 手機 API、裝置連結、大腦同步 │
|
||||||
|
│ 等你核准、整理摘要 │ │ 長連線(tunnel.rs、wire.rs) │
|
||||||
|
└───┬─────────┬────┘ └──────────────────────────────┘
|
||||||
|
│ │
|
||||||
|
┌───▼───┐ ┌───▼──────────────────────────────────────────────┐
|
||||||
|
│ 模型 │ │ 工具(tools.rs 為主) │
|
||||||
|
│ provi- │ │ 檔案與指令、Bot 的電腦(cloudbox.rs)、瀏覽器 │
|
||||||
|
│ ders.rs│ │ (botbrowser.rs、browser.rs、web.rs)、螢幕操作 │
|
||||||
|
└────────┘ │ (computer.rs、desktop.rs、axread.rs;Linux:x11.rs、 │
|
||||||
|
│ wl.rs、portal.rs、desk.rs)、Mac App(macapps.rs)、 │
|
||||||
|
│ 保險庫(vault*.rs)、MCP(mcp.rs)、記憶(memory.rs) │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
| 檔案 | 做什麼 |
|
||||||
|
|---|---|
|
||||||
|
| `model.rs` | 資料:State、Bot、Message、設定;讀寫 `state.json` |
|
||||||
|
| `agent.rs` | 一個回合:組系統指示、送模型、收串流、跑工具、核准關卡、長對話摘要 |
|
||||||
|
| `providers.rs` | 各家模型的 API(OpenAI 相容、Anthropic、OpenCode Go…)、串流、重試、看不到圖時的看圖小幫手 |
|
||||||
|
| `tools.rs` | 檔案、指令、背景指令;指令在 macOS 沙盒裡跑(見「安全」) |
|
||||||
|
| `policy.rs` | 哪些動作直接做、先問、拒絕 |
|
||||||
|
| `cloudbox.rs`、`box/` | Bot 自己的電腦:一個 Docker 容器(有桌面、瀏覽器) |
|
||||||
|
| `computer.rs`、`desktop.rs`、`axread.rs` | 看畫面、滑鼠鍵盤;讀 App 的按鈕清單(agent-desktop) |
|
||||||
|
| `botbrowser.rs`、`browser.rs`、`web.rs` | AnyBot 自己的瀏覽器(Chrome DevTools 協定)、讀你的瀏覽器、抓網頁 |
|
||||||
|
| `routines.rs`、`triggers.rs` | 例行任務和觸發 |
|
||||||
|
| `channels.rs` | 訊息管道(Slack、Discord、Telegram) |
|
||||||
|
| `remote.rs` | 手機 API、帳號登入、裝置連結、大腦同步的請求端 |
|
||||||
|
| `tunnel.rs`、`wire.rs` | 和帳號伺服器之間的長連線(WebSocket) |
|
||||||
|
| `app/mirror_ui.rs` | 大腦和螢幕之間的同步 |
|
||||||
|
| `setup.rs` | `anybot setup` |
|
||||||
|
| `httpd.rs` | 很小的 HTTP 伺服器(AnyBot 和帳號伺服器共用) |
|
||||||
|
| `mobile/index.html` | 手機網頁(單一檔案,編進程式裡) |
|
||||||
|
|
||||||
|
## 資料存放
|
||||||
|
|
||||||
|
都在資料資料夾裡(macOS `~/Library/Application Support/AnyBot/`、Linux `~/.local/share/AnyBot/`、Windows `%APPDATA%\AnyBot\`;`ANYBOT_DATA_DIR` 可以改):
|
||||||
|
|
||||||
|
| 檔案 | 內容 |
|
||||||
|
|---|---|
|
||||||
|
| `state.json` | 全部的 Bot、聊天、設定和金鑰 |
|
||||||
|
| `attachments/bot-<id>/` | 聊天附件 |
|
||||||
|
| `browser/` | AnyBot 瀏覽器的設定檔(登入狀態) |
|
||||||
|
| `brain-copy.json` | 螢幕電腦保留的大腦副本(離線時還能看) |
|
||||||
|
| `brain-files/` | 螢幕電腦從大腦下載的附件 |
|
||||||
|
| `image-notes.json` | 看圖小幫手對每張圖的描述(不用重看) |
|
||||||
|
|
||||||
|
寫入時先寫暫存檔再改名,不會寫一半。
|
||||||
|
|
||||||
|
## 安全
|
||||||
|
|
||||||
|
- **核准關卡**:工具動作照規則直接做、先問或拒絕(`policy.rs`,FEATURES.md 第 16、19 節)。「直接操作模式」可以跳過詢問。
|
||||||
|
- **指令的沙盒(macOS)**:Bot 執行的指令在 `sandbox-exec` 裡跑,讀不到也寫不到 AnyBot 的資料資料夾和 `.ssh`、`.gnupg`、鑰匙圈、`.aws` 等;只有 `attachments/` 可以讀(不能寫)。環境變數裡的模型金鑰會先拿掉。
|
||||||
|
- **Bot 的電腦**:放在容器裡,和你的電腦分開。
|
||||||
|
- **保險庫**:密碼、卡號、CVC 不會交給模型,由 AnyBot 直接填進網頁。
|
||||||
|
- **多台電腦**:金鑰只留在大腦(下面說明);手腳只接受帳號裡的 AnyBot,用配對碼加入的只接受那一個大腦。
|
||||||
|
|
||||||
|
## 多台電腦怎麼運作
|
||||||
|
|
||||||
|
### 連線
|
||||||
|
|
||||||
|
每台 AnyBot(大腦、螢幕、手腳)都保持一條 WebSocket 連到帳號伺服器(`/relay/host/ws`),雙方每 15–20 秒互相確認,45–60 秒沒聲音就重連。帳號伺服器沒有 WebSocket 時(舊版),改用 HTTP 長輪詢(`/relay/host/next`)。
|
||||||
|
|
||||||
|
- 手機的要求:手機 → 帳號伺服器(HTTPS)→ 這條連線 → AnyBot → 回覆原路回去。
|
||||||
|
- AnyBot 找另一台 AnyBot(同步、手腳):從自己的連線送 `call` 上去,伺服器轉給對方的連線,答案以 `ret` 回來。
|
||||||
|
- 連線斷掉時,等待中的請求立刻收到「斷線了」,不用等逾時;伺服器也立刻知道這台離線。
|
||||||
|
- 超過 1 KB、而且壓得小的內容用 deflate 壓縮(圖片、PDF 這類已壓縮的照原樣送)。
|
||||||
|
|
||||||
|
格式見 [protocol.md](protocol.md)。
|
||||||
|
|
||||||
|
### 大腦和螢幕的同步(`app/mirror_ui.rs`)
|
||||||
|
|
||||||
|
大腦回答「從版本 N 之後改了什麼」:
|
||||||
|
|
||||||
|
- **設定**:State 去掉聊天、拿掉金鑰後算指紋,變了才送整份。
|
||||||
|
- **聊天**:每則訊息有指紋,只送第一個不同的那則之後的部分。
|
||||||
|
- **討論串**、**誰在工作**、**等你核准的事**。
|
||||||
|
|
||||||
|
螢幕的請求會等最多 20 秒,有變化就馬上回(長輪詢,跑在長連線上)。大腦上的改動通常 1 秒內出現在螢幕上。
|
||||||
|
|
||||||
|
螢幕上的改動變成小操作送回大腦:
|
||||||
|
|
||||||
|
- 單一值(`set`、`bot_set`);
|
||||||
|
- 清單和對照表逐項修改(`patch`、`bot_patch`:新增或修改某幾項、刪掉某幾項、改順序),所以兩台同時改同一份清單的不同項目,兩邊都會保留;
|
||||||
|
- 新增、刪除、排序 Bot(`bot_add`、`bot_del`、`bot_order`)。
|
||||||
|
|
||||||
|
每個改動有編號,大腦回報收到第幾號;還沒確認的改動,會疊在大腦傳回來的新設定上,打字不會被蓋回去。所有改動和聊天動作由一個工作排隊依序送出。送失敗時,螢幕重新要一份完整資料。
|
||||||
|
|
||||||
|
聊天動作(傳訊息、停止、核准、重新產生、開新對話…)送 `act`,由大腦執行。附件先上傳到大腦。
|
||||||
|
|
||||||
|
### 金鑰只在大腦
|
||||||
|
|
||||||
|
送給螢幕的設定裡,模型金鑰、訊息管道的 Token、連接器的環境變數和標頭都換成「••••(已設定,在大腦上)」。螢幕改設定送回來時,大腦看到這個替代字,就保留原本的值(清單裡依 id 或名字對上)。螢幕要取得模型清單時,由大腦用自己的金鑰去抓。
|
||||||
|
|
||||||
|
### 手腳
|
||||||
|
|
||||||
|
大腦的 Bot 呼叫工具時可以指定 `computer`。如果是帳號裡的另一台,就透過長連線送 `/api/device/run` 給它,由它在自己身上執行(螢幕、檔案、指令、它的 AnyBot 瀏覽器),再把文字和截圖傳回來。那台要開「裝置連結」才接受;若設了只給某個大腦用(`hands_for`),帳號伺服器會附上是哪一台在問,不是那台就拒絕。
|
||||||
|
|
||||||
|
### 配對碼
|
||||||
|
|
||||||
|
已登入的手機或 AnyBot 向帳號伺服器要一組 6 位數字(10 分鐘、一次)。新電腦用這組數字換到自己的身分(主機 id 和金鑰),不經過密碼。配對碼記得它是給哪個大腦的,新電腦就只讓那個大腦使用。
|
||||||
|
|
||||||
|
### 看圖小幫手
|
||||||
|
|
||||||
|
模型拒收圖片時,AnyBot 記下這個模型看不到圖。之後送給它的請求裡,你傳的每張圖會先交給你帳號裡看得到圖的模型(依序試 xAI、Anthropic、OpenAI、Gemini、OpenRouter…,先用你設的預設模型),把描述(包括圖裡的文字)放進訊息。描述依圖片記住,同一張圖不會再看一次;失敗的模型 10 分鐘內跳過。
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
# 從原始碼建置和安裝
|
||||||
|
|
||||||
|
## 需要準備
|
||||||
|
|
||||||
|
| 平台 | 需要 |
|
||||||
|
|---|---|
|
||||||
|
| 全部 | Rust(stable,用 rustup 安裝)、git、make |
|
||||||
|
| macOS 12 以後 | Xcode Command Line Tools(`xcode-select --install`) |
|
||||||
|
| Linux | 編譯用:`build-essential pkg-config libasound2-dev`;執行用的系統套件見下面,`make deps` 會一次裝好 |
|
||||||
|
| Windows | Visual Studio Build Tools(C++)。可以 `cargo build --release`,安裝包還沒做 |
|
||||||
|
|
||||||
|
安裝 Rust:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --proto '=https' -sSf https://sh.rustup.rs | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 步驟
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone git@git.30cm.net:daniel.w/AnyBot.git
|
||||||
|
cd AnyBot
|
||||||
|
make deps # Linux:安裝系統套件(會問 sudo 密碼);Mac:檢查 Xcode 工具
|
||||||
|
make # 建置 AnyBot 和帳號伺服器(第一次約 5–15 分鐘)
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
`make install` 做的事:
|
||||||
|
|
||||||
|
- **macOS**:做出 `AnyBot.app`(含圖示),放到 `~/Applications/`。沒有 Apple 開發者簽章,第一次打開請按右鍵 › 打開。想放到別的地方:`make install APPDIR=/Applications`。
|
||||||
|
- **Linux**:程式放在 `~/.local/bin/anybot`,選單項目和圖示放在 `~/.local/share/`。不需要 root。
|
||||||
|
|
||||||
|
不想安裝、只想試:`make run`。
|
||||||
|
|
||||||
|
## Linux 的系統套件
|
||||||
|
|
||||||
|
`make deps` 依發行版(apt、dnf、pacman)安裝這些,全部都是選用的,缺了 App 會說明少了什麼:
|
||||||
|
|
||||||
|
| 套件 | 用途 |
|
||||||
|
|---|---|
|
||||||
|
| libasound2、libxkbcommon-x11-0、libgl1、xdg-utils | 視窗和聲音(必要) |
|
||||||
|
| libnotify-bin | 通知 |
|
||||||
|
| libsecret-tools | 保險庫存在系統鑰匙圈 |
|
||||||
|
| espeak-ng | 唸出回覆 |
|
||||||
|
| zenity | 選檔案的視窗 |
|
||||||
|
| xclip / wl-clipboard | 貼上圖片;GNOME/KDE Wayland 上打中文 |
|
||||||
|
| ffmpeg | 影片、語音、示範錄影 |
|
||||||
|
| docker.io | Bot 自己的電腦 |
|
||||||
|
| fonts-noto-cjk | 更多中文字形(內建的粉圓體已經夠用) |
|
||||||
|
|
||||||
|
沒有螢幕的 Linux(只當手腳或大腦):只需要 `libasound2`。要讓 Bot 看得到畫面,再執行 `make hands`,會裝好虛擬螢幕(Xvfb)、視窗管理員和輔助使用服務。
|
||||||
|
|
||||||
|
## 更新
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git pull
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
你的 Bot、聊天和設定不在程式裡,更新不會動到它們。
|
||||||
|
|
||||||
|
## 移除
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make uninstall
|
||||||
|
```
|
||||||
|
|
||||||
|
只移除程式。要連資料一起刪,刪掉資料資料夾:
|
||||||
|
|
||||||
|
| 平台 | 資料在哪 |
|
||||||
|
|---|---|
|
||||||
|
| macOS | `~/Library/Application Support/AnyBot/` |
|
||||||
|
| Linux | `~/.local/share/AnyBot/` |
|
||||||
|
| Windows | `%APPDATA%\AnyBot\` |
|
||||||
|
|
||||||
|
設成開機自動執行的(`anybot setup` 裝的)也要停掉:Linux 執行 `systemctl --user disable --now anybot`。
|
||||||
|
|
||||||
|
## 其他執行方式
|
||||||
|
|
||||||
|
| 指令 | 用途 |
|
||||||
|
|---|---|
|
||||||
|
| `make serve`(`anybot --serve`) | 不開視窗:Bot、例行任務、訊息管道一直運作,用手機操作 |
|
||||||
|
| `make setup`(`anybot setup`) | 把這台接到你的 AnyBot(大腦或手腳),見 [multi-device.md](multi-device.md) |
|
||||||
|
| `make relay-run` | 在這台跑帳號伺服器(測試用,埠 8080) |
|
||||||
|
| `make server` | 在伺服器上用 Docker 架帳號伺服器、大腦和 HTTPS |
|
||||||
|
| `ANYBOT_DATA_DIR=/某個資料夾 anybot` | 用另一份資料(測試、同一台跑兩份) |
|
||||||
|
|
||||||
|
## 常見問題
|
||||||
|
|
||||||
|
**Mac 說「無法打開,因為無法驗證開發者」**
|
||||||
|
按右鍵 › 打開,再按一次「打開」。只有第一次需要。
|
||||||
|
|
||||||
|
**Mac 上 Bot 看不到螢幕、不能點擊**
|
||||||
|
系統設定 › 隱私權與安全性:「螢幕錄製」和「輔助使用」要打開 AnyBot。App 的 設定 › 電腦 有按鈕直接開到那一頁。
|
||||||
|
|
||||||
|
**Linux 啟動時說找不到 libasound.so.2**
|
||||||
|
`sudo apt-get install libasound2`(Ubuntu 24.04 以後叫 `libasound2t64`)。
|
||||||
|
|
||||||
|
**編譯時找不到 alsa 或 pkg-config**
|
||||||
|
Linux 少了編譯用的套件:`sudo apt-get install build-essential pkg-config libasound2-dev`。
|
||||||
|
|
||||||
|
**第一次 `make` 很久**
|
||||||
|
要下載並編譯所有套件,第一次需要 5–15 分鐘,之後只會重編有改的部分。
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
# 多台電腦:大腦、手腳、螢幕
|
||||||
|
|
||||||
|
帳號裡選一台 AnyBot 當**大腦**,其他電腦和手機都連到它。
|
||||||
|
|
||||||
|
| 角色 | 是什麼 | 怎麼設 |
|
||||||
|
|---|---|---|
|
||||||
|
| 大腦 | 保存所有 Bot、聊天、記憶和例行任務,由它思考和排程。要一直開著 | 登入帳號就是(預設) |
|
||||||
|
| 螢幕 | 有視窗的電腦:看到的、改的都是大腦的 Bot;同時也是大腦的手腳 | 設定 › 手機與帳號 › 大腦 › 「改用它當大腦」 |
|
||||||
|
| 手腳 | 讓大腦的 Bot 操作這台:畫面、點擊打字、檔案和指令、這台的瀏覽器 | 「加一台電腦」的那一行,或 `anybot setup` |
|
||||||
|
| 手機 | 網頁版,打開帳號伺服器的網址 | 登入同一個帳號 |
|
||||||
|
|
||||||
|
所有電腦都透過**帳號伺服器**互相連絡。每台 AnyBot 主動連過去、保持一條一直開著的連線,所以電腦不用開放連接埠,也不用固定 IP。
|
||||||
|
|
||||||
|
## 一、架帳號伺服器和大腦
|
||||||
|
|
||||||
|
### 做法 A:放在伺服器上(推薦,Mac 關機時大腦也還在)
|
||||||
|
|
||||||
|
需要:一台有 Docker 的 Linux 主機,以及一個指到它的網域。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone git@git.30cm.net:daniel.w/AnyBot.git && cd AnyBot
|
||||||
|
make server
|
||||||
|
```
|
||||||
|
|
||||||
|
只問三件事:網域、帳號、密碼。會自動申請 HTTPS 憑證、建立帳號、啟動大腦。完成畫面會告訴你手機和電腦怎麼連。之後再執行一次 `make server`,可以選「用現有設定,更新並重新啟動」。
|
||||||
|
|
||||||
|
不想在伺服器上編譯:下載發行檔 `AnyBot-<版本>-server-x86_64.tar.gz`,解開後 `cd deploy/server && ./setup.sh`,用的是已經編好的程式。
|
||||||
|
|
||||||
|
### 做法 B:先用一台 Mac 當大腦(家裡用 Tailscale)
|
||||||
|
|
||||||
|
1. 跑帳號伺服器,並設成開機自動啟動:
|
||||||
|
```sh
|
||||||
|
make relay
|
||||||
|
mkdir -p "$HOME/Library/Application Support/AnyBot/relay"
|
||||||
|
cp relay/target/release/anybot-relay "$HOME/Library/Application Support/AnyBot/relay/"
|
||||||
|
cp deploy/mac/com.anybot.relay.plist ~/Library/LaunchAgents/ # 先把裡面的網址改成你的
|
||||||
|
launchctl load ~/Library/LaunchAgents/com.anybot.relay.plist
|
||||||
|
```
|
||||||
|
2. 用 Tailscale 給它 HTTPS 網址:`tailscale serve --bg 8780`(網址像 `https://mac-mini.xxxx.ts.net`)。
|
||||||
|
3. 打開 AnyBot › 設定 › 手機與帳號,填這個網址,建立帳號並登入。這台就是大腦。
|
||||||
|
|
||||||
|
只有同一個 Tailscale 網路裡的手機和電腦連得到。
|
||||||
|
|
||||||
|
## 二、接上其他電腦
|
||||||
|
|
||||||
|
### 有視窗的電腦(螢幕)
|
||||||
|
|
||||||
|
打開 AnyBot › 設定 › 手機與帳號,登入同一個帳號,在「大腦」那一欄按某一台旁邊的「改用它當大腦」。
|
||||||
|
|
||||||
|
- 這台原本的 Bot 會留著不刪,可以按「把這台的 Bot 搬過去」複製到大腦(連同聊天、金鑰、技能和連接器;已經在大腦上的不會重複搬)。
|
||||||
|
- 隨時可以按「改回用這台自己的 Bot」。
|
||||||
|
- 選大腦時也會打開這台的「裝置連結」:大腦的 Bot 可以用這台當手腳,每個動作仍照大腦的規則先問你。
|
||||||
|
|
||||||
|
### 沒有螢幕的 Linux(手腳)
|
||||||
|
|
||||||
|
1. 在手機 AnyBot 右上「⋯ › 加一台電腦」,或電腦 AnyBot 的 設定 › 手機與帳號 › 大腦 › 加一台電腦 › 產生配對碼。
|
||||||
|
2. 會出現一行(10 分鐘內有效,只能用一次):
|
||||||
|
```sh
|
||||||
|
anybot setup https://anybot.example.com 469615
|
||||||
|
```
|
||||||
|
3. 在那台電腦裝好 AnyBot(`make install`,或發行檔 `AnyBot-<版本>-headless-x86_64.tar.gz` 裡的 `./install.sh`),再貼上這一行執行。
|
||||||
|
|
||||||
|
它會:加入帳號(不用密碼)、只讓產生配對碼的那個大腦使用、設成開機自動執行(systemd 使用者服務 `anybot`)、沒有螢幕時開一個 1440×900 的虛擬螢幕,最後確認大腦看得到它。大腦約一分鐘內就會把它列進 Bot 可以用的電腦。
|
||||||
|
|
||||||
|
想讓 Bot 看得到這台的畫面,先執行 `make hands`(裝 Xvfb、視窗管理員、輔助使用服務)。
|
||||||
|
|
||||||
|
只打 `anybot setup` 會出現一個畫面:填網址和配對碼(或改用帳號密碼)。給程式用:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ANYBOT_ACCOUNT_PASSWORD=… anybot setup --server https://… --user 帳號 [--brain-name 雲端大腦] [--no-service]
|
||||||
|
```
|
||||||
|
|
||||||
|
登出後也要繼續跑:`sudo loginctl enable-linger $USER`(完成畫面會提醒)。
|
||||||
|
|
||||||
|
### 手機
|
||||||
|
|
||||||
|
打開帳號伺服器的網址,登入。可以「加入主畫面」當 App 用、開通知。帳號裡有螢幕電腦時,手機會直接打開它們用的大腦。
|
||||||
|
|
||||||
|
## 三、連不上時
|
||||||
|
|
||||||
|
畫面上方會說是哪一段連不到:
|
||||||
|
|
||||||
|
| 看到 | 意思 | 怎麼辦 |
|
||||||
|
|---|---|---|
|
||||||
|
| 這台電腦沒有連上網路 | 這台斷網 | 檢查 Wi-Fi 或網路線 |
|
||||||
|
| 連不到帳號伺服器:伺服器沒有開 | 帳號伺服器沒在跑 | 伺服器上 `docker compose ps`;Mac 上看 `launchctl list | grep anybot` |
|
||||||
|
| 帳號伺服器暫時無法使用 | 正在重開或 HTTPS 憑證還沒好 | 等一下,會自動重連 |
|
||||||
|
| 大腦目前離線 | 大腦關機、睡眠或 AnyBot 沒開 | 打開大腦;這段時間還能看之前的對話 |
|
||||||
|
| 大腦收到了,但太久沒有回應 | 大腦太忙或網路很慢 | 稍後再試 |
|
||||||
|
| 這台電腦的帳號登入失效了 | 帳號被登出或金鑰換了 | 重新登入 |
|
||||||
|
| 這台電腦只讓大腦「…」使用 | 別台想用這台當手腳 | 正常:配對時就限定了 |
|
||||||
|
|
||||||
|
大腦連不到時:之前的對話還能看,不能傳訊息;連回來會自動恢復,不用重開。
|
||||||
|
|
||||||
|
**忘記帳號密碼**:在帳號伺服器那台,先停掉它,再執行(輸入兩次新密碼,畫面不顯示):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
anybot-relay --data <資料夾> --reset-password <帳號>
|
||||||
|
```
|
||||||
|
|
||||||
|
舊的加密備份是用舊密碼鎖的,改完之後在電腦上按一次「立刻備份」。
|
||||||
|
|
||||||
|
## 四、安全
|
||||||
|
|
||||||
|
- 模型金鑰、訊息管道的 Token、連接器的密碼只在大腦上。螢幕電腦看到的是「已設定,在大腦上」,在螢幕上改設定也不會洗掉真的金鑰。
|
||||||
|
- 手腳只接受帳號裡的 AnyBot,用配對碼加入的只接受那一個大腦。
|
||||||
|
- 每個動作照大腦的規則先問你(或照你設的「一律允許」)。
|
||||||
|
- 配對碼 10 分鐘有效、只能用一次,連續猜錯會被暫停 5 分鐘。
|
||||||
|
- 技術細節見 [architecture.md](architecture.md) 和 [protocol.md](protocol.md)。
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
# 介面與長連線格式
|
||||||
|
|
||||||
|
給要改帳號伺服器或寫其他用戶端的人。所有回覆都是 JSON;錯誤是 `{"error": "白話的說明"}` 加上對應的 HTTP 狀態碼。
|
||||||
|
|
||||||
|
## 身分
|
||||||
|
|
||||||
|
| 誰 | 怎麼證明 | 從哪來 |
|
||||||
|
|---|---|---|
|
||||||
|
| 人(手機) | `Authorization: Bearer <session>` | `POST /relay/login` |
|
||||||
|
| 一台 AnyBot(主機) | `Authorization: Bearer <host token>` | `POST /relay/hosts`(登入後)或 `POST /relay/pair/claim`(配對碼) |
|
||||||
|
|
||||||
|
伺服器只存密碼和金鑰的雜湊值。
|
||||||
|
|
||||||
|
## 帳號伺服器(relay)
|
||||||
|
|
||||||
|
### 帳號
|
||||||
|
|
||||||
|
| 方法 | 路徑 | 說明 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | `/relay/signup` | `{user, password, invite}`。第一個帳號不用邀請碼,之後要 `ANYBOT_RELAY_INVITE` |
|
||||||
|
| POST | `/relay/login` | `{user, password, device}` → `{token}`。錯 8 次停 5 分鐘 |
|
||||||
|
| POST | `/relay/logout` | 登出這個 session |
|
||||||
|
| GET | `/relay/me` | 帳號、可用的 AnyBot(含 `online`、`uses`、`hands`)、登入過的裝置、有沒有備份 |
|
||||||
|
|
||||||
|
### 主機
|
||||||
|
|
||||||
|
| 方法 | 路徑 | 說明 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | `/relay/hosts` | (人)`{name}` → `{id, token}`。同名的主機會換新金鑰 |
|
||||||
|
| POST | `/relay/hosts/<id>/remove` | (人)移除主機 |
|
||||||
|
| GET | `/relay/host/ws` | (主機)長連線,見下面 |
|
||||||
|
| GET | `/relay/host/next` | (主機)舊式長輪詢:有要求就回 job,25 秒沒有回 204 |
|
||||||
|
| POST | `/relay/host/reply/<job id>` | (主機)回答舊式 job |
|
||||||
|
| GET | `/relay/host/peers` | (主機)帳號裡的其他主機 |
|
||||||
|
| GET/POST | `/relay/host/members` | (主機)團隊成員和角色(admin、member) |
|
||||||
|
| POST | `/relay/host/notify` | (主機)推播通知到帳號的手機 |
|
||||||
|
| POST | `/relay/host/sites/<name>` | (主機)發布 Bot 做的網頁,公開在 `/s/<name>/` |
|
||||||
|
| PUT/GET | `/relay/backup` | 加密備份(伺服器打不開) |
|
||||||
|
|
||||||
|
### 配對碼
|
||||||
|
|
||||||
|
| 方法 | 路徑 | 說明 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | `/relay/pair` | (人或主機)`{for: 大腦的主機 id}` → `{code, expires_in: 600}` |
|
||||||
|
| POST | `/relay/pair/claim` | (不用登入)`{code, name}` → `{id, token, user, for, for_name}`。只能用一次;同一個位址錯 8 次停 5 分鐘 |
|
||||||
|
|
||||||
|
### 轉送
|
||||||
|
|
||||||
|
| 路徑 | 誰 | 轉成 |
|
||||||
|
|---|---|---|
|
||||||
|
| `/h/<host>/api/...` | 手機 | 那台 AnyBot 的 `/api/...`,角色是 owner/admin/member |
|
||||||
|
| `/relay/hosts/<host>/api/sync[/...]` | 同帳號的主機 | 大腦的 `/api/sync...`,角色 `mirror` |
|
||||||
|
| `/relay/hosts/<host>/api/device/run` | 同帳號的主機 | 手腳的 `/api/device/run`,角色 `device` |
|
||||||
|
|
||||||
|
轉送時伺服器附上 `user`、`role` 和 `from`(問的那台主機 id)。對方不在線時立刻回 503「目前離線」;收到了但 35 秒內沒回答,回 504。
|
||||||
|
|
||||||
|
## AnyBot 提供的介面(`remote::handle`)
|
||||||
|
|
||||||
|
只有經過帳號伺服器(或同一個網路、用手機密碼登入)才到得了。
|
||||||
|
|
||||||
|
| 方法 | 路徑 | 角色 | 說明 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| GET | `/api/state?since=` | 手機 | Bot 清單、誰在工作、等核准的事(有變化才回,最多等 20 秒) |
|
||||||
|
| GET | `/api/bots/<id>?since=` | 手機 | 一個 Bot 的訊息 |
|
||||||
|
| POST | `/api/bots/<id>/<動作>` | 手機 | `send`、`stop`、`answer`、`read`、`new`、`upload`… |
|
||||||
|
| GET/POST | `/api/screen/<box\|mac>` | 手機 | 看畫面、點擊打字 |
|
||||||
|
| POST | `/api/device/run` | device | `{tool, args, who}` → `{text, ok, image}`:在這台跑一個工具 |
|
||||||
|
| GET | `/api/sync?since=&client=` | mirror | 大腦的變化(見 architecture.md) |
|
||||||
|
| POST | `/api/sync/edit` | mirror | `{ops, client, seq}` → `{ok, applied}` |
|
||||||
|
| POST | `/api/sync/act` | mirror | `{bot, action, …}`:send、stop、answer、read、new、regenerate、reply、restore |
|
||||||
|
| POST | `/api/sync/upload/<bot>?name=` | mirror | 原始檔案內容 → `{path}` |
|
||||||
|
| GET | `/api/sync/file?path=` | mirror | 大腦上的附件(只限 `attachments/` 裡的檔案) |
|
||||||
|
| POST | `/api/sync/models` | mirror | `{provider}` → `{models}`:用大腦的金鑰取得模型清單 |
|
||||||
|
|
||||||
|
## 長連線的格式(`src/wire.rs`)
|
||||||
|
|
||||||
|
WebSocket 的二進位訊息,每則是:
|
||||||
|
|
||||||
|
```
|
||||||
|
[4 位元組,表頭長度,big-endian] [表頭 JSON] [內容的原始位元組]
|
||||||
|
```
|
||||||
|
|
||||||
|
內容不做 base64。`z: true` 表示內容用 deflate 壓過;`z_ok: true` 表示問的人看得懂壓過的回答。
|
||||||
|
|
||||||
|
| t | 方向 | 表頭欄位 | 意思 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `hello` | AnyBot → 伺服器 | `brain`、`hands` | 這台在看哪個大腦、是不是手腳(手機和設定畫面用) |
|
||||||
|
| `job` | 伺服器 → AnyBot | `id, method, path, query, user, role, from, z, z_ok` | 給這台的要求 |
|
||||||
|
| `reply` | AnyBot → 伺服器 | `id, status, ctype, headers, z` | 回答 job |
|
||||||
|
| `call` | AnyBot → 伺服器 | `id, to, method, path, query, z` | 要問帳號裡的另一台(只能是 `/api/sync…` 或 `/api/device/run`) |
|
||||||
|
| `ret` | 伺服器 → AnyBot | `id, status, ctype, z` | call 的回答 |
|
||||||
|
|
||||||
|
心跳用 WebSocket 的 ping/pong。連線斷掉時,伺服器把還在等的 job 都回 503「斷線了」,AnyBot 把還在等的 call 都回「和帳號伺服器的連線斷了」。
|
||||||
|
|
||||||
|
## 同步的改動(`/api/sync/edit` 的 ops)
|
||||||
|
|
||||||
|
| op | 欄位 | 說明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `set` | `key, value` | 換掉一個設定(`next_*` 計數只會變大) |
|
||||||
|
| `patch` | `key, patch` | 清單或對照表逐項修改 |
|
||||||
|
| `merge` | `key, value` | 搬移時補上大腦沒有的(providers、skills、mcp_servers) |
|
||||||
|
| `bot_add` | `bot` | 新增 Bot(id 撞到會換) |
|
||||||
|
| `bot_del` | `id` | 刪除(主要 Bot 不能刪) |
|
||||||
|
| `bot_set` | `id, key, value` | 改 Bot 的一個欄位 |
|
||||||
|
| `bot_patch` | `id, key, patch` | 逐項修改 Bot 的清單欄位(例行任務保留大腦記錄的執行時間) |
|
||||||
|
| `bot_read` | `id` | 標為已讀 |
|
||||||
|
| `bot_order` | `ids` | Bot 的順序 |
|
||||||
|
|
||||||
|
`patch` 的格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"value": …} 換掉整個值
|
||||||
|
{"map": {"put": {"鍵": 值}, "del": ["鍵"]}} 對照表
|
||||||
|
{"list": {"put": [項目], "del": [鍵], "order": [鍵]}} 清單;鍵是項目的 id,沒有就用 name,純值就是值本身
|
||||||
|
```
|
||||||
|
|
||||||
|
清單裡的項目沒有 id 或名字、或鍵重複時,送整個值。
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
# 發行
|
||||||
|
|
||||||
|
## 自己打包
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make dist # 版本號取自 git tag(沒有就用 commit 編號)
|
||||||
|
make dist VERSION=1.2.0 # 指定版本號
|
||||||
|
```
|
||||||
|
|
||||||
|
| 在哪打包 | 產生 |
|
||||||
|
|---|---|
|
||||||
|
| Mac | `dist/AnyBot-<版本>-macos-arm64.zip`:AnyBot.app(含圖示,ad hoc 簽章) |
|
||||||
|
| Linux | `dist/AnyBot-<版本>-ubuntu-<架構>.tar.gz`:有視窗的版本,`./install.sh` 安裝 |
|
||||||
|
| | `dist/AnyBot-<版本>-headless-<架構>.tar.gz`:沒有螢幕的 Linux(手腳或大腦),附 `hands.sh` |
|
||||||
|
| | `dist/AnyBot-<版本>-server-<架構>.tar.gz`:帳號伺服器+大腦+HTTPS,用已編好的程式建 Docker 映像,`deploy/server/setup.sh` |
|
||||||
|
|
||||||
|
Linux 版要在 Ubuntu 22.04(glibc 2.35)上編譯,才能在較新的 Ubuntu、Debian 12 等直接跑;用 `deploy/ci/Dockerfile.linux` 這個環境就是。
|
||||||
|
|
||||||
|
打包腳本:`deploy/mac/bundle.sh`、`deploy/ci/package-linux.sh`。
|
||||||
|
|
||||||
|
## 推 tag 自動建置(Gitea Actions)
|
||||||
|
|
||||||
|
`.gitea/workflows/release.yml`:推一個 `v` 開頭的 tag,兩個 runner 各自建置,把檔案掛到那個 tag 的 Release 上(沒有 Release 會自動建立;重建時會換掉同名的檔案)。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git tag v1.2.0
|
||||||
|
git push origin v1.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
| 工作 | runner 標籤 | 做什麼 |
|
||||||
|
|---|---|---|
|
||||||
|
| linux | `anybot-linux` | 在 `anybot-builder:22.04` 容器裡編譯、跑測試、打三個 Linux 包、上傳 |
|
||||||
|
| macos | `macos-arm64` | 在 Mac 上直接編譯、做 AnyBot.app、上傳 |
|
||||||
|
|
||||||
|
上傳用 `secrets.RELEASE_TOKEN`;沒設時用 Gitea 自動給的 token。如果自動的 token 沒有建立 Release 的權限,到 repo › 設定 › Actions › Secrets 加一個 `RELEASE_TOKEN`(有這個 repo 寫入權限的 access token)。
|
||||||
|
|
||||||
|
### 設定 Linux runner
|
||||||
|
|
||||||
|
在一台有 Docker 的 Linux(x86_64)上:
|
||||||
|
|
||||||
|
1. 在 Gitea 打開 Actions(管理員:`app.ini` 的 `[actions] ENABLED = true`),到 repo › 設定 › Actions › Runners › 建立新的 runner,複製註冊權杖。
|
||||||
|
2. 安裝 runner([gitea/runner](https://gitea.com/gitea/runner/releases),下載後核對 `.sha256`):
|
||||||
|
```sh
|
||||||
|
V=5.3.0
|
||||||
|
curl -fsSLO https://gitea.com/gitea/runner/releases/download/v$V/gitea-runner-$V-linux-amd64.xz
|
||||||
|
curl -fsSLO https://gitea.com/gitea/runner/releases/download/v$V/gitea-runner-$V-linux-amd64.xz.sha256
|
||||||
|
sha256sum -c gitea-runner-$V-linux-amd64.xz.sha256 && xz -d gitea-runner-$V-linux-amd64.xz
|
||||||
|
install -m 755 gitea-runner-$V-linux-amd64 ~/.local/bin/gitea-runner
|
||||||
|
```
|
||||||
|
3. 建好建置環境(只要一次;更新 Rust 時再建一次):
|
||||||
|
```sh
|
||||||
|
docker build -t anybot-builder:22.04 -f deploy/ci/Dockerfile.linux deploy/ci
|
||||||
|
```
|
||||||
|
4. 設定和註冊:
|
||||||
|
```sh
|
||||||
|
mkdir -p ~/.config/gitea-runner
|
||||||
|
cp deploy/ci/runner-linux.yaml ~/.config/gitea-runner/config.yaml # 檔案路徑裡的使用者名稱照你的改
|
||||||
|
cd ~/.config/gitea-runner
|
||||||
|
gitea-runner register --no-interactive --instance https://你的-gitea --token 註冊權杖 \
|
||||||
|
--name anybot-linux --labels "anybot-linux:docker://anybot-builder:22.04" -c config.yaml
|
||||||
|
```
|
||||||
|
5. 一直跑(systemd 使用者服務):
|
||||||
|
```ini
|
||||||
|
# ~/.config/systemd/user/gitea-runner.service
|
||||||
|
[Unit]
|
||||||
|
Description=Gitea runner (AnyBot releases)
|
||||||
|
After=network-online.target docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
WorkingDirectory=%h/.config/gitea-runner
|
||||||
|
ExecStart=%h/.local/bin/gitea-runner daemon -c %h/.config/gitea-runner/config.yaml
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
```
|
||||||
|
```sh
|
||||||
|
systemctl --user daemon-reload && systemctl --user enable --now gitea-runner
|
||||||
|
sudo loginctl enable-linger $USER # 登出後也繼續跑
|
||||||
|
```
|
||||||
|
|
||||||
|
`runner-linux.yaml` 用兩個 Docker 磁碟區保留下載的套件(`anybot-cargo-registry`)和編譯結果(`anybot-ci-target`),之後每次發行只重編有改的部分。
|
||||||
|
|
||||||
|
### 設定 Mac runner
|
||||||
|
|
||||||
|
Apple 的 App 只能在 Mac 上編譯。在一台裝好 Rust 和 Xcode Command Line Tools 的 Mac 上:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
V=5.3.0
|
||||||
|
curl -fsSLO https://gitea.com/gitea/runner/releases/download/v$V/gitea-runner-$V-darwin-arm64.xz
|
||||||
|
curl -fsSLO https://gitea.com/gitea/runner/releases/download/v$V/gitea-runner-$V-darwin-arm64.xz.sha256
|
||||||
|
shasum -a 256 -c gitea-runner-$V-darwin-arm64.xz.sha256 && xz -d gitea-runner-$V-darwin-arm64.xz
|
||||||
|
install -m 755 gitea-runner-$V-darwin-arm64 ~/.local/bin/gitea-runner
|
||||||
|
mkdir -p ~/.config/gitea-runner && cd ~/.config/gitea-runner
|
||||||
|
gitea-runner register --no-interactive --instance https://你的-gitea --token 註冊權杖 \
|
||||||
|
--name anybot-mac --labels "macos-arm64:host"
|
||||||
|
gitea-runner daemon # 或做成 launchd 代理,開機自動執行
|
||||||
|
```
|
||||||
|
|
||||||
|
`host` 表示工作直接在這台 Mac 上跑(不用容器)。編譯結果保留在工作資料夾旁的 `anybot-target/`。
|
||||||
|
|
||||||
|
### 注意
|
||||||
|
|
||||||
|
- runner 要連得到 Gitea 的網址(HTTPS),也要能從 GitHub 下載 `actions/checkout`(Gitea 預設的 action 來源)。
|
||||||
|
- Mac 版用 ad hoc 簽章,沒有公證;使用者第一次打開要按右鍵 › 打開。要正式簽章和公證需要 Apple 開發者帳號。
|
||||||
Loading…
Reference in New Issue