Files
agent-desktop/README.md
T
Pine 123ba0d0fa feat: 桌面端认证/通信/tauri 后端更新 + README
- auth/jwt/header 认证流程更新,会话与令牌处理增强
- 通信(ChatWindow/ConversationList/Network)更新
- tauri backend.rs/remote.rs、tauri.conf.json 更新
- pineagents app 路由(agent_gate/opc)与配置更新
2026-09-11 21:17:48 +08:00

666 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<p align="center">
<img src="./assets/readme/hero.svg" width="100%"
alt="PineAgents — 本地或云端部署的智能体工作台:多频道对话、Skills/插件扩展、定时任务与代码模式">
</p>
<p align="center">
<b>闭源商业软件</b> · 自有许可见 <a href="LICENSE">LICENSE</a> · 上游 QwenPaw 许可见 <a href="LICENSE_QwenPaw">LICENSE_QwenPaw</a> · 修改记录见 <a href="NOTICE">NOTICE</a>
</p>
---
## 目录
- [项目简介](#项目简介)
- [目录结构](#目录结构)
- [架构与流程](#架构与流程)
- [环境要求](#环境要求)
- [安装(从源码)](#安装从源码)
- [启动](#启动)
- [版本号管理(唯一来源)](#版本号管理唯一来源)
- [构建与打包](#构建与打包)
- [版本号设置与同步](#版本号设置与同步)
- [macOS / Windows 一键构建、产物清单](#macos--windows-一键构建产物清单)
- [手动分步构建](#手动分步构建)
- [增量更新(完整命令)](#增量更新incremental-update本地生成完整产物)
- [Docker 部署](#docker-部署)
- [测试](#测试)
- [二次开发指引](#二次开发指引)
- [许可证](#许可证)
---
## 项目简介
PineAgents 是一个可自主运行的**智能体操作系统(Agent OS)**:它通过多频道(钉钉、飞书、微信、QQ、Discord、Telegram 等)与用户对话,用 Skills 与插件扩展能力,支持定时任务(Cron)、多智能体编排、代码模式与三层记忆。
本项目是开源项目 [QwenPaw](https://github.com/agentscope-ai/QwenPaw)Apache 2.0)的**闭源商业衍生**,走「Fork + 上游同步」开发路线。所有基于上游的修改均登记在 [`NOTICE`](NOTICE)。
---
## 目录结构
### 顶层
| 路径 | 作用 |
|------|------|
| `VERSION` | **版本号唯一来源**Single Source of Truth),修改版本号只需改这一个文件 |
| `src/pineagents/` | **Python 后端运行时**(全部核心逻辑) |
| `console/` | **前端 Web 界面**React + Vite),含 Tauri 桌面壳(`console/src-tauri/` |
| `plugins/` | 独立插件包(channel / tool / app |
| `deploy/` | Docker 部署(`Dockerfile``entrypoint.sh``config/` |
| `scripts/` | 构建 / 安装 / 打包脚本(含 `sync_version.py` 版本同步) |
| `assets/readme/` | README 静态资源(本项目 SVG 图) |
| `tests/` | 测试(unit / contract / integration / e2e |
| `docs/secondary-dev/` | **二次开发文档**(品牌盘点、抽离方案) |
| `old_docs/` | **原上游文档归档**QwenPaw README/CONTRIBUTING/RELEASING 等) |
| `mykey/` | Tauri 更新签名密钥对(私钥已 gitignore,严禁提交) |
| `LICENSE` | PineAgents 闭源商业许可 |
| `LICENSE_QwenPaw` | 上游 QwenPawApache 2.0)许可 |
| `NOTICE` | 所有基于上游的修改说明(必读) |
| `SECURITY.md` | 安全策略 |
### 后端核心(`src/pineagents/`
| 模块 | 作用 |
|------|------|
| `__version__.py` | **版本号运行时获取**(从 `VERSION` 文件读取,支持开发/打包环境,自动迁移配置文件版本) |
| `constant.py` | 核心常量、路径、环境变量回退(品牌配置入口) |
| `branding.py` | **品牌单一事实源**PineAgents 命名,改品牌改这里) |
| `agents/` | 智能体核心:`react_agent.py`(决策循环)、prompt 构建、**Skill 系统**、工具集、记忆 |
| `runtime/` | 运行时编排:循环执行、工具守卫、slash 命令、prompt 管理 |
| `app/` | **FastAPI 服务层**:路由、频道接入、多智能体管理、Cron、工作区 |
| `cli/` | 命令行入口(`pineagents` / `qwenpaw` / `copaw`),含 TUI |
| `providers/` | 模型供应商(云端 / 本地 Ollama / LM Studio |
| `drivers/` | 协议连接器层(MCP / A2A / ACP |
| `governance/` | 权限治理(allow / deny / ask / sandbox |
| `sandbox/` | 内核级执行隔离(macOS Seatbelt / Linux Bubblewrap / Win AppContainer |
| `security/` | Tool Guard / File Guard / Skill Scanner 安全规则 |
| `memory/` | 记忆(三层:工作上下文、逐字历史、ReMe 个人知识库) |
| `loop/` | 循环模板(Coding Mode / Mission Mode)与审批门控 |
| `token_usage/` | 每轮 token / 上下文用量统计 |
| `plugins/` | 插件加载与市场 |
| `pawapp/` | 小程序(App)平台 |
| `tauri/` | Tauri 桌面端后端入口(sidecar 进程) |
### 前端(`console/`
| 路径 | 作用 |
|------|------|
| `src/pages/` | 页面:Chat、SettingsAgents/Models/Market…)、Agent 配置、Inbox、AppCenter |
| `src/layouts/` | 侧边栏 / 顶栏 / 菜单 / 路由注册 |
| `src/stores/` | 前端状态(agent、session、sidebar、message queue |
| `src/api/` | 后端 REST 接口封装 |
| `src/plugins/` | 插件宿主 SDK`window.PineAgents` 契约) |
| `src/locales/` | 多语言文案(zh/en/ja/ru/vi…) |
| `src/constants/branding.ts` | 前端品牌单一事实源 |
| `src/tauri/` | Tauri 桌面端 API 封装(更新、后端运行时等) |
| `vite.config.ts` | Vite 构建配置(从 `VERSION` 文件读取版本号并注入 `window.__APP_VERSION__` |
| `src-tauri/` | Tauri 桌面壳(Rust |
---
## 架构与流程
一次对话的主链路:**用户入口 → FastAPI 服务层 → 运行时编排 → 智能体决策循环 → 输出**,并由治理、沙箱、记忆、用量四类横向能力贯穿。
<p align="center">
<img src="./assets/readme/architecture.svg" width="100%"
alt="PineAgents 架构:多频道入口 → FastAPI → 运行时编排 → 智能体决策循环 → 输出,横向贯穿治理/沙箱/记忆/用量">
</p>
---
## 环境要求
| 组件 | 最低版本 | 说明 |
|------|---------|------|
| Python | 3.10+ | 推荐 3.11`.python-version` 固定) |
| Node.js | 18+ | 推荐 20 LTS |
| npm / yarn | 最新 | 前端包管理(项目用 yarn,`.yarnrc.yml` |
| Rust | 1.77+ | Tauri 桌面壳编译(`rust-toolchain.toml` 固定) |
| macOS | 12+ | 桌面端构建(Xcode Command Line Tools |
| Windows | 10+ | 桌面端构建(Visual Studio Build Tools |
| Linux | Ubuntu 20.04+ / Debian 11+ | 服务器部署 |
---
## 安装(从源码)
### 1. 克隆仓库
```bash
git clone <repo-url> agent-desktop
cd agent-desktop
```
### 2. Python 后端依赖
```bash
# 使用 uv(推荐,更快)
uv venv
uv pip install -e ".[dev]"
# 或使用 pip
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
```
### 3. 前端依赖
```bash
cd console
yarn install # 或 npm install
cd ..
```
### 4. 初始化配置
```bash
# 生成默认配置(~/.pineagents/config.json
pineagents init --defaults
# 或手动复制模板
cp config.example.json ~/.pineagents/config.json
```
---
## 启动
### 开发模式(推荐)
```bash
# 方式一:Tauri 桌面端(前端 + 后端 sidecar 一起启动)
cd console
yarn tauri:dev # 或 npx tauri dev
# 方式二:仅后端 API 服务
pineagents serve --host 127.0.0.1 --port 8088
# 方式三:前端开发服务器(单独调试前端,后端需单独启动)
cd console
yarn dev # 访问 http://localhost:8093
```
### 生产模式
```bash
# 后端服务
pineagents serve --host 0.0.0.0 --port 8088
# 或使用 systemd / supervisor 托管
```
---
## 版本号管理(唯一来源)
### 核心原则
**版本号只有一个真实来源:项目根目录 `VERSION` 文件。**
所有端(Python 后端、前端、Tauri、Cargo)的版本号都从这个文件读取或同步,禁止在代码中写死版本号。
### VERSION 文件格式
```
0.0.1b3
```
- 支持 PEP 440 格式:`0.0.1b3`beta)、`0.0.1a1`alpha)、`0.0.1rc1`RC)、`0.0.1`(正式版)
- Tauri 构建时自动转换为 semver 格式:`0.0.1b3``0.0.1-beta.3`
### 各端版本获取方式
| 端 | 获取方式 | 时机 |
|---|---|---|
| **Python 后端** | `__version__.py` 读取 `VERSION` 文件 | 运行时 |
| **前端** | `vite.config.ts` 读取 `VERSION`,注入 `window.__APP_VERSION__` | 构建时 |
| **Tauri** | `tauri.conf.json` version 字段(构建前自动同步) | 构建时 |
| **Cargo** | `Cargo.toml` version 字段(构建前自动同步) | 构建时 |
### 自动版本迁移
用户安装新版本后,首次启动应用时,后端会自动比较 `VERSION` 文件与用户配置文件(`~/.pineagents/config.json`)中的 `app_version`,若 `VERSION` 文件更新则自动写入配置文件——**无需用户手动删除配置**。
迁移逻辑在 `src/pineagents/__version__.py``ensure_config_version_updated()` 函数中实现,在后端启动入口(`tauri/entry.py``cli/app_cmd.py`)调用。
### 版本号获取优先级(Python 后端)
1. 环境变量 `PINEAGENTS_APP_VERSION`(部署覆盖用)
2. 项目根目录 `VERSION` 文件
3. PyInstaller 打包资源目录(`sys._MEIPASS/pineagents/VERSION`
4. 用户配置文件 `~/.pineagents/config.json``app_version`
5. 兜底默认值 `0.0.0`(仅在以上全部失败时使用)
---
## 构建与打包
### 版本号设置与同步
#### 修改版本号(标准流程)
```bash
# 1. 修改唯一来源
echo "0.0.1b4" > VERSION
# 2. 同步到各端配置文件(tauri.conf.json / Cargo.toml / branding.py / config.py
uv run scripts/sync_version.py
# 3. 验证一致性
uv run scripts/sync_version.py --check
```
#### 版本同步脚本说明
`scripts/sync_version.py``VERSION` 文件读取版本号,同步到以下文件:
| 目标文件 | 同步字段 | 格式 |
|---------|---------|------|
| `console/src-tauri/tauri.conf.json` | `version` | **semver**(自动转换,如 `0.0.1b3``0.0.1-beta.3` |
| `console/src-tauri/Cargo.toml` | `version` | **semver**(自动转换,Cargo 要求 semver 格式) |
| `src/pineagents/branding.py` | `version` | PEP 440 |
| `src/pineagents/config/config.py` | `app_version` 默认值 | PEP 440 |
> **注意**Tauri 和 Cargo 都要求 `version` 字段必须是 semver 格式,因此同步脚本会自动将 PEP 440 格式转换为 semver 格式。Python 后端相关文件(branding.py / config.py)保持 PEP 440 格式。
```bash
# 同步版本号
uv run scripts/sync_version.py
# 仅检查是否一致,不修改
uv run scripts/sync_version.py --check
```
> **注意**`npx tauri dev` 和 `npx tauri build` 会在 `beforeDevCommand` / `beforeBuildCommand` 中自动运行版本同步,无需手动执行。
#### Tauri 版本覆盖配置
`scripts/pack-tauri/sync_tauri_version.mjs``VERSION` 文件读取版本号,转换为 semver 格式,生成 gitignored 的 `console/src-tauri/tauri.version.conf.json`
```bash
node scripts/pack-tauri/sync_tauri_version.mjs
```
生成的 `tauri.version.conf.json` 包含:
- `version`semver 格式版本号(如 `0.0.1-beta.3`
- `plugins.updater`:更新配置(公钥、端点)
- `bundle.createUpdaterArtifacts`:是否生成更新签名(有私钥时自动开启)
> 不直接改 `tauri.conf.json` 的 version(避免 rebase 后产生陈旧的生成值);打包时以 `--config src-tauri/tauri.version.conf.json` 读取。
---
### macOS / Windows 一键构建、产物清单
#### 前置条件
1. 版本号已设置(`VERSION` 文件)
2. Tauri 更新签名私钥存在(`mykey/tauri-signer.key`,构建脚本自动加载)
3. 前端依赖已安装(`cd console && yarn install`
4. Python 依赖已安装(`uv pip install -e ".[dev]"`
#### 一键构建
```bash
# macOS → dist/ 下产出 .app / .dmg / zip / .app.tar.gz(自动更新产物)
./scripts/pack-tauri/build_macos_pyinstaller.sh
# WindowsPowerShell)→ 产出 NSIS 安装包
pwsh -File scripts/pack-tauri/build_win_pyinstaller.ps1
```
一键脚本内部依次完成:
1. **版本同步**`sync_version.py` + `sync_tauri_version.mjs`
2. **前端构建**`npm run build:prod`
3. **PyInstaller 后端 sidecar**:打包 Python 后端为可执行文件
4. **Tauri 打包**`tauri build --config src-tauri/tauri.version.conf.json`
5. **产物收集**:含 updater stage`.app.tar.gz` + `.sig`
#### macOS 签名
当前无 Apple Developer ID,一键脚本自动使用 ad-hoc 签名(`APPLE_SIGNING_IDENTITY="-"`),本地运行与分发可用但**不做公证**。
将来申请到 Developer ID 后:
```bash
export APPLE_SIGNING_IDENTITY="Developer ID Application: 你的证书名"
./scripts/pack-tauri/build_macos_pyinstaller.sh
```
#### 更新签名(Tauri v2 原生格式)
密钥对保存在项目 `mykey/` 目录:
- 私钥:`tauri-signer.key`(权限 600**已加入 .gitignore 严禁提交仓库**
- 公钥:`tauri-signer.key.pub`(已同步至 `tauri.conf.json` 与 server-core `.env``DESKTOP_UPDATER_PUBKEY`
构建脚本自动从 `mykey/tauri-signer.key` 加载私钥(单行 base64,无密码),无需手动 export。
如需手动注入:
```bash
export TAURI_SIGNING_PRIVATE_KEY="$(cat mykey/tauri-signer.key)"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" # 生成密钥时未设密码则为空
```
**重新生成密钥对**(丢失私钥后所有旧更新包将无法验证):
```bash
cd console && npx tauri signer generate -w ../mykey/tauri-signer.key --ci
# 然后把新公钥(mykey/tauri-signer.key.pub 内容)同步到:
# 1. tauri.conf.json plugins.updater.pubkey
# 2. server-core .env DESKTOP_UPDATER_PUBKEY
```
---
### 手动分步构建
如需分步调试,可按以下步骤手动构建:
```bash
# 1) 版本同步
uv run scripts/sync_version.py
node scripts/pack-tauri/sync_tauri_version.mjs
# 2) 前端引导产物
cd console
npm run build:tauri-bootstrap # 产出 dist-tauri
# 3) 后端 sidecarPyInstaller,产物在 dist/ 下)
cd ..
./scripts/pack-tauri/build_pyinstaller.sh # macOS / Linux
pwsh -File scripts/pack-tauri/build_pyinstaller.ps1 # Windows
# 4) Tauri 打包(macOS 示例)
# --bundles app 只构建 app+updater,跳过 dmgapp 体积>3G 时 dmg 易失败)
cd console
export TAURI_SIGNING_PRIVATE_KEY="$(cat ../mykey/tauri-signer.key)"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD=""
npx tauri build --config src-tauri/tauri.version.conf.json --bundles app
```
---
### 产物清单
| 产物 | 位置 | 用途 |
|---|---|---|
| `YunOPC-Hub.app` | `console/src-tauri/target/release/bundle/macos/` | macOS 应用本体 |
| `*-macOS.app.tar.gz` | `dist/`(一键脚本) | macOS 自动更新产物(Tauri updater |
| `*-macOS.app.tar.gz.sig` | `dist/` | 自动更新包签名(Tauri 原生格式) |
| `*-macOS.zip` | `dist/` | 手动分发压缩包(含 .app) |
| `*-setup.exe` | `dist/` | Windows 安装与自动更新 |
| 同目录 `*.sig` | 随安装包 | Tauri 更新签名(tauri build 自动生成) |
> **注意**:当前 macOS 构建默认 `--bundles app`(不生成 .dmg),因 app 内含 PyInstaller 后端(torch/pandas 等,>3G),dmg 打包易超时失败。如需 dmg,可手动 `npx tauri build --bundles app,dmg`,但建议先精简后端依赖。
---
### 增量更新(Incremental Update,本地生成完整产物)
增量更新 = **Tauri updater 让已安装客户端静默拉到新版本**
核心产物:
1. 按平台生成的更新包(`.app.tar.gz` / `*-setup.exe`
2. 对应 `.sig` 签名
3. 统一的 `latest.json` 清单
`generate_update_manifest.py` 提供两个子命令完成本地编排。
#### 子命令 1:`stage` — 收集单个平台的更新产物
① 首先构建产物(构建脚本会自动从 `mykey/tauri-signer.key` 加载私钥):
```bash
# macOS(产物含 .app.tar.gz + .sig,一键脚本已自动执行 stage)
./scripts/pack-tauri/build_macos_pyinstaller.sh
# Windows(产物含 -setup.exe + .sig
pwsh -File scripts/pack-tauri/build_win_pyinstaller.ps1
```
② 若构建脚本未替你 stage(或要在构建之外手动补跑),手动执行:
```bash
# macOSdarwin-aarch64 / darwin-x86_64 视 arch 而定)
python3 scripts/pack-tauri/generate_update_manifest.py stage \
--bundle-dir console/src-tauri/target/release/bundle/macos \
--pattern '*.app.tar.gz' \
--target darwin-aarch64 \
--output "dist/YunOPC-Hub-0.0.1b3-macOS.app.tar.gz" \
--pubkey-config console/src-tauri/tauri.version.conf.json
# Windowswindows-x86_64
python scripts/pack-tauri/generate_update_manifest.py stage \
--bundle-dir console/src-tauri/target/release/bundle/nsis \
--pattern '*-setup.exe' \
--target windows-x86_64 \
--output "dist/YunOPC-Hub-0.0.1b3-setup.exe" \
--pubkey-config console/src-tauri/tauri.version.conf.json
```
参数说明:
- `--bundle-dir`Tauri 产物目录(macOS 为 `bundle/macos`Windows 为 `bundle/nsis`
- `--pattern`:匹配产物名(macOS 用 `*.app.tar.gz`Windows 用 `*-setup.exe`
- `--target`:更新目标(`darwin-aarch64` / `darwin-x86_64` / `windows-x86_64`),必须与客户端目标一致
- `--output`:产物落盘路径,同名的 `.sig` 会一并复制
- `--pubkey-config`:可选;传 `tauri.version.conf.json` 时会把 `.sig` 的签名 key id 与配置里 `plugins.updater.pubkey` 比对,不一致直接报错(**强烈建议每次携带**)
每次 `stage` 会在产物同目录写一个 sidecar JSON`tauri-<target>-updater.json`),记录 `target / artifact / signature`
#### 子命令 2`manifest` — 汇总各平台 sidecar,生成统一 `latest.json`
```bash
python3 scripts/pack-tauri/generate_update_manifest.py manifest \
--version 0.0.1b3 \
--base-url "https://opc.pinesound.cn/desktop-updates/files" \
--metadata \
dist/tauri-darwin-aarch64-updater.json \
dist/tauri-windows-x86_64-updater.json \
--notes "发布说明:本次修复 xxx" \
--output dist/latest.json
```
参数说明:
- `--version`:本次版本(PEP 440,写入清单的 `version` 字段,会转成 semver
- `--base-url`:产物托管 URL 前缀(缺省所有平台统一;`url = <base-url>/<artifact>`
- `--metadata`:各平台 `stage` 生成的 sidecar JSON(可重复传多次)
- `--target-base 'target=URL'`:可选、可重复。当各平台产物在不同目录时按目标覆盖 `base-url`
- `--notes` / `--pub-date`:发布说明与时间(`pub-date` 缺省为当前 UTCISO 格式)
- `--output`:最终清单路径,即 `tauri.conf.json``plugins.updater.endpoints` 指向的那个 `latest.json`
#### 产物与清单的对应关系
| 文件名 | 说明 |
|---|---|
| `YunOPC-Hub-<version>-macOS.app.tar.gz`+`.sig` | macOS 增量更新包(Tauri updater |
| `YunOPC-Hub-<version>-setup.exe`+`.sig` | Windows 增量更新包(Tauri updater |
| `tauri-<target>-updater.json` | `stage` 生成的平台 sidecar |
| `latest.json` | `manifest` 汇总的最终清单(客户端拉取的入口) |
`latest.json` 内容结构:
```json
{
"version": "0.0.1-beta.3",
"notes": "发布说明:本次修复 xxx",
"pub_date": "2026-09-11T00:00:00Z",
"platforms": {
"darwin-aarch64": {
"url": "https://opc.pinesound.cn/desktop-updates/files/YunOPC-Hub-0.0.1b3-macOS.app.tar.gz",
"signature": "<minisign base64>"
},
"windows-x86_64": {
"url": "https://opc.pinesound.cn/desktop-updates/files/YunOPC-Hub-0.0.1b3-setup.exe",
"signature": "<minisign base64>"
}
}
}
```
#### 准备更新源 / 覆盖端点
`tauri.conf.json` 的缺省端点是 `https://opc.pinesound.cn/desktop-updates/latest.json`。构建/测试时可用环境变量覆盖(无需改配置):
```bash
export TAURI_UPDATER_ENDPOINTS='["https://opc.pinesound.cn/desktop-updates/latest.json"]' # 覆盖端点
export TAURI_UPDATER_PUBKEY='<公钥 base64>' # 覆盖签名公钥(缺省用 tauri.conf.json
export TAURI_SIGNING_PRIVATE_KEY="$(cat mykey/tauri-signer.key)" # 更新签名私钥(脚本会自动加载,仅手动注入时才需)
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" # 生成密钥时未设密码则为空
```
> 端点既接受 JSON 数组也接受逗号/换行分隔;JSON 数组会校验必须全部为字符串。
> 只要 `TAURI_SIGNING_PRIVATE_KEY` 有值,`sync_tauri_version.mjs` 就会开启 `createUpdaterArtifacts``tauri build` 才产出 `.sig`;否则跳过。
---
### 更新发布(admin 端)
自建更新源为 `https://opc.pinesound.cn/desktop-updates/*`server-core 动态生成 `latest.json` / `meta.json`,产物托管于 `/desktop-updates/files/*`)。
运营方在 admin-portal「桌面端版本」页面上传各平台安装包 + `.sig`,设置发布说明、灰度比例与强制最低版本后发布;桌面端通过 `X-App-Version` 请求头参与灰度判定,`min_version` 低于当前版本时强制自动安装。
#### 发布 SOP(每次发版)
```bash
# 1) 设置版本号(修改唯一来源 + 同步各端)
echo "0.0.1b3" > VERSION
uv run scripts/sync_version.py
# 2) 分别构建两平台(脚本自动从 mykey/tauri-signer.key 加载私钥,产出 .sig 与更新产物)
./scripts/pack-tauri/build_macos_pyinstaller.sh # 内部已 stage macOS 更新包
pwsh -File scripts/pack-tauri/build_win_pyinstaller.ps1
# 3) 若某平台未自动 stage(或需补跑),手动执行 stage(见上)
# 4) 汇总生成统一 latest.json
python3 scripts/pack-tauri/generate_update_manifest.py manifest \
--version 0.0.1b3 \
--base-url "https://opc.pinesound.cn/desktop-updates/files" \
--metadata dist/tauri-darwin-aarch64-updater.json dist/tauri-windows-x86_64-updater.json \
--output dist/latest.json
# 5) 把 latest.json + 各平台更新包(.app.tar.gz/-setup.exe) + 对应 .sig 上传到更新源
# (自建源 os 托管目录 /desktop-updates/files/
# 确认 latest.json 里 url/signature 与上传文件位置一致后,客户端即可自动更新
# 6) admin-portal「桌面端版本」→ 新建版本(版本号与第 1 步一致)
# 逐平台上传 安装包 + 同名 .sig → 设置发布说明/灰度比例/强制最低版本 → 发布
# 观察运行反馈后调至 100% 全量;异常时下架当前版本 / 重新发布上一版本回滚
```
> 桌面端更新源在 `console/src-tauri/tauri.conf.json` 的 `plugins.updater.endpoints`**老版本客户端仍指向旧源,需用新配置构建并发布一个版本才能完成源切换**。
> 遗留的 conda-pack 打包脚本(`scripts/pack/`)已废弃,仅供回滚,见 [`scripts/pack/README.md`](scripts/pack/README.md)。
---
### 网站
```bash
bash scripts/website_build.sh # 输出 website/dist/
```
---
## Docker 部署
### 构建镜像
`deploy/Dockerfile` 是多阶段构建:Stage 1 自动构建前端 ConsoleStage 2 组装 Python 运行时(含 Chromium 与 Playwright 配置),无需在宿主机预装前端依赖。
```bash
# 一键脚本(推荐)
bash scripts/docker_build.sh pineagents:latest
# 等价原始命令
docker build -f deploy/Dockerfile -t pineagents:latest .
```
> 默认排除 `imessage`(macOS 专用)频道。可用环境变量覆盖:
> `QWENPAW_DISABLED_CHANNELS=imessage,voice`(排除)或 `QWENPAW_ENABLED_CHANNELS=discord,telegram`(白名单)。
### 运行
```bash
# docker compose(推荐,端口 8088
docker compose up -d # 访问 http://127.0.0.1:8088/
# 或原始 docker run(挂载三个持久化卷)
docker run -d --name pineagents \
-p 127.0.0.1:8088:8088 \
-v pineagents-data:/app/working \
-v pineagents-secrets:/app/working.secret \
-v pineagents-backups:/app/working.backups \
pineagents:latest
```
容器启动时若工作区缺少 `config.json` 会自动执行 `pineagents init --defaults`
### 自定义端口与启用认证
```bash
docker run -d --name pineagents \
-p 127.0.0.1:3000:3000 \
-e QWENPAW_PORT=3000 \
-e QWENPAW_AUTH_ENABLED=true \
-e QWENPAW_AUTH_USERNAME=admin \
-e QWENPAW_AUTH_PASSWORD=changeme \
pineagents:latest
```
> ⚠️ 认证变量为 `QWENPAW_AUTH_ENABLED` / `QWENPAW_AUTH_USERNAME` / `QWENPAW_AUTH_PASSWORD`(旧名 `COPAW_*` 兼容)。注意 `docker-compose.yml` 注释中的 `PINEAGENTS_AUTH_*` 并非有效变量,属待修正的历史注释。
> 不启用认证时,容器会打印安全提示:请确保服务仅暴露于可信网络,或对不可信访问者开启认证。
---
## 测试
```bash
make test # 全部测试
make test-unit # 单元测试
make test-contract # 契约测试
make test-integration # 集成测试
make coverage-full # 覆盖率(含 HTML 报告)
make quick # 快速反馈(单测,失败即停)
```
或用测试入口脚本:
```bash
python scripts/run_tests.py # 全部测试
python scripts/run_tests.py -u # 单元测试
python scripts/run_tests.py -i # 集成测试
python scripts/run_tests.py -a -c # 全部 + 覆盖率
python scripts/run_tests.py -p # 并行(需 pytest-xdist
python scripts/run_tests.py -h # 帮助
```
---
## 二次开发指引
本项目基于 QwenPaw 二次开发,核心约定:
- **改品牌**:改后端 `src/pineagents/branding.py` + 前端 `console/src/constants/branding.ts`,不要改散落字面量。
- **改版本号**:只改项目根目录 `VERSION` 文件,然后运行 `python scripts/sync_version.py` 同步各端,禁止在代码中写死版本号。
- **改逻辑**:智能体决策见 `agents/react_agent.py`,编排见 `runtime/`,治理见 `governance/`
- **必读**`docs/secondary-dev/`(品牌盘点与抽离方案)、`NOTICE`(修改登记)、`SECURITY.md`
- **铁律**:任何基于上游 QwenPaw 的改动,**必须登记到 `NOTICE`** 后再提交。
上游同步:从远端 `github` 拉取 QwenPaw,改动推送至远端 `infoepoch_lan`
---
## 许可证
- **PineAgents**:闭源商业软件,见 [`LICENSE`](LICENSE)(模板,上线前请法务审核)。
- **上游 QwenPaw**Apache License 2.0,见 [`LICENSE_QwenPaw`](LICENSE_QwenPaw)。
- 修改说明见 [`NOTICE`](NOTICE)。
**Copyright © 2026 PineAgents 保留所有权利。**