diff --git a/README.md b/README.md index e78176d..1251e88 100644 --- a/README.md +++ b/README.md @@ -1,339 +1,334 @@ +
+ # LazyBoy -![給 Agent 一台真的電腦](./docs/readme-hero.png) +### 給 Agent 一台真的電腦 -在瀏覽器裡開 Agent。每個都有自己的 Linux 桌面:開網頁、敲指令、學你示範過的流程。金鑰、模型、檔案都留在你這台機器上。 +在瀏覽器裡指派工作,讓 AI Agent 在隔離的 Linux 桌面中開網頁、操作應用程式、整理檔案,並在你需要時把控制權交回來。 ---- +[![Self-hosted](https://img.shields.io/badge/deployment-self--hosted-3ec5a8?style=flat-square)](#快速開始) +[![Rust 2024](https://img.shields.io/badge/Rust-2024-000000?style=flat-square&logo=rust)](./Cargo.toml) +[![React 19](https://img.shields.io/badge/React-19-20232a?style=flat-square&logo=react)](./apps/web/package.json) +[![Docker Compose](https://img.shields.io/badge/Docker-Compose-2496ed?style=flat-square&logo=docker&logoColor=white)](./docker-compose.yml) -## 需要的硬體 +[快速開始](#快速開始) · [功能](#核心能力) · [運作流程](#一個任務如何完成) · [系統架構](#系統架構) · [二次開發](#二次開發) · [安全](#安全模型) -LazyBoy 是本機系統,不是雲端沙盒。機器要跑四件事:**Postgres**、**API(含網頁)**、**supervisor**、以及每個 Agent 的 **Debian 桌面容器**。 +
-| 項目 | 最低能跑 | 建議(一台桌面常開) | -| --- | --- | --- | -| 作業系統 | macOS / Linux,已裝 Docker Engine + Compose | 同上,磁碟給 Docker 至少 30 GB | -| CPU | 4 核 | 8 核以上。每個桌面預設吃 2 核(`LAZYBOY_COMPUTER_CPUS`) | -| 記憶體 | 8 GB(只能開一台、還會卡) | **16 GB**。每個桌面預設 2 GB(`LAZYBOY_COMPUTER_MEMORY_MB`)再加上 API、Postgres、嵌入模型 | -| 磁碟 | 約 15 GB(桌面映像 + Postgres) | 家目錄 `data/homes/` 會隨瀏覽器設定檔長大 | -| GPU | 不需要 | 模型走網路 API,畫面是 CPU 上的 Xvfb | -| 網路 | 第一次建映像、拉套件需要 | 之後離線也能開 UI;聊天要模型金鑰能連外 | +![LazyBoy 三欄工作空間:Agent、對話與即時電腦畫面](./docs/readme-hero.png) -每個 Agent 桌面都會套用 Docker 的 CPU、記憶體與 PID 上限,預設是 2 CPU、2 GB、2048 個 PID。可在 `.env` 調整 `LAZYBOY_COMPUTER_CPUS`、`LAZYBOY_COMPUTER_MEMORY_MB`、`LAZYBOY_COMPUTER_PIDS`。Linux 若安裝 LXCFS,將 `LAZYBOY_LXCFS_ROOT` 指到它的 `/var/lib/lxcfs`,容器內的 `htop`、`free` 等也會顯示 cgroup 配額;macOS 仍會套用配額,但 Docker Desktop 不提供這個 `/proc` 虛擬化。桌面容器不掛主機 Docker socket,也不使用 `privileged`;需要一般管理命令時才在 `.env` 設定 `LAZYBOY_COMPUTER_SUDO=true`,權限只在該 Agent 容器內生效。 +LazyBoy 是一套本機優先(local-first)的多 Agent 工作空間。每個 Agent 都能使用瀏覽器、終端、檔案系統與桌面應用程式;執行畫面可即時觀看、可以人工接管,也能把示範整理成技能,之後透過排程重複執行。 -預設一個 Team 電腦容器可同時掛最多 **8** 個螢幕(`TEAM_SCREEN_LIMIT`)。再開私人電腦就是再一個容器、再 2 GB。分頁開著時心跳會讓桌面保持熱機;關掉分頁約 10 分鐘後凍結(記憶體還在),約 6 小時後才真正停機。 +> [!IMPORTANT] +> LazyBoy 會把對話、記憶、憑證與瀏覽器設定檔持久化在自己的主機,但使用外部模型時,完成任務所需的提示詞、工具結果或畫面仍可能傳送給你設定的模型供應商。請依資料敏感度選擇供應商與部署方式。 ---- +## 為什麼是 LazyBoy? -## 怎麼快速啟動 +| 一般聊天機器人 | LazyBoy | +| --- | --- | +| 告訴你怎麼做 | 在隔離桌面中實際操作 | +| 看不到執行過程 | 即時 noVNC 畫面與步驟狀態 | +| 人與 Agent 搶控制權 | 接管/釋放租約,安全交接 | +| 每次重新說明流程 | 示範一次,儲存為可攜技能 | +| 一次性對話 | Session、長期記憶、排程與群組 | +| 固定模型與工具 | xAI、OpenCode Go、OpenAI 相容端點與 MCP | -要有 Docker(含 Compose 外掛)和 Make。第一次會編 `lazyboy/computer:local`(Debian + XFCE + Chromium),會比較久。 +## 快速開始 + +### 需求 + +- macOS 或 Linux +- Docker Engine / Docker Desktop(含 Compose) +- Make、Git +- 至少 4 核 CPU、8 GB RAM;建議 8 核、16 GB RAM +- 一組支援的模型 API 金鑰 + +第一次啟動會建置 Debian、XFCE、Chromium 桌面映像,因此會比後續啟動久。 ```bash +git clone +cd LazyBoy + make env -# 在 .env 填 XAI_API_KEY -# (也可以之後在「本機工作區 → 設定」接 xAI / OpenCode Go / OpenAI 相容端點) +# 編輯 .env,至少填入 XAI_API_KEY、OPENCODE_GO_API_KEY +# 或 OPENAI_API_KEY 其中之一 + make up make health ``` -打開 [http://127.0.0.1:3101](http://127.0.0.1:3101),用 `.env` 裡的 `LAZYBOY_APP_TOKEN` 登入,建一個 Agent,傳一句話。 +開啟 [http://127.0.0.1:3101](http://127.0.0.1:3101),使用 `.env` 中的 `LAZYBOY_APP_TOKEN` 登入,建立第一個 Agent,然後直接描述目標。 -沒有 Make: +```text +打開指定的訓練網站,完成還沒看完的章節,最後整理進度。 +``` + +常用指令: + +| 指令 | 用途 | +| --- | --- | +| `make up` | 建置並啟動完整堆疊 | +| `make health` | 檢查 API 健康狀態 | +| `make logs` | 持續查看服務日誌 | +| `make ps` | 查看容器狀態 | +| `make down` | 停止服務,保留 PostgreSQL 資料 | +| `make purge` | 停止服務並刪除 PostgreSQL volume | + +沒有 Make 時,可以從 `.env.example` 建立設定後執行: ```bash cp .env.example .env -# openssl rand -hex 32 → LAZYBOY_APP_TOKEN -# openssl rand -hex 32 → SANDBOX_SUPERVISOR_TOKEN -# openssl rand -hex 32 → LAZYBOY_VAULT_KEY +# 為 LAZYBOY_APP_TOKEN、SANDBOX_SUPERVISOR_TOKEN、 +# LAZYBOY_VAULT_KEY 各自執行一次 openssl rand -hex 32 docker compose up -d --build ``` -前端熱重載:`apps/web` 裡 `npm install && npm run dev`,開 [http://127.0.0.1:5173](http://127.0.0.1:5173)。Vite 把 `/api` 和 `/view` 轉到 3101。 +## 核心能力 -本機 Rust 開發(Postgres 仍在 Docker): +| 能力 | 實際行為 | +| --- | --- | +| 可觀察的電腦操作 | 右側 noVNC 顯示 Agent 的 1280×800 桌面;模型觀察與人類畫面分流 | +| 瀏覽器與原生桌面控制 | 網頁透過 CDP 元素操作;原生 UI 使用 AT-SPI,必要時才退回座標 | +| 人工接管 | 登入、2FA、驗證碼或敏感步驟可暫停 Agent,由使用者接管後續跑 | +| 示範教學 | 記錄語意操作事件並整理成 playbook,不依賴固定像素重播 | +| 長期記憶 | PostgreSQL + pgvector + MiniLM;只保存明確要求記住的內容 | +| 安全登入 | 每個 Agent 的 AES-256-GCM 憑證庫;只對相符的 HTTPS 網域填入 | +| 排程 | 五欄 cron、時區支援、立即試跑;到期任務進入一般 run 佇列 | +| 多 Agent 與群組 | Team 共用電腦可分配不同 DISPLAY;私人模式使用獨立容器 | +| MCP 與檔案技能 | 支援 stdio、HTTP、SSE MCP,以及 `data/skills/*/SKILL.md` | +| 語音通話 | 前端提供可設定的即時語音工作階段 | +| 中英介面 | `zh-TW` 與 English 文案共用完整 key 契約 | -```bash -make dev # 準備 .env、Postgres、桌面映像 -make dev-supervisor # 終端 1 -make dev-api # 終端 2 +## 一個任務如何完成 + +```mermaid +flowchart LR + U["你
文字・附件・語音"] --> W["React 工作空間
對話・電腦・記憶"] + W -->|HTTP / WebSocket| A["Rust API + Agent
模型・工具・技能"] + A -->|受控工具呼叫| S["Supervisor
資源與生命週期"] + S -->|啟動 / 暫停 / 恢復| C["隔離 Linux 電腦
Chromium・XFCE・檔案"] + C -->|畫面與事件| W + + Q["排程"] -. Cron .-> A + A <--> M[("PostgreSQL
Session・記憶・事件")] + V["加密憑證庫"] -. 僅相符 HTTPS 網域 .-> C + + classDef ui fill:#151517,stroke:#3ec5a8,color:#f1f1f2 + classDef core fill:#101012,stroke:#85858a,color:#f1f1f2 + classDef secure fill:#221c0e,stroke:#fcd68a,color:#f1f1f2 + class U,W ui + class A,M,Q core + class S,C,V secure ``` -| 指令 | 做什麼 | +
+ +**[開啟可縮放、可搜尋、支援深色模式的互動流程圖 →](./docs/workflow.html)** + +
+ +主流程之外還有兩個重要迴圈: + +1. **接管迴圈**:使用者接管時,進行中的 run 進入等待;釋放後從目前畫面重新排隊執行。 +2. **技能迴圈**:示範期間記錄控制項與頁面情境,模型整理成 playbook;往後仍在當下畫面重新尋找元素,不重播舊座標。 + +## 系統架構 + +LazyBoy 的公開入口只有 API。Supervisor 位於 Compose 內部 control network,不直接對主機開埠;Agent 桌面也不掛載主機 Docker socket。 + +```text +Browser + │ HTTP / WebSocket / authenticated screen proxy + ▼ +lazyboy-api (:3101) + ├── React 靜態前端 + ├── Session / Run / Memory / Schedule / Vault + ├── Model provider / MCP client + └── PostgreSQL + pgvector + │ + │ authenticated internal control API + ▼ +lazyboy-supervisor (:7091, internal only) + │ + ├── provision / pause / resume / stop + ├── CPU / memory / PID limits + └── isolated computer containers + ├── Chromium + CDP + ├── XFCE + AT-SPI + ├── Xvfb + x11vnc + websockify + └── per-computer persisted home +``` + +### 主要元件 + +| 元件 | 職責 | | --- | --- | -| `make up` / `make down` / `make purge` | 啟動/停止(留資料)/連 Postgres 一起清 | -| `make logs` `make ps` `make health` | 看狀態 | -| `make computer` | 只重建桌面映像 | -| `make postgres` | 只開資料庫 `127.0.0.1:5434` | +| `apps/web` | Vite + React 19 三欄工作空間、noVNC、語音與雙語 UI | +| `crates/api` | 對外 Axum API、Agent run、Session、排程、記憶、MCP、保險箱 | +| `crates/harness` | 模型供應商、憑證解析與語音契約 | +| `crates/supervisor` | Docker 電腦生命週期、隔離與資源上限 | +| `crates/control` | CDP、AT-SPI、X11 與畫面觀察操作 | +| `crates/controld` | 電腦容器內部的 localhost 控制服務 | +| `crates/contracts` | 跨 crate 的 Bot、Run、Computer、Voice 資料契約 | +| `PostgreSQL` | 對話、run、記憶、排程、憑證與保留政策 | ---- +### 電腦生命週期 -## 跟 Grok Bot 比,好在哪 +```mermaid +stateDiagram-v2 + [*] --> Stopped + Stopped --> Booting: 第一個需要 GUI 的工具 + Booting --> Running: ready + Running --> WaitingTakeover: 使用者接管 / 2FA + WaitingTakeover --> Running: 釋放控制權 + Running --> Suspended: 閒置 10 分鐘 + Suspended --> Running: 新任務或畫面心跳 + Suspended --> Stopped: 休眠約 6 小時 + Running --> Stopped: 手動停止 +``` -[Grok Bot](https://grok.com) 是 xAI 的雲端隊友:對話、電腦、排程都在他們的機器上。LazyBoy 走同一類產品(本機開源實作對齊 Rakazo 那條線),差在**誰擁有執行環境**。 +## 安全模型 -| | Grok Bot | LazyBoy | +目前程式碼包含下列防護: + +- API session token 與 Supervisor token 分離。 +- Supervisor 只在內部 Compose network,並使用 `no-new-privileges`、唯讀 root filesystem 與 capability drop。 +- 每台電腦有 CPU、RAM、PID 上限;預設 2 CPU、2 GB、2048 PID。 +- API 預設只綁定 `127.0.0.1:3101`。 +- 憑證以獨立 `LAZYBOY_VAULT_KEY` 加密,輪替登入 token 時不應更換此 key。 +- 已保存登入只接受 HTTPS、精確或合法子網域匹配,不對相似惡意網域填入。 +- Markdown 連結限制為 HTTP(S)、`mailto:`、`tel:` 與頁內錨點。 +- 日誌有大小與檔案數上限;診斷資料有可設定的保留週期。 + +部署注意事項: + +1. 對區網或網際網路開放前,先放在 HTTPS reverse proxy 後方,並設定 `LAZYBOY_SECURE_COOKIE=true`。 +2. 不要把 Supervisor `:7091` 對外發布,也不要將 Docker socket 掛進 Agent 電腦。 +3. `LAZYBOY_APP_TOKEN`、`SANDBOX_SUPERVISOR_TOKEN`、`LAZYBOY_VAULT_KEY` 必須使用不同的高熵值。 +4. 模型仍可能看見任務所需的網頁內容與截圖;密碼、token 與高敏感資料不要放進提示詞。 +5. CAPTCHA、2FA 與不確定的敏感操作應由使用者接管。 + +## 硬體與資源 + +| 項目 | 最低可執行 | 建議 | | --- | --- | --- | -| 跑在哪 | xAI 雲端 | 你的 Docker | -| 模型 | Grok | 你帶金鑰:xAI、OpenCode Go、或任何 OpenAI 相容端點 | -| 電腦 | 廠商提供的桌面 | 你映像裡的 Debian/XFCE/Chromium,家目錄在 `data/homes/` | -| 資料 | 在服務端 | 對話、記憶、保險箱、瀏覽器設定檔都在本機 Postgres + 磁碟 | -| 登入帳號 | 跟雲端工作流程走 | 每個 Agent 自己的保險箱(AES-256-GCM),模型只看到帳號 id | -| 客製 | 封閉 | 開源。工具、MCP、技能 JSON 可改可搬 | -| 費用形態 | 訂閱/用量 | 電費與硬體;模型金鑰另計 | -| 多 Agent 同桌 | 產品內建 | Team 電腦一個容器最多 8 螢幕;私人電腦一人一容器 | -| 教會它 | 看產品當下提供什麼 | 你示範一次,CDP 記語意事件,模型整理成技能 | +| CPU | 4 核 | 8 核以上 | +| RAM | 8 GB,單台電腦 | 16 GB 以上 | +| 磁碟 | 約 15 GB | Docker 至少保留 30 GB | +| GPU | 不需要 | 模型預設走外部 API | +| 作業系統 | macOS / Linux | Linux 可選配 LXCFS 顯示容器內 cgroup 配額 | -適合 LazyBoy 的情況:資料不能出門、要自己選模型、要看它點了哪個控制項、或想把「看完訓練影片交測驗」這種流程做成可匯出的技能。 +每台電腦的預設限制可由 `LAZYBOY_COMPUTER_CPUS`、`LAZYBOY_COMPUTER_MEMORY_MB`、`LAZYBOY_COMPUTER_PIDS` 調整。瀏覽器分頁的心跳會維持熱機;沒有工作且約 10 分鐘無人觀看時暫停,持續休眠約 6 小時後停止。 -Grok Bot 適合的情況:不想養 Docker、要官方託管、機器不夠力。 +## 設定 ---- +`make env` 會以 `.env.example` 為基礎建立 `.env`,並保留既有金鑰。 -## 每個功能簡介 +| 變數 | 用途 | 預設 | +| --- | --- | --- | +| `XAI_API_KEY` | xAI 模型 | 空 | +| `OPENCODE_GO_API_KEY` | OpenCode Go 模型 | 空 | +| `OPENAI_API_KEY` | OpenAI 相容端點 | 空 | +| `LAZYBOY_APP_TOKEN` | Web 登入 token | 必填 | +| `SANDBOX_SUPERVISOR_TOKEN` | API ↔ Supervisor 驗證 | 必填 | +| `LAZYBOY_VAULT_KEY` | 憑證庫加密 key | 必填且必須保持穩定 | +| `LAZYBOY_BIND_IP` | 主機監聽位址 | `127.0.0.1` | +| `LAZYBOY_SECURE_COOKIE` | HTTPS-only cookie | `false` | +| `LAZYBOY_COMPUTER_CPUS` | 每台電腦 CPU | `2` | +| `LAZYBOY_COMPUTER_MEMORY_MB` | 每台電腦記憶體 | `2048` | +| `LAZYBOY_COMPUTER_PIDS` | 每台電腦 PID 上限 | `2048` | +| `LAZYBOY_MEMORY_ENABLED` | 長期記憶 | `true` | -**對話與 Session** -每個 Agent 多則對話。訊息進 Postgres,同一則用 `clientNonce` 去重。問候、閒聊走純文字,**不會**為了「看一下螢幕」去開 Docker。 +完整清單與保留政策請見 [`.env.example`](./.env.example)。 -**Team / 私人電腦** -Team:工作區共用一個家目錄,每個 bot 有自己的 `DISPLAY`(`:1`、`:2`…)和瀏覽器設定檔。私人:這個 bot 獨佔一個容器。 +## 二次開發 -**即時畫面** -右側預覽是 noVNC。瀏覽器連 `/view/{botId}/vnc.html`,API 用已登入的 cookie 轉到容器裡的 websockify。模型看到的截圖另走 `computer_observe`,上面會蓋黃字編號;你盯著的 VNC **沒有**那些編號。 +### 本機開發 -**接管 / 釋放** -人按接管就拿到控制租約(預設 15 分鐘,心跳續約)。進行中的 run 會進 `waiting_takeover`,放開後從目前畫面接著做。模型遇到登入牆、2FA、驗證碼會呼叫 `request_takeover`。 +```bash +make dev # 準備 .env、PostgreSQL、電腦映像 +make dev-supervisor # 終端 1:Supervisor :7091 +make dev-api # 終端 2:API :3101 +``` -**觀察與操作** -- Chromium 網頁:`browser`(CDP,點 element id) -- 原生視窗(對話框、檔案管理員、XFCE):`computer_act`(AT-SPI id,不行再退 xdotool 座標) -- 檔案與指令:`list_files` / `read_file` / `write_file` / `shell` -點到 `[disabled]` 的控制項會最多等 45 秒等它亮。模型用文字回「我在等」會結束整段 run,所以等待必須是 `wait` 工具。 +前端熱更新: -**教技能** -你示範,容器內 CDP 錄「點了哪個控制項、填了什麼、去了哪一頁」,再抽幾個關鍵畫面。停下來後模型整理成意圖級 playbook,之後用普通工具在**當下畫面**找控制項,不是重播座標。密碼欄不錄。技能可匯出 JSON。 +```bash +cd apps/web +npm install +npm run dev # http://127.0.0.1:5173 +``` -**Slash 指令與長目標** -在 `data/skills//SKILL.md` 放工作區共用的唯讀技能,就能在輸入框打 `/name 參數` 執行;輸入 `/` 會顯示可用技能。`/goal` 是 harness 的持續執行模式,和錄製示範產生的 playbook 分開。選單支援方向鍵、Enter/Tab 選取、Esc 關閉。`/goal 目標` 會先規劃、執行並檢查結果,直到模型回報已驗證完成;只有需要登入、驗證碼、接管畫面或缺少必要資訊時才會停下來請你處理。缺少必要資訊時會記為等待輸入,補充訊息後接續原目標;停止按鈕可以取消。一般訊息仍維持 40 回合上限,教學技能 80 回合。 +### 檢查與測試 -**記憶** -`pgvector` + MiniLM(384 維)。只有你叫它記住、或它呼叫 `remember` 的內容會進長期記憶。密碼與 token 會被拒。清除對話不會清記憶。 +```bash +make fmt +make clippy +make test -**保險箱** -每個 bot 自己的站名/帳號/密碼。模型用 `list_accounts` 只看到站與使用者名稱,`use_saved_login` 在 Chromium 登入表單填入。金鑰用 `LAZYBOY_VAULT_KEY` 加密。 +cd apps/web +npm run typecheck +npm run build -**排程** -五欄 cron,預設 `Asia/Taipei`。對話裡講「以後每天九點」或側欄新增。tick 迴圈把到期列變成普通 queued run。 +cd ../.. +node --test tests/frontend.test.mjs +python3 tests/control.test.py +python3 tests/log-rotation.test.py -**MCP** -工作區級外掛。市集或自訂 stdio/HTTP/SSE。stdio 跑在 API 容器裡。 +# 需要已啟動的 PostgreSQL Compose service +docker compose up -d postgres +python3 tests/retention.test.py +``` -**群組** -多個 Agent 同一個 thread。Team 電腦上各用各的螢幕。同一 bot 同時只跑一個 run,後面的訊息排隊。 - -**附件** -圖片給當則模型看,不進歷史二進位。要讓電腦開原檔會放 `inbox/`,兩小時後刪。 - -**頭像與狀態** -Blobatar 色塊+眼睛。啟動、喚醒、連線、換手時,預覽左上角與思考列會顯示對應文字。分頁開著時心跳保住容器,換手不拆 VNC。 - ---- - -## 系統怎麼轉起來 - -![其實只有三個角色:你、LazyBoy、它的電腦](./docs/diagrams/map.png) - -Compose 裡 supervisor **不**對主機開埠。API 在容器網路連 `supervisor:7091`。家目錄 `data/homes/` bind 進容器的 `/home/lazyboy`。 - ---- - -## 你送一則訊息 - -![它先決定:聊就好,還是要動手](./docs/diagrams/chat.png) - -同一 bot 已有進行中的工作時,新訊息會排隊(`queuedBehindActive`)。人正在接管時,後面的話只排隊,思考轉圈不會假裝它還在動。問候路徑會把工具表清空,從源頭避免「哈囉」去開電腦。 - -`execute_run` 每一輪:續租約 → 寫步驟文字 → 問模型 → 沒有工具就結束(技能沒過會再把畫面塞回去)→ 有工具且需要沙盒才 boot → 畫面沒變就不重複塞圖。回合上限:聊天 4、一般 40、教學技能 80;`/goal` 會持續到完成或明確需要人介入。目標執行期間,同一對話送進來的新訊息會作為下一輪的補充指示。 - ---- - -## 電腦的作息 - -![開著、小睡、關機](./docs/diagrams/sleep.png) - -閒置(`crates/api/src/computer.rs` `idle_loop`): - -1. 執行中、超過 10 分鐘沒人看、也沒有進行中的 run/示範 → `docker pause`,狀態 `suspended` -2. 休眠超過 6 小時 → `docker stop`,狀態 `stopped` -3. 分頁還在就心跳,不會進 1 - -開機/喚醒:已在跑就直接回;凍結中就 `unpause`(約一秒);沒有容器才 `provision`,等 `/tmp/lazyboy/ready`。畫面走 `/view/{bot}/vnc.html`,已登入的 cookie 轉到 websockify。 - ---- - -## 它怎麼看、怎麼點 - -![你看乾淨的,它看有編號的](./docs/diagrams/look.png) - -![能認控制項,就不要猜座標](./docs/diagrams/click.png) - -模型從不直接連 VNC。它只打 API 工具;工具經 sandbox HTTP 進 supervisor,再 `docker exec` 或打容器內 `controld`。 - -編號只畫在給模型的 JPEG 上。VNC 是乾淨桌面。每次 navigation/snapshot 會重編號,舊 id 作廢。`computer_act` 點在瀏覽器視窗上會被拒,避免用像素點網頁。解析度契約是 **1280×800**。 - -每個 bot 一個 `computer_screens` 列:slot、DISPLAY、執行租約、控制租約。人接管寫 `control_holder=user`,worker 在回合邊界停,不跟你搶滑鼠。`view_only` 用 postMessage 切,不重掛 iframe,所以換手時預覽不會黑掉。 - -`lazyboy-controld` 聽 `127.0.0.1:7070`。Team 多螢幕:slot 0 = `:1`,slot N = `:N+1`。`lazyboy-screen ensure` 在同一個容器裡再長一組 Xvfb。 - ---- - -## 模型金鑰從哪來 - -![由近到遠,找到第一把就用](./docs/diagrams/keys.png) - -`crates/harness` 不管滑鼠,只決定這次 run 要用哪一家模型。真正的 agent 迴圈在 `crates/api/src/runs.rs`。 - -金鑰:**這個機器人 → 工作區設定 → 環境變數**。API 跑在 Docker 時,迴圈位址 `127.0.0.1` 會被改成 `host.docker.internal`,才能打到你本機的相容端點。 - ---- - -## 教會它 - -![你做一次,它記住為什麼](./docs/diagrams/teach.png) - -之後 run 若 prompt 對得上技能名,會把完整 playbook 塞進當則,並清掉舊聊天以免模型複誦上次的「還在倒數」。執行仍用 `browser`/`computer_act`,在**現在**的畫面上找「Next」,不是記像素。 - ---- - -## 排程怎麼進工作 - -![到點以後,跟你傳訊息同一條路](./docs/diagrams/schedule.png) - -Cron 五欄。時區寫在列上,預設台北。`立刻跑` 只是立刻插一筆 run,不改下一拍時間。 - ---- - -## 專案目錄(二次開發從這裡找) - -![把它當成幾間房間,不是分層蛋糕](./docs/diagrams/folders.png) - -Cargo workspace。畫面在 `apps/web`,對話與工作在 `api`,怎麼點在 `control`,開機在 `supervisor` + `image/computer`,問哪一家模型在 `harness`。前端是獨立的 Vite app,由 API 把 `apps/web/dist`(或開發時的 `apps/web`)端出去。 +### 專案結構 ```text LazyBoy/ -├── apps/web/ 瀏覽器 UI(Vite + React) -│ ├── src/App.tsx 幾乎全部畫面:側欄、聊天、電腦、設定 -│ ├── src/schedule.tsx 排程面板 -│ ├── src/avatar.tsx Blobatar 頭像 -│ ├── src/api.ts fetch 包裝、401 -│ ├── src/types.ts 跟 API JSON 對齊的型別 -│ ├── src/locales/zh-TW.ts 所有使用者看得到的字 -│ ├── src/*.css 樣式(styles / chat / computer / refinements…) -│ └── vnc.html 內嵌桌面(noVNC);API 的 /view 會讀這一檔 +├── apps/web/ React 前端與 noVNC 頁面 ├── crates/ -│ ├── contracts/ 跨 crate 的型別:Bot、Run、ComputerState、動作 JSON -│ ├── harness/ 模型後端:CredentialChain、resolve_backend、connect_model -│ ├── control/ 桌面契約:螢幕 slot、租約、CDP/AT-SPI/xdotool、overlay -│ │ 含 a11y.py / cdp.py(容器裡被 exec 的腳本) -│ ├── sandbox/ API 打 supervisor 的 HTTP 客戶端;fake 給測試 -│ ├── supervisor/ Docker:provision / pause / unpause / exec / observe / act -│ ├── controld/ 打進容器的小 HTTP(127.0.0.1:7070) -│ └── api/ 唯一對外程序:路由、worker、idle、排程 tick、靜態網頁 -│ └── src/ -│ ├── main.rs 啟動、三條背景迴圈 -│ ├── routes.rs 組 router;bot / computer HTTP 也在這 -│ ├── runs.rs agent 迴圈(租約、complete_once、nudge) -│ ├── tools.rs tool_definitions + dispatch(加工具從這裡) -│ ├── computer.rs boot / 凍結 / 心跳 / 螢幕租約 -│ ├── sessions.rs 對話 CRUD、送訊息、SSE -│ ├── skills.rs 示範錄製與蒸馏 -│ ├── file_skills.rs 讀取 data/skills 下的唯讀 SKILL.md -│ ├── schedules.rs cron -│ ├── vault.rs 登入保險箱 -│ ├── memory.rs pgvector 記憶 -│ ├── mcp.rs MCP 連線 -│ ├── screen_proxy.rs /view 反代 -│ └── db.rs SQL 與列定義 -├── image/ -│ ├── api/Dockerfile -│ ├── supervisor/Dockerfile -│ └── computer/ 桌面映像 -│ ├── Dockerfile -│ ├── start.sh PID 1:controld + Xvfb/XFCE/VNC -│ └── lazyboy-screen Team 額外 DISPLAY -├── migrations/ sqlx,檔名流水號;API 啟動時自動 migrate -├── data/homes/ 每個電腦的家目錄(bind 進容器 /home/lazyboy) -├── data/skills//SKILL.md 工作區共用的 slash 技能(執行時掛載) -├── tests/ 跨語言的小測試(node:test、Python) -├── scripts/ init-env、build-computer-image、dev -├── docker-compose.yml 正式堆疊(Postgres + supervisor + API) -├── Makefile make up / dev / test -└── reference/rakazo/ 上游參考實作,不要當 runtime 依賴 +│ ├── api/ 對外 API 與 Agent 執行迴圈 +│ ├── contracts/ 跨元件資料契約 +│ ├── control/ 瀏覽器與桌面控制 +│ ├── controld/ 容器內控制服務 +│ ├── harness/ 模型與語音後端 +│ ├── sandbox/ API 到 Supervisor 的抽象 +│ └── supervisor/ Docker 生命週期管理 +├── image/ API、Supervisor、電腦映像 +├── migrations/ SQLx PostgreSQL migrations +├── tests/ Rust 以外的契約與回歸測試 +├── scripts/ 環境初始化與執行工具 +├── docs/hero.html README 首頁視覺原稿 +├── docs/workflow.html 可互動產品流程圖 +├── docker-compose.yml 正式堆疊 +└── Makefile 常用開發與部署指令 ``` -### 想改什麼,開哪個檔 +## 徽章與開源認證 -| 你要做的事 | 先開 | -| --- | --- | -| 加一個模型工具(例如 `screenshot_region`) | `crates/api/src/tools.rs`(`tool_definitions` + `dispatch`);若要 GUI,`runs.rs` 的 `tool_needs_sandbox` / `tool_needs_gui` | -| 工具對應的滑鼠/鍵盤/CDP | `crates/control/src/{actions,x11,cdp,a11y}.rs` 與同目錄 `.py` | -| 新的 HTTP 端點 | 功能模組自己的 `router()`(如 `schedules.rs`),在 `routes.rs` `.merge(...)`;電腦/bot 則直接寫在 `routes.rs` | -| 新狀態、動作 JSON、Run 狀態機 | `crates/contracts/src/`(改完 `api` / 前端 `types.ts` 一起對) | -| 換模型供應商或金鑰解析 | `crates/harness/src/resolve.rs`、`crates/contracts/src/model.rs`、`workspace.rs` | -| 容器怎麼開、凍結、等 ready | `crates/supervisor/src/docker.rs`、`crates/api/src/computer.rs` | -| 桌面裡多裝套件、改 XFCE、開機腳本 | `image/computer/`,然後 `make computer` | -| 對話 UI、電腦預覽、頭像小卡 | `apps/web/src/App.tsx` + 對應 css | -| 畫面上的中文 | `apps/web/src/locales/zh-TW.ts`(key 加了 `tsc` 才會過) | -| 內嵌 VNC 行為(貼上、唯讀) | `apps/web/vnc.html` | -| 新資料表 | `migrations/0xx_....sql`;列定義補 `crates/api/src/db.rs` | -| 排程 UI | `apps/web/src/schedule.tsx` | -| MCP 市集清單 | `crates/api/src/mcp_catalog.rs` | +README 頂端目前只顯示可以直接從原始碼驗證的資訊徽章,不把「尚未執行的檢查」包裝成通過。 -### 加一支工具的最短路徑 +若要取得可公開查驗的安全與品質徽章,建議依序完成: -1. `tools.rs` 的 `tool_definitions` 加 `ToolDefinition`(名稱、說明、JSON Schema)。說明是寫給模型看的。 -2. 同一個檔的 `dispatch` 加 match arm,回 `ToolOutcome { text, image, pause, blocks }`。 -3. 若會動到桌面:`runs.rs` 裡 `tool_needs_sandbox` / `tool_needs_gui` 把名字加進去,否則不會 boot、也拿不到螢幕租約。 -4. 需要新的容器指令就放 `control`(Rust 組 argv,Python 做 CDP/AT-SPI),supervisor 的 `exec` 已經會把 `DISPLAY` 帶進去。 -5. 前端若要顯示步驟文字,`runs.rs` 的 `describe_step` 加一列。 -6. `SANDBOX_PROVIDER=fake cargo test -p lazyboy-api` 先過,再對真容器看。 +- [ ] 在公開 GitHub repository 建立正式 mirror;OpenSSF 的公開查驗以 GitHub 為主要整合目標。 +- [ ] 加入完整 `LICENSE`、`SECURITY.md`、`CONTRIBUTING.md` 與行為準則。 +- [ ] 建立 CI:Rust format / Clippy / tests、前端 typecheck / build / tests。 +- [ ] 啟用 Dependabot 或 Renovate、CodeQL、secret scanning 與 branch protection。 +- [ ] 執行並發布 [OpenSSF Scorecard](https://scorecard.dev/) 結果後,再加入 Scorecard 徽章。 +- [ ] 在 [OpenSSF Best Practices](https://www.bestpractices.dev/) 登記專案、誠實完成 Passing 問卷後,再加入認證徽章。 +- [ ] 若提供容器映像,再加入 SBOM、簽章與可重現版本發布流程。 -### 本機二次開發迴圈 +> 不建議現在顯示 CI passing、coverage、OpenSSF 或 Best Practices 徽章:目前 repository 沒有對應的公開結果,徽章會失真或直接顯示 unknown。 -```bash -make env && make postgres # 資料庫 -make computer # 桌面映像有改才需要 -make dev-supervisor # 終端 1,:7091 -make dev-api # 終端 2,:3101,會自動跑 migrations -# 前端另開: -cd apps/web && npm install && npm run dev # :5173 -``` +## 專案狀態 -- 改 `apps/web/src`:Vite 熱更新。 -- 改 `crates/api`:停掉 `dev-api` 再 `make dev-api`。 -- 改 `crates/supervisor`:同樣重跑 supervisor。 -- 改 `crates/control` 的 `.py`:映像沒重建的話,執行中的容器還是舊腳本;要嘛 `make computer` 後重開電腦,要嘛確認 supervisor exec 讀的是映像內檔案。 -- 改 `image/computer`:一定 `make computer`,再在 UI 重啟該台電腦。 -- 契約改了:同時改 `contracts`、呼叫端、`apps/web/src/types.ts`。 +LazyBoy 目前版本為 `0.1.0`,仍屬早期階段。建議先在本機或受信任網路中使用;對外部署前請完成威脅模型、權限檢查、備份與還原演練。 -檢查: - -```bash -make fmt && make clippy && make test -node --test tests/frontend.test.mjs # 排程 cron、VNC 貼上、登入填表防護 -``` - -`SANDBOX_PROVIDER=fake` 時 API 不碰 Docker,適合先測 run/工具契約。 - -`reference/rakazo/` 是對齊用的上游,不要在 LazyBoy runtime import 它。 +Cargo workspace 的授權中繼資料宣告為 MIT。若要正式公開散布,應先在 repository 根目錄補上完整 MIT `LICENSE` 文字,再把上方授權徽章改為連到該檔案。 --- -## 安全(操作時要記得) +
-- Supervisor 只在 Compose 內網,不要對 LAN 開埠 -- 畫面走已登入 API,VNC 密碼不進瀏覽器 URL -- 區網請走 HTTPS;終端是 HTTPS 時設 `LAZYBOY_SECURE_COOKIE=true` -- API 綁非本機時 `LAZYBOY_APP_TOKEN` 至少 32 字;supervisor 拒絕空白、過短、`dev-token` -- 保險箱用 `LAZYBOY_VAULT_KEY`;換登入 token 時這把 key 要留著 -- 模型看不到密碼本文;2FA/CAPTCHA 一定要人在**它的**畫面上處理 +用自然語言交代工作,保留看得見、接得回來的控制權。 + +
diff --git a/docs/bug-improvement-plan.md b/docs/bug-improvement-plan.md deleted file mode 100644 index b9631dc..0000000 --- a/docs/bug-improvement-plan.md +++ /dev/null @@ -1,30 +0,0 @@ -# Bug 檢查與改善計劃 - -檢查日期:2026-09-05。範圍為目前工作目錄的前端、排程與 VNC 剪貼簿整合;不是全專案完整稽核。保留原有未提交修改。 - -## 本次已修正 - -- 頭像:縮小聊天氣泡 span 選擇器範圍,固定頭像尺寸,群聊頂欄改以實際 32px 計算堆疊位置,移除額外縮放。 -- 綠點:恢復頭像本身的 presence,取消側欄額外偽元素,限制綠點大小;未讀仍以右上藍點區別。 -- 排程:補齊表單、checkbox、換行及按鈕配置;由整個電腦側欄負責捲動,避免列表與編輯器壓縮預覽。聊天排程卡片與文字採上下排列。 -- 等待動畫:電腦採獨立薄荷色機器人、呼吸光環與琥珀軌道;聊天只依工作中的對話顯示思考,不再因電腦啟動/連線而出現。排除 null session ID 互相比較產生假忙碌。 -- 剪貼簿:在 noVNC 接收前攔截 Ctrl/Cmd+V,讀取本機文字後只送出一次遠端貼上;權限不足時開啟手動貼上框。延遲貼上期間換連線或變唯讀就取消。父子訊息驗證來源視窗,避免舊 iframe 更新目前狀態。 - -## 計劃項目完成狀態 - -| 優先 | 項目 | 實作與驗證 | -| --- | --- | --- | -| P1 | 固定間隔排程 | 改用 `@every Nm/Nh/Nd`,一天固定 24 小時;跨月、DST、漏跑相位測試通過。舊 cron 保留原文 | -| P1 | Cron 編輯不失真 | 僅五欄及合法範圍轉換預設;未知格式保留 Advanced,空值禁止儲存;修正 Unix/Rust 星期編號差異 | -| P1 | 電腦啟動失敗恢復 | 失敗立即刷新狀態,刷新失敗回復前值;回應只更新原本的 bot | -| P1 | 遠端貼上同步 | 後端確認 X11 剪貼簿內容後才貼上;終端使用 Ctrl+Shift+V,連續貼上依序處理 | -| P2 | 複製與權限提示 | 顯示同步結果、權限失敗退路;macOS Cmd+C 交由後端判斷終端快捷鍵 | -| P2 | CSS 拆分 | Avatar、Chat、Computer、Schedule 分檔,響應式規則集中最後載入 | -| P2 | 主 JS 過大 | 動畫改為 lazy import;主檔約 446 kB,動畫 chunk 約 318 kB,消除 500 kB chunk 警告 | - -## 驗證與限制 - -- 前端 TypeScript、正式打包、5 個 Node 回歸測試、3 個 Python 控制測試通過;Rust 驗證詳見[安全與 Harness 檢查](security-and-harness-review.md)。 -- 瀏覽器工具回報沒有可用瀏覽器,尚未完成視覺截圖與真實 VNC 端到端驗收。這些項目已完成程式修正,不能等同所有畫面已實機驗收。 -- 待實機驗收:1440×900、1280×720、390×844;群聊、長排程、多筆排程、開機與聊天各自獨立、權限拒絕、中文/emoji/多行與重連。 -- 剪貼簿支援純文字;圖片、檔案不在此次實作範圍。Lottie 上游 eval 提醒仍存在;未量測首屏時間。 diff --git a/docs/diagrams.html b/docs/diagrams.html deleted file mode 100644 index e966325..0000000 --- a/docs/diagrams.html +++ /dev/null @@ -1,925 +0,0 @@ - - - - - LazyBoy diagrams - - - - -
-
LAZYBOY · 怎麼轉起來
-

其實只有三個角色

-

不是一疊伺服器名詞。你說話、中間有人幫忙想、右邊那台電腦去按。

-
-
-
-
-
把這堂課看完
-
好,我去開電腦點 Next。
-
右側還能看見它的桌面
-
-
-
-
- -

瀏覽器
-
-

聊天、看桌面、必要時接手滑鼠。分頁開著,電腦就不會睡著。

-
-
-
-
-
-
- -
- 記憶技能要不要開機 -
-
-
-
-
-

LazyBoy

想、記、叫人做事
-
-

把話留給模型、把檔案留給磁碟。問候不會無故開機。

-
-
-
-
-
-
-
-
Chromium
-
star.example / course / 12
-
-

Workplace Safety

-

單元 12/18

-
-
- -
-
-
-
-
-
-
- -

它的電腦

真的 Linux 桌面
-
-

Debian、視窗、瀏覽器。家目錄在你硬碟上,不在別人雲裡。

-
-
-
-
- -
-
傳一句話之後
-

它先決定:聊就好,還是要動手

-

同一條路走完。差別只在第四步要不要打開那台電腦。

-
-
-
-
-
哈囉
-
把課看完
-
-
-
1

你送出

訊息進這則對話。同一句話重送不會變兩則。

-
-
-
-
-
現在這則
-
下一則等著
-
再下一則
-
-
-
2

排隊

這個 Agent 一次只做一件事。忙完才輪到下一則。

-
-
-
-
- -
記憶 · 技能 · 你剛說的
-
-
-
3

問模型

帶上記憶、技能、你剛說的話。金鑰用你自己的。

-
-
-
-
-
哈囉 → 直接回你
-
「把課看完」→ 開電腦再點
-
-
-
4

兩條岔路

問候不開機。真的要動手,才叫醒那台 Debian。

-
-
-
-
-
-
Chromium
-
star.example / course / 13
-
-

Quiz

-

右側畫面跟著動

-
-
-
-
-
-
-
-
5

寫回來

回覆出現在聊天裡。右側畫面跟著動。

-
-
-
- -
-
電腦的作息
-

開著、小睡、關機

-

分頁還在看,它就醒著。沒人看才睡,醒來幾乎不用等。

-
-
-
-
-
-
Chromium
-
你還在這個分頁
-
-

熱機中

-

每兩秒打一次招呼

-
-
-
-
-
-
-
-
-
1
-

開著

-

記憶體佔著,畫面隨時能進。

-
心跳執行中
-
-
-
-
-
-
-
Chromium
-
離開約 10 分鐘
-
-

凍結

-

行程還在記憶體裡

-
-
-
-
-
-
Zzz
-
-
-
2
-

小睡

-

像筆電合蓋。下次用,大約一秒內醒來。

-
凍結不要重開桌面
-
-
-
-
-
-
- -
家目錄還在
-
-
-
-
-
-
3
-

關機

-

約六小時後才真正停掉,把那 2 GB 還你。

-
停機家目錄還在
-
-
-
-
- -
-
它怎麼「看」螢幕
-

你看乾淨的,它看有編號的

-

模型從來不連你的 VNC。它拿一張蓋了黃字的截圖,點編號,不是猜像素。

-
-
-

你看到的

右側預覽,就是那台 Linux 桌面。沒有黃框、沒有編號。

-
-
-
-
Chromium
-
star.example / course / 12
-
-

Workplace Safety

-

單元 12/18 · 看完按 Next

-
-
    -
  • 11. 防護用具
  • -
  • 12. 現場巡視
  • -
  • 13. 測驗
  • -
-
- -
-
-
-
-
-
-
-

模型看到的

同一瞬間多一層黃字。它說「點 3」,就是點那個按鈕。

-
-
-
-
Chromium
-
star.example / course / 12
-
-

Workplace Safety

-

單元 12/18 · 看完按 Next

-
-
    -
  • 11. 防護用具
  • -
  • 12. 現場巡視
  • -
  • 13. 測驗
  • -
-
- -
-
-
-
-
-
-
-
- -
-
動手的三種方式
-

能認控制項,就不要猜座標

-

網頁走瀏覽器、對話框走無障礙樹,實在沒編號才點像素。

-
-
-
-
-
-
Chromium
-
mail.example / inbox
-
-

收件匣

-
-
訓練系統 · 請完成單元 12
-
HR · 本週排班
-
IT · 密碼即將到期
-
-
- - -
-
-
-
-
-
-
-
1
-

網頁

-

Chromium 裡的按鈕、輸入框。點 id,會自己捲到看不見的地方。

-
優先
-
-
-
-
-
-

開啟檔案

-
report.pdf
-
syllabus.pdf
-
notes.txt
-
開啟4
-
-
-
-
2
-

桌面視窗

-

檔案選取、系統對話框。用無障礙名稱,不是用視窗外框。

-
原生 GUI
-
-
-
-
-
-
-
640, 400
-
-
-
-
3
-

座標

-

畫布、沒有名字的控制項。最後才用。畫面契約是 1280×800。

-
不得已
-
-
-
-
- -
-
教會它
-

你做一次,它記住「為什麼」

-

不是錄巨集。它記你點了哪個控制項、填了什麼、去了哪一頁,下次在活的畫面上自己找。

-
-
-
-
-
-
你在操作
-
star.example / course / 12
-
-

Workplace Safety

-

你正在示範

-
- -
-
-
-
-
-
1

你示範

畫面交給你,用平常的方式做完。密碼欄不會被記。

-
-
-
-
-
click Next
-
fill 姓名
-
goto /quiz
-
-
-
2

它在旁邊看

記語意事件。畫面沒變就不存,避免一堆廢幀。

-
-
-
-
- 技能草稿 - 目標:看完課並交測驗
- 輸入:學員姓名
- 步驟:找到 Next → 填表 → 送出
- 完成:出現分數 -
可改名 · 試跑 · 匯出 JSON
-
-
-
3

整理成技能

目標、可變輸入、步驟、怎麼算做完。你可以改名字再存。

-
-
-
-
-
-
下次自己做
-
star.example / course / 3
-
-

另一堂課

-

在現在的畫面上找 Next

-
- -
-
-
-
-
-
4

下次自己做

找「Next」,不是重播上次的座標。

-
-
-
- -
-
排程
-

到點以後,跟你傳訊息同一條路

-

不是另一套引擎。鬧鐘響了,就塞進同一個工作隊列。

-
-
-
-
-
每天九點交報告
-
對話裡講,或側欄自己建
-
-
-
1

你說「每天九點」

五欄時間,預設台北。

-
-
-
-
-
- - - - - - -
每天 09:00
-
-
-
2

鬧鐘記住

到點前它只是一列時間。按「立刻跑」也不會把鬧鐘撥亂。

-
-
-
-
-
-
09:00 交報告
-
你剛傳的那則
-
下一則等著
-
-
-
3

變成普通工作

跟你剛傳的那則訊息一樣:排隊、問模型、必要時開電腦。

-
-
-
- -
-
模型金鑰
-

由近到遠,找到第一把就用

-

不管滑鼠。只決定這次要問哪一家模型。

-
-
-
-
- -
xai-••••••••••••用這把
-
-
-
1

這個機器人

它自己若存了 key,用它的。適合不同 Agent 接不同家。

-
-
-
-
- -
尚未填寫
-
-
-
2

工作區設定

本機工作區裡填的。畫面上存的會蓋過環境變數。

-
-
-
-
- -
XAI_API_KEY=…
OPENAI_API_KEY=
-
-
-
3

環境變數

什麼都沒填才落到這裡。

-
-
-
- -
-
想改程式時
-

把它當成幾間房間,不是分層蛋糕

-

從你看得見的畫面往裡走。不必先背 crate 依賴圖。

-
-
-
apps/web
-

畫面

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 聊天、電腦預覽、頭像、排程表單。中文在 locales。VNC 在 vnc.html。 -
-
-
-
crates/api
-

對話與工作

-
- 送訊息、工具、開機、技能、排程、保險箱。加工具從 tools.rs 開始。 -
-
-
-
crates/control
-

怎麼點

-
- 編號、CDP、無障礙樹、滑鼠。桌面上的手。 -
-
-
-
supervisor + image/computer
-

怎麼開機

-
- 容器的生老病死,以及 XFCE/Chromium 長什麼樣。 -
-
-
-
crates/harness
-

問哪一家模型

-
- 金鑰從哪來、打哪一個網址。不管滑鼠。 -
-
-
- - - - diff --git a/docs/diagrams/chat.png b/docs/diagrams/chat.png deleted file mode 100644 index 0b1adf5..0000000 Binary files a/docs/diagrams/chat.png and /dev/null differ diff --git a/docs/diagrams/click.png b/docs/diagrams/click.png deleted file mode 100644 index abd9b47..0000000 Binary files a/docs/diagrams/click.png and /dev/null differ diff --git a/docs/diagrams/folders.png b/docs/diagrams/folders.png deleted file mode 100644 index 9c2fe51..0000000 Binary files a/docs/diagrams/folders.png and /dev/null differ diff --git a/docs/diagrams/keys.png b/docs/diagrams/keys.png deleted file mode 100644 index 63af9d8..0000000 Binary files a/docs/diagrams/keys.png and /dev/null differ diff --git a/docs/diagrams/look.png b/docs/diagrams/look.png deleted file mode 100644 index f9b2398..0000000 Binary files a/docs/diagrams/look.png and /dev/null differ diff --git a/docs/diagrams/map.png b/docs/diagrams/map.png deleted file mode 100644 index 7c6928b..0000000 Binary files a/docs/diagrams/map.png and /dev/null differ diff --git a/docs/diagrams/schedule.png b/docs/diagrams/schedule.png deleted file mode 100644 index 27c229a..0000000 Binary files a/docs/diagrams/schedule.png and /dev/null differ diff --git a/docs/diagrams/sleep.png b/docs/diagrams/sleep.png deleted file mode 100644 index 06c267c..0000000 Binary files a/docs/diagrams/sleep.png and /dev/null differ diff --git a/docs/diagrams/teach.png b/docs/diagrams/teach.png deleted file mode 100644 index d58a836..0000000 Binary files a/docs/diagrams/teach.png and /dev/null differ diff --git a/docs/hero.html b/docs/hero.html index bece8d5..34b5730 100644 --- a/docs/hero.html +++ b/docs/hero.html @@ -360,11 +360,11 @@ LazyBoy -
本機開源 每個 Agent 有自己的 Linux 桌面
+
本機自託管 每個 Agent 有自己的 Linux 桌面

給 Agent 一台真的電腦

-

在瀏覽器裡開機器人。它會自己開網頁、敲指令、學你示範過的流程。
金鑰、模型、桌面都在你這台機器上。

+

在瀏覽器裡開機器人。它會自己開網頁、敲指令、學你示範過的流程。
對話與桌面資料留在你的主機,模型連線由你選擇。

diff --git a/docs/readme-hero.png b/docs/readme-hero.png index 3c9c7a4..3ded218 100644 Binary files a/docs/readme-hero.png and b/docs/readme-hero.png differ diff --git a/docs/security-and-harness-review.md b/docs/security-and-harness-review.md deleted file mode 100644 index 58572b3..0000000 --- a/docs/security-and-harness-review.md +++ /dev/null @@ -1,62 +0,0 @@ -# 安全、Harness 與 Docker 檢查 - -日期:2026-09-05。檢查第一方 Rust workspace、前端、控制腳本及容器設定;保留工作目錄原有修改。這是程式與依賴檢查,不是完整滲透測試;reference 第三方專案未逐行稽核。 - -## 已修正的安全問題 - -| 風險 | 問題與修正 | 主要位置 | -| --- | --- | --- | -| 高 | 桌面原本持有 supervisor 主 token;改為依 home key 衍生的 HMAC token,管理路由驗證容器歸屬 | crates/supervisor/src/docker.rs、main.rs | -| 高 | 可提供任意 home 路徑及透過 symlink 越界;限定 DATA_DIR/homes/key,附件改用 cap-std 目錄能力讀寫 | supervisor/docker.rs、api/attachments.rs | -| 高 | 已儲存密碼可能填入不相符網站;現在強制指定 hostname 與 HTTPS,不自動送出;DOM 觀察遮蔽密碼類欄位 | api/tools.rs、control/cdp.py | -| 高 | 桌面提供的 JavaScript 可能在 API 同源執行;noVNC 靜態程式改由 API 可信映像提供 | api/screen_proxy.rs、image/api/Dockerfile | -| 中 | 固定登入 cookie 無法個別撤銷;改為隨機 session、伺服器期限及登出撤銷,加入 Origin/Fetch Metadata 與本機 Host 檢查 | api/auth.rs | -| 中 | MCP 子程序繼承服務秘密;改為環境白名單及明確設定 | api/mcp.rs | -| 中 | 工具輸出可能進入記錄;移除輸出摘錄;附件隨機儲存名稱避免覆寫 | api/runs.rs、attachments.rs | - -Session 目前放在單一 API 記憶體,重啟失效;不適用多副本共用登入。已建立的 WebSocket 不會因 cookie 撤銷立即關閉。MCP 仍是同 UID 的可信子程序,環境白名單不等於作業系統沙箱。 - -## Harness 與電腦控制 - -- 修正 observe 對 POST-only 端點誤用 GET,消除正常路徑每次失敗再 fallback。 -- 移除有副作用操作失敗後的自動重播;執行前落盤 toolsStarted,完成批次保存上下文 checkpoint。worker 在不確定操作是否執行的狀態中斷,會標記失敗而非重做。 -- 模型暫時失敗採有限次重試與退避;各工具及程序有期限與輸出上限。Checkpoint 移除截圖並限制 1 MiB;超限保留不確定狀態以避免盲目重播。這不是 exactly-once 保證。 -- 使用 websocket-client 處理 CDP framing、控制訊框與 timeout;限制 Chromium 除錯介面為 loopback,移除 wildcard Origin。 -- DOM snapshot 使用每次唯一 selector,降低舊觀察誤點新元素風險;點擊前檢查可見、啟用、遮擋,動作後等待有限畫面更新。 -- 指標/視窗觀察並行;終端剪貼簿使用正確快捷鍵,確認 X11 文字一致後再貼上。 -- 排程以交易與 SKIP LOCKED 避免多 worker 重複派發,入列與下次時間一併提交。Unix 星期轉換已有回歸測試。DOM 與 DOW 同時受限的 cron 明確拒絕,避免不同 cron 引擎的 OR/AND 語意差異。 - -這些改動減少多餘往返與重播風險,尚未進行真實桌面延遲 benchmark,沒有速度倍數保證。DOM 優先、需要時使用桌面操作仍是本專案適合的路徑;沒有為了換框架重寫整個控制層。 - -## Docker 改善與升級 - -已加入 cargo/npm 建置 cache、cargo --locked、npm ci、直接 MCP 套件版本固定、API 非 root、capabilities 限縮、健康檢查、restart/init、程序與日誌上限。資料庫/控制網路獨立;預設只在 127.0.0.1:3101 開放 API,Postgres 不對主機映射。開發資料庫使用 make postgres 的額外 Compose 設定。 - -新安裝執行 make env,產生四組獨立秘密與隨機資料庫密碼,檔案權限 0600。既有 .env 不會被覆寫。 - -既有安裝更新時: - -1. 備份資料庫、data 與 .env。此次僅建置映像,沒有重啟你的正式服務、旋轉秘密或刪除既有桌面。 -2. 保留原本 vault key。若之前未設定 LAZYBOY_VAULT_KEY,先把原本 LAZYBOY_APP_TOKEN 的值保存為 LAZYBOY_VAULT_KEY,才能旋轉 app token;否則舊密碼可能無法解密。 -3. 舊桌面曾取得 supervisor 主 token,更新時應更換 LAZYBOY_SUPERVISOR_TOKEN 並重建所有舊桌面容器(保留 home 資料)。新版 controlVersion 會使舊容器在重新 provision 時重建。 -4. API 改為 UID 1000。檢查既有 API 資料與快取目錄是否可由 UID 1000 存取;只調整確定需要的目錄,勿遞迴改動所有使用者 home。HOST_DATA_DIR 必須對應正確的主機資料目錄。 -5. 既有 Postgres volume 不會因改 .env 自動換密碼;密碼輪替須同時更新資料庫角色與連線設定。API 重啟後重新登入。 - -Supervisor 仍掌握 Docker socket,可控制 Docker 主機;cap_drop 不能消除此權限。較強隔離方案是專用 Docker daemon/VM。桌面 Chromium 既有 --no-sandbox 與可信 MCP 的執行邊界仍需納入威脅模型。基底映像尚未固定 digest,未做完整 OS image CVE 掃描;直接套件固定版本不代表所有下載資產都可完全重現。 - -## 驗證與剩餘告警 - -- Rust workspace:114 個測試通過,含隔離 PostgreSQL 測試;前端 Node 5 個、Python 3 個測試通過。 -- 前端 typecheck、production build、程式碼 diff 空白檢查(不含 README 的 Markdown 換行空白)、Compose config 驗證通過;三個 Docker 映像實際建置成功。 -- 隔離 API smoke test:未登入拒絕、跨站登入拒絕、合法登入可存取 API、可信 noVNC asset 可用、登出後舊 cookie 被拒絕;均使用最後建置的映像驗證。 -- npm audit:0。cargo-audit:RUSTSEC-2023-0071(rsa 0.9.10,無修補版本)仍在 lockfile,但目前啟用依賴樹 cargo tree -i rsa 無結果;不能把未使用的 lockfile 告警說成已移除。 -- paste 1.0.15 有停止維護告警 RUSTSEC-2024-0436,由 fastembed/影像相關上游引入,仍需追蹤替代版本。 -- 未完成瀏覽器視覺驗收、真實 VNC 剪貼簿端到端、故障注入/負載測試。Lottie eval 提醒仍存在。 - -## 方法參考 - -- [Docker 安全與 daemon 信任邊界](https://docs.docker.com/engine/security/)、[Docker socket 保護](https://docs.docker.com/engine/security/protect-access/)、[建置快取](https://docs.docker.com/build/cache/optimize/) -- [Playwright actionability](https://playwright.dev/docs/actionability):參考動作前狀態檢查原則,並未導入 Playwright runtime。 -- [長時間 agent harness](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents):持久化進度與恢復的設計參考。 -- [CDP 協定](https://chromedevtools.github.io/devtools-protocol/)、[websocket-client 用法](https://websocket-client.readthedocs.io/en/latest/examples.html) -- [RSA advisory](https://rustsec.org/advisories/RUSTSEC-2023-0071.html)、[paste advisory](https://rustsec.org/advisories/RUSTSEC-2024-0436.html) diff --git a/docs/workflow.html b/docs/workflow.html new file mode 100644 index 0000000..9fbe403 --- /dev/null +++ b/docs/workflow.html @@ -0,0 +1,14847 @@ + + + + + + + LazyBoy:從一句話到完成任務 Diagram + + + + + + + + + + + +
+ +
+
+
+

LazyBoy:從一句話到完成任務

+
+
+ + + + + + + +
+ + LazyBoy:從一句話到完成任務 + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / 任務主流程 + + + 02 / 持續自動化 + + + + EX / 資料與安全 + + + + + 說明目標 + + + 理解與編排 + + + 執行與回傳 + + + + Rust Agent Runtime + + Docker 隔離邊界 + + 排程、記憶與登入資料 + + + + + + + + + + + + + React 工作空間 · 對話・電腦・記憶 · 任務主流程 › 說明目標 · zh-TW / EN + + + + React 工作空間 + 對話・電腦・記憶 + zh-TW / EN + + + + API + Agent · 模型、工具與技能 · 任務主流程 › Rust Agent Runtime › 說明目標 · Rust / Axum + + + + API + Agent + 模型、工具與技能 + Rust / Axum + + + + Supervisor · 資源限制與生命週期 · 任務主流程 › Rust Agent Runtime › 理解與編排 + + + + Supervisor + 資源限制與生命週期 + + + + 隔離電腦 · 瀏覽器、檔案與輸入 · 任務主流程 › Docker 隔離邊界 › 理解與編排 · noVNC + + + + 隔離電腦 + 瀏覽器、檔案與輸入 + noVNC + + + + 即時結果 · 畫面、訊息與狀態 · 任務主流程 › 執行與回傳 + + + + 即時結果 + 畫面、訊息與狀態 + + + + 排程 · 到點自動執行 · 持續自動化 › 說明目標 + + + + 排程 + 到點自動執行 + + + + 記憶與事件 · PostgreSQL + pgvector · 資料與安全 › 排程、記憶與登入資料 › 說明目標 + + + + 記憶與事件 + PostgreSQL + pgvector + + + + 加密憑證庫 · 網站登入資料 · 資料與安全 › 排程、記憶與登入資料 › 理解與編排 + + + + 加密憑證庫 + 網站登入資料 + + + + + + 檢索與寫入 + + + + 受控工具呼叫 + + + + 螢幕與事件 + + + + Cron 觸發 + + + + 啟動・暫停・恢復 + + + + 僅匹配 HTTPS 網域 + + + + HTTP / WebSocket + + + + + Legend + + + User UI + + + + Agent logic + + + + Policy + + + + Tool action + + + + Context / trace + + + + Cloud service + + + +

+ + + + + + + + + +
+ + +
+
+
+
+

看得見,也接得回來

+
+
    +
  • • 即時觀看 Agent 桌面與目前步驟
  • +
  • • 需要時由使用者接管,再交還給 Agent
  • +
+
+ +
+
+
+

從一次任務到持續自動化

+
+
    +
  • • 示範操作可整理成可重用技能
  • +
  • • 排程、長期記憶與 MCP 延伸工作能力
  • +
+
+
+ +
+ + + + diff --git a/docs/workflow.json b/docs/workflow.json new file mode 100644 index 0000000..d428430 --- /dev/null +++ b/docs/workflow.json @@ -0,0 +1,90 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "LazyBoy:從一句話到完成任務", + "quality_profile": "showcase", + "views": [ + { + "id": "task-flow", + "label": "任務主流程", + "focus": ["workspace", "api", "supervisor", "desktop", "result"], + "note": "沿著主路徑查看指令如何成為可觀察、可接管的桌面操作。" + }, + { + "id": "memory-schedule", + "label": "記憶與排程", + "focus": ["api", "memory", "schedule"], + "note": "長期記憶與排程會在需要時補充情境並重新啟動任務。" + }, + { + "id": "safety", + "label": "隔離與憑證", + "focus": ["vault", "supervisor", "desktop"], + "note": "登入資料經加密保存,桌面工作負載在獨立容器中執行。" + } + ] + }, + "lanes": [ + { "id": "flow", "label": "任務主流程" }, + { "id": "automation", "label": "持續自動化" }, + { "id": "data", "label": "資料與安全", "variant": "exception" } + ], + "phases": [ + { "id": "intent", "label": "說明目標", "fromCol": 0, "toCol": 1 }, + { "id": "orchestrate", "label": "理解與編排", "fromCol": 2, "toCol": 3, "variant": "emphasis" }, + { "id": "operate", "label": "執行與回傳", "fromCol": 4, "toCol": 5, "variant": "dashed" } + ], + "groups": [ + { "id": "agent_core", "label": "Rust Agent Runtime", "lane": "flow", "fromCol": 1, "toCol": 2, "variant": "emphasis" }, + { "id": "sandbox_boundary", "label": "Docker 隔離邊界", "lane": "flow", "fromCol": 2, "toCol": 3, "variant": "security" }, + { "id": "durable_context", "label": "排程、記憶與登入資料", "lane": "data", "fromCol": 1, "toCol": 3, "variant": "dashed" } + ], + "mainPath": ["workspace", "api", "supervisor", "desktop", "result"], + "semanticChecks": { + "allowedRoots": ["workspace", "schedule", "vault"], + "allowedTerminals": ["result", "memory"], + "requiredPaths": [ + { "from": "workspace", "to": "result" }, + { "from": "schedule", "to": "desktop" }, + { "from": "vault", "to": "desktop" } + ] + }, + "nodes": [ + { "id": "workspace", "lane": "flow", "col": 0, "type": "frontend", "label": "React 工作空間", "sublabel": "對話・電腦・記憶", "tag": "zh-TW / EN", "width": 150 }, + { "id": "api", "lane": "flow", "col": 1, "type": "backend", "label": "API + Agent", "sublabel": "模型、工具與技能", "tag": "Rust / Axum", "width": 146 }, + { "id": "supervisor", "lane": "flow", "col": 2, "type": "security", "label": "Supervisor", "sublabel": "資源限制與生命週期", "width": 146 }, + { "id": "desktop", "lane": "flow", "col": 3, "type": "cloud", "label": "隔離電腦", "sublabel": "瀏覽器、檔案與輸入", "tag": "noVNC", "width": 146 }, + { "id": "result", "lane": "flow", "col": 4, "type": "frontend", "label": "即時結果", "sublabel": "畫面、訊息與狀態", "width": 142 }, + { "id": "schedule", "lane": "automation", "col": 1, "type": "messagebus", "label": "排程", "sublabel": "到點自動執行", "width": 130 }, + { "id": "memory", "lane": "data", "col": 1, "type": "database", "label": "記憶與事件", "sublabel": "PostgreSQL + pgvector", "width": 154 }, + { "id": "vault", "lane": "data", "col": 3, "type": "security", "label": "加密憑證庫", "sublabel": "網站登入資料", "width": 142 } + ], + "edges": [ + { "id": "workspace-api", "from": "workspace", "to": "api", "label": "HTTP / WebSocket", "variant": "emphasis", "role": "main" }, + { "id": "api-supervisor", "from": "api", "to": "supervisor", "label": "受控工具呼叫", "variant": "security", "role": "main" }, + { "id": "supervisor-desktop", "from": "supervisor", "to": "desktop", "label": "啟動・暫停・恢復", "variant": "security", "role": "main" }, + { "id": "desktop-result", "from": "desktop", "to": "result", "label": "螢幕與事件", "variant": "emphasis", "role": "return" }, + { "id": "schedule-api", "from": "schedule", "to": "api", "label": "Cron 觸發", "variant": "dashed", "role": "async" }, + { "id": "api-memory", "from": "api", "to": "memory", "label": "檢索與寫入", "variant": "dashed", "role": "branch" }, + { "id": "vault-desktop", "from": "vault", "to": "desktop", "label": "僅匹配 HTTPS 網域", "variant": "security", "role": "branch" } + ], + "cards": [ + { + "dot": "emerald", + "title": "看得見,也接得回來", + "items": [ + "即時觀看 Agent 桌面與目前步驟", + "需要時由使用者接管,再交還給 Agent" + ] + }, + { + "dot": "violet", + "title": "從一次任務到持續自動化", + "items": [ + "示範操作可整理成可重用技能", + "排程、長期記憶與 MCP 延伸工作能力" + ] + } + ] +} diff --git a/reference/rakazo b/reference/rakazo deleted file mode 160000 index 0f5c4ce..0000000 --- a/reference/rakazo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0f5c4cefd59cdbe440deb7e05fd3f503164a6068