diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index fdeb84f..0500107 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -41,6 +41,8 @@ jobs: run: python3 -m unittest discover -s tests -v - name: Compile Python sources run: python3 -m py_compile core/*.py scripts/tools/*.py scripts/build-online.py + - name: Verify generated web assets + run: python3 scripts/sync-web-assets.py --check - name: Build online packages run: python3 scripts/build-online.py - name: Verify package payload diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7a73b70..d6eb3d2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ permissions: jobs: validate: - runs-on: ubuntu-latest + runs-on: ${{ vars.AGENTBOOT_LINUX_RUNNER || 'ubuntu-latest' }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: @@ -24,10 +24,11 @@ jobs: python3 -m unittest discover -s tests -v python3 -m py_compile core/*.py scripts/*.py scripts/tools/*.py tests/*.py sh -n install.sh scripts/*.sh pages/install.sh cloudflare/deploy.sh + python3 scripts/sync-web-assets.py --check node --check cloudflare/worker.js online: - runs-on: ubuntu-latest + runs-on: ${{ vars.AGENTBOOT_LINUX_RUNNER || 'ubuntu-latest' }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: @@ -48,7 +49,7 @@ jobs: pages/VERSION offline-linux: - runs-on: ubuntu-latest + runs-on: ${{ vars.AGENTBOOT_LINUX_RUNNER || 'ubuntu-latest' }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: @@ -128,7 +129,7 @@ jobs: publish: needs: [validate, online, offline-linux, offline-windows, macos-smoke] if: github.ref_type == 'tag' - runs-on: ubuntu-latest + runs-on: ${{ vars.AGENTBOOT_LINUX_RUNNER || 'ubuntu-latest' }} environment: release-production permissions: contents: write diff --git a/.gitignore b/.gitignore index 49f94c3..228f62d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,7 @@ __pycache__/ node_modules/ .DS_Store Thumbs.db + +# 本地性能实验输出(固定基线 performance-baseline.json 需要提交) +performance-run.log +performance-round-*.json diff --git a/CHANGELOG.md b/CHANGELOG.md index a179219..5c65225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # 更新日志 +## v1.2.0 (2026-08-31) + +- 全面重构中英文主页:亮色乐观视觉、桌面/手机自适应、可访问性、品牌 404 与单一 canonical。 +- Pages 成为唯一网页源,Worker 自动生成同源资源;HTML 短缓存、版本化静态资源 immutable 缓存与 ETag 304。 +- `agentboot` 延迟加载 HTTP/SSL 网络栈与安装专用模块,控制台冷启动显著缩短。 +- 新增轻量 `launch.py`,让命令入口复用 Python 字节码缓存;频繁启动菜单实测降低约 34.6%。 +- 多 Agent 安装按相同 Node 约束合并 npm 解析与连接,失败自动回退逐个安装;批次内复用 Node、镜像和环境探测。 +- npm 安装优先本地缓存并关闭进度与冗余日志;在线安装器移除重复体检,POSIX 安装约减少一次 2 秒级网络检查。 +- 内置 Agent 使用紧凑请求 JSON,并在断线重连时复用 TLSContext;保持 TLS 校验、代理与流式安全边界。 +- 新增确定性性能基准,覆盖启动、内置 Agent 本地请求、安装调度与主页关键资源,所有实验保留/回退可审计。 + ## v1.1.0 (2026-08-30) - 新增 Agent 安全卸载:菜单 `[9]`、`agentboot uninstall `、批量卸载与 CoCo `--purge`。 diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..a7152a4 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,98 @@ +# DESIGN.md — AgentBoot + +## 1. Objective + +AgentBoot 的每个界面都应让人感到“工具已经准备好,下一步可以轻松开始”:明亮、可靠、有行动感,但不幼稚或喧闹。页面必须像一个经过认真编辑的开源工具,而不是套了营销模板的下载页;质量标准是可长期复用、可直接发布的产品级界面。 + +## 2. Product Context + +- **What the product does:** AgentBoot 用一套可验证的安装流程,在 Linux、macOS 与 Windows 上按需安装、配置和卸载主流 AI Agent。 +- **Who it's for:** 同时尝试多个命令行 AI Agent 的开发者与技术爱好者;他们会核对命令、来源和兼容性,希望快速开始,也在意安全边界与离线可用性。 +- **Adjacent brands (feel like these):** Linear 的信息纪律、Vercel 文档的技术清晰度、Duolingo 的积极能量。 +- **Distant brand (do not feel like this):** 远离典型 Web3 落地页;不使用霓虹渐变、漂浮光球和夸张承诺制造技术感。 +- **Cultural register:** 技术可信为底,乐观亲切为表。像一个熟悉终端、愿意把复杂步骤收拾干净的朋友。 + +## 3. Visual Foundations + +### 3a. Color + +- **Neutral scale:** `--ink-950: #12251D`, `--ink-800: #294238`, `--ink-600: #5B7067`, `--ink-400: #91A198`, `--paper: #FFFDF5`, `--surface: #FFFFFF`, `--line: #DCE7DF`, `--soft: #F2F7F2`。 +- **Accents:** `--green: #137A52` 为主要行动色,`--green-dark: #0D5E3E` 为悬停与高对比状态,`--sun: #FFD84D` 为乐观点睛色,`--coral: #FF7A59` 与 `--sky: #DFF3FF` 只用于结构提示。 +- **Semantic:** `--success: #137A52`, `--warning: #A65E00`, `--error: #B42318`, `--info: #176B87`。 +- **Usage rules:** 每个视口最多一个实心绿色主按钮;太阳黄承担品牌识别和视觉锚点,不承载小号正文;珊瑚色和天蓝色不得同时铺满大面积背景;正文永远使用中性色。 + +### 3b. Typography + +- **Display face:** `ui-rounded, "Avenir Next Rounded", "SF Pro Rounded", "Segoe UI", sans-serif`,只使用 700 与 800,标题字距 `-0.035em`。 +- **Body face:** `Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif`,使用 400、600、700。 +- **Monospace:** `"SFMono-Regular", Consolas, "Liberation Mono", monospace`。 +- **Type scale:** `12 / 14 / 16 / 18 / 24 / 32 / 48 / 72`,关键尺寸通过 `clamp()` 在相邻级别间平滑缩放。 +- **Weight discipline:** 800 只用于 H1 和关键数字,700 用于 H2/H3,600 用于标签与按钮,正文保持 400;不得把整段说明设为半粗体。 + +### 3c. Spacing & rhythm + +- **Base unit:** `4px`。 +- **Spacing scale:** `4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 96 / 128px`。 +- **Generous whitespace:** 桌面端主章节垂直内边距为 `96–128px`,平板为 `72–96px`,手机为 `56–72px`;正文行宽不超过 `68ch`,全站容器不超过 `1180px`。 + +### 3d. Component seeds + +- **Button:** 仅三类:绿色实心主操作、白底描边次操作、带方向箭头的文本链接。最小触控高度 `44px`,圆角 `12px`,不使用胶囊按钮。 +- **Card / container:** 只在命令、安全说明和深度信息确实需要成组时使用;圆角 `18–24px`、1px 实线边框、无通用阴影。功能列表使用开放式分隔线,不做 3×2 卡片阵列。 +- **Iconography:** 使用同一套 1.8px 圆角线性 SVG;图标只辅助动作或状态,不替代文字,不用 Emoji 作为装饰。 +- **Brand mark:** 由太阳黄圆点和深绿色终端折线组成,表达“启动”和“向前”,可纯 CSS/SVG 重建。 + +## 4. Accessibility + +- **Text contrast:** 正文至少 4.5:1,大字和 UI 至少 3:1;黄色不直接承载低字号文字。 +- **Motion:** 动画只包含 160–420ms 的淡入、位移和状态反馈;`prefers-reduced-motion: reduce` 时关闭非必要动画与平滑滚动。 +- **Focus indicators:** 所有可交互元素使用 `3px` 太阳黄外环加 `2px` 纸白间隔,不依赖颜色变化单独表达焦点。 +- **Alt text policy:** 装饰性几何图形 `aria-hidden="true"`;信息图形必须用可见文本或准确的 `aria-label` 说明其状态和意义。 +- **Interaction floor:** 触控目标至少 `44×44px`;命令行可键盘滚动,复制结果通过礼貌型 live region 通知;页面提供跳到主要内容的链接。 + +## 5. Voice & Tone + +- **Register:** 对话式技术表达,明确但不生硬,积极但不喊口号。 +- **Sentence rhythm:** 标题短而有动词;说明句长短混合,一段只传递一个判断。 +- **Words this brand uses:** “装好”、“验证”、“按需”、“开始创造”。 +- **Words this brand refuses:** “赋能”、“颠覆”、“无缝”、“革命性”、“解锁潜能”。 +- **Address:** 中文优先使用省略主语的直接表达,必要时称“你”;英文使用 “you”,不称 “users”。 + +## 6. Implementation Practices + +- **Token format:** 原生 CSS 自定义属性;静态页与 Worker 使用同一份 `pages/assets/site.css` 源文件。 +- **Component convention:** 无框架、无运行时依赖的语义 HTML + 原生 CSS/JavaScript;任何浏览器脚本失效时,安装命令、链接和主要信息仍可使用。 +- **Image treatment rules:** 不加载图库照片或 AI 位图;品牌表达来自排版、纯色几何和终端界面的代码原生图形。 +- **Grid system:** 最大 12 列的流式 CSS Grid;主要采用不对称 `7:5` 首屏、`5:7` 安装区和内容重要性驱动的分栏,而不是等宽卡片矩阵。 +- **Motion rules:** `cubic-bezier(.2,.8,.2,1)`,160–420ms;允许轻微上移、颜色和边框转换,禁止弹跳、无限漂浮和视差滚动。 +- **Synchronization:** `pages/` 是中英文 HTML、CSS 与 JavaScript 的唯一视觉源;生成脚本把其内容嵌入 Worker,并以检查模式阻止多个线上入口漂移。 + +## 7. Anti-Patterns + +- **No gradient hero backgrounds.** AgentBoot 的乐观感来自高明度纯色和清晰动作,不借用 SaaS 默认霓虹氛围。 +- **No Emoji section headings.** 技术可信度由语言、排版和一致线性图形建立,不用聊天式符号凑气氛。 +- **No 3×2 feature-card grid.** 功能按重要性分级,用开放式行列与一个重点模块表达,而不是把七项能力压成同样重量。 +- **No primary button everywhere.** 首屏和当前安装动作各自只有一个主要视觉重心,其余操作使用描边或文本链接。 +- **No empty superlatives.** 不写“强大、无缝、革命性”;每句价值陈述必须能由具体功能或发布验证支持。 +- **No forced dark mode.** 用户明确要求亮色,视觉在不同系统主题下保持统一;高对比与夜间阅读通过色值质量解决,不自动翻转品牌。 +- **No desktop shrink-down.** 手机布局重新排序、隐藏低价值表格列并扩大触控区,不只是把桌面内容缩小。 + +## 8. Decision-Making + +1. **Accessibility is the floor.** 品牌色、布局或动效与可访问性冲突时,先改设计选择。 +2. **Install clarity over visual novelty.** 安装命令、平台区别与安全说明不得为了构图被折叠或隐藏。 +3. **One source over local perfection.** Worker、Pages 与镜像必须共享视觉源;不接受某个入口单独手工美化。 +4. **Content hierarchy over completeness.** 核心价值先出现,深度能力后展开;同级信息过多时先合并语义,而不是再加卡片。 +5. **Optimism over spectacle.** 色彩与微动效鼓励行动,但不抢占命令和文档的阅读注意力。 +6. **Distinctive over generic.** 在不损害前五项的前提下,优先采用有 AgentBoot 识别度的结构,而非模板默认组件。 + +## 9. Workflow + +1. 先写清每个页面唯一要完成的用户动作,并确认中英文信息对齐。 +2. 用语义 HTML 排出阅读顺序,确保无 CSS/JavaScript 也能完成主要任务。 +3. 应用颜色、字阶、空间与组件规则,保持每屏只有一个主要动作。 +4. 从 `320px` 手机宽度开始布置,再扩展到平板、桌面和超宽屏。 +5. 运行生成/同步检查,保证 Worker、Pages 与站点镜像没有内容漂移。 +6. 用键盘、减少动态效果模式和高缩放检查交互与可读性。 +7. 在真实浏览器中对中英文页面执行桌面、平板、手机截图和溢出检查。 +8. 运行完整自动化测试与线上冒烟,确认安装分发端点未被视觉改动影响。 diff --git a/DESIGN_NOTES.md b/DESIGN_NOTES.md new file mode 100644 index 0000000..d04499b --- /dev/null +++ b/DESIGN_NOTES.md @@ -0,0 +1,23 @@ +# AgentBoot design notes + +## Current direction + +- Bright “launch day” visual language: paper white, action green, sun yellow, small coral/sky accents. +- Editorial landing-page hierarchy with product-UI precision around installation commands. +- Signature element: a code-native terminal inside an orbital launch motif; no external image or font requests. +- `pages/` is the authored source. Cloudflare Worker pages are generated by `scripts/sync-web-assets.py`. + +## Validated behavior + +- Desktop viewport: 1440×1100, no horizontal overflow. +- Chinese and English mobile viewports: 375×812, no horizontal overflow. +- Brand links and action controls meet the 44px touch target floor. +- Copy buttons use Clipboard API with a legacy fallback and visible success/error states. +- Reduced-motion, visible focus, table overflow, deep-path 404 assets, and light-only color scheme are explicit. + +## Avoid next time + +- Do not reintroduce gradient heroes, emoji headings, equal-weight feature-card grids, or external font/image dependencies. +- Keep decorative pseudo-elements inside their component bounds; an earlier `right: -13px` spotlight label caused mobile document overflow. +- Do not hand-edit `cloudflare/web-assets.js`; regenerate it from `pages/`. +- Do not publish the osome.work mirror without replacing its historical installer/package copies with the current verified Pages set. diff --git a/README.en.md b/README.en.md index 2bf69b2..64d8647 100644 --- a/README.en.md +++ b/README.en.md @@ -98,7 +98,7 @@ ab model # provider manager ## Offline packages -Download a platform-and-Agent-specific verified pack from [Releases](https://github.com/bit-cook/AgentBoot/releases). v1.1.0 initially publishes Linux x64 and Windows x64 Codex packs only after CI installs them, runs `codex --version`, and uninstalls them. Build other Agents on their target platform with menu `[7]` or: +Download a platform-and-Agent-specific verified pack from [Releases](https://github.com/bit-cook/AgentBoot/releases). v1.2.0 initially publishes Linux x64 and Windows x64 Codex packs only after CI installs them, runs `codex --version`, and uninstalls them. Build other Agents on their target platform with menu `[7]` or: ```bash python core/menu.py build-offline win-x64 claude-code,pi diff --git a/README.md b/README.md index f04f1c3..e95508b 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ ab bench # 性能基准 ## 离线安装与瘦身定制 -到 [Releases](https://github.com/bit-cook/AgentBoot/releases) 下载带平台与 Agent 名称的已验证精简包,拷到目标机解压后运行包内 `install-offline.ps1` / `sh install-offline.sh`。v1.1.0 首批发布 Linux x64 / Windows x64 的 Codex 包,发布流水线会实际执行安装、`codex --version` 和卸载后才上传。 +到 [Releases](https://github.com/bit-cook/AgentBoot/releases) 下载带平台与 Agent 名称的已验证精简包,拷到目标机解压后运行包内 `install-offline.ps1` / `sh install-offline.sh`。v1.2.0 首批发布 Linux x64 / Windows x64 的 Codex 包,发布流水线会实际执行安装、`codex --version` 和卸载后才上传。 其他 Agent 使用菜单 `[7]` 或 `build-offline win-x64 claude-code,pi` 在目标平台按需构建。Hermes 含平台相关 Python venv,必须在对应平台构建;Aider 暂不支持离线。 diff --git a/VERSION b/VERSION index 9084fa2..26aaba0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0 +1.2.0 diff --git a/cloudflare/DEPLOY.md b/cloudflare/DEPLOY.md index 543693b..e2b83c0 100644 --- a/cloudflare/DEPLOY.md +++ b/cloudflare/DEPLOY.md @@ -3,6 +3,7 @@ `cloudflare/worker.js` 为 `https://boot.ide.pub` 提供: - `/`、`/en`:中英文产品页; +- `/assets/*`:与 Pages 共用的样式、交互与图标,按版本长期缓存; - `/install.sh`、`/install.ps1`:当前 Release 安装器; - `/rel/`:Release 资产代理,支持 `Range` / `If-Range`; - `/health`:实际探测当前版本安装器、在线包与 SHA-256 旁车。 @@ -30,10 +31,13 @@ sh deploy.sh `deploy.sh` 会执行: ```sh +python3 ../scripts/sync-web-assets.py --check npx wrangler deploy --config wrangler.jsonc python3 ../scripts/verify-live-release.py ``` +网页以 `pages/` 为唯一源。修改主页、404、CSS、JavaScript 或图标后,先在仓库根目录运行 `python3 scripts/sync-web-assets.py`。CI 与部署脚本会用 `--check` 阻止 Worker 和 Pages 内容漂移。 + 只有以下条件全部满足才算发布成功: - `/health` 返回当前 tag 且 `ok=true`; diff --git a/cloudflare/deploy.sh b/cloudflare/deploy.sh index dd25e18..c1be0dc 100755 --- a/cloudflare/deploy.sh +++ b/cloudflare/deploy.sh @@ -4,6 +4,7 @@ set -eu cd "$(dirname "$0")" command -v npx >/dev/null 2>&1 || { echo "需要 Node.js/npm 提供 npx" >&2; exit 1; } +python3 ../scripts/sync-web-assets.py --check echo "==> 使用 Wrangler 部署 Worker boot …" npx --yes wrangler deploy --config wrangler.jsonc echo "==> 验证主入口与镜像 …" diff --git a/cloudflare/web-assets.js b/cloudflare/web-assets.js new file mode 100644 index 0000000..4e75f05 --- /dev/null +++ b/cloudflare/web-assets.js @@ -0,0 +1,2 @@ +// Generated by scripts/sync-web-assets.py; do not edit by hand. +export const WEB_ASSETS = Object.freeze({"/index.html":{"body":"\n\n\n \n \n \n \n AgentBoot · 一条命令,启动你的 AI Agent\n \n \n \n \n \n \n \n\n\n 跳到主要内容\n\n \n\n
\n
\n

v1.2.0 · 免费开源 · 三平台可用

\n

一条命令,
Agent 就位

\n

把安装、模型、镜像、离线包和安全卸载收进一个清楚的菜单。你选择要用的 AI Agent,AgentBoot 负责把它可靠地装好。

\n \n
    \n
  • 14 个 Agent 按需选择
  • \n
  • Linux · macOS · Windows
  • \n
  • 离线安装
  • \n
  • MIT License
  • \n
\n
\n\n
\n \n \n \n
\n
\n \n agentboot\n
\n
\n

$ agentboot

\n

检查系统环境与网络…

\n

选择你想安装的 Agent

\n

校验来源与 SHA-256

\n

安装完成,开始创造。

\n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n

从这里开始

\n

复制一行,马上启动

\n

安装完成后输入 agentboot 打开控制台菜单,或输入 ab 直接与内置 Agent 对话。

\n
\n
\n \n \n \n

安装器仅使用项目控制的 Worker、GitHub Pages 与 GitHub Release,并对安装包执行同源 SHA-256 校验。

\n
\n
\n\n
\n
\n
\n Linux / macOS\n \n
\n curl -fsSL https://boot.ide.pub/install.sh | sh\n
\n\n
\n
\n Windows PowerShell\n \n
\n powershell -NoProfile -ExecutionPolicy Bypass -Command \"iex ((New-Object Net.WebClient).DownloadString('https://boot.ide.pub/install.ps1'))\"\n
\n
\n
\n\n
    \n
  1. 安装 AgentBoot

    上面一条命令约 10 秒完成,只安装 AgentBoot 本体与内置 Agent。

  2. \n
  3. 选择需要的 Agent

    运行 agentboot,在菜单中安装、卸载和配置。

  4. \n
  5. 开始工作

    运行选好的 Agent;也可以直接输入 ab 零配置对话。

  6. \n
\n
\n\n
\n
\n

不仅是安装脚本

复杂留在里面,选择留给你

\n

不塞给你一个全家桶,也不把失败留给你排查。AgentBoot 把最容易出错的环境、网络、来源与卸载边界统一处理。

\n
\n\n
\n
\n

始终可用的起点

\n

即使其他 Agent 装不上,ab 也能马上工作

\n

零第三方依赖 Python 核心,默认接入 Agnes 免费模型,并内置离线 Linux 知识库、会话持久化和高危命令拦截。

\n
\n\n
    \n
  1. 01

    按需安装,不装全家桶

    14 个主流 Agent 自由选择:Claude Code、Codex、Qwen Code、OpenCode、MiMo、Cline、Pi、CoCo 等。

  2. \n
  3. 02

    模型提供商统一管理

    Agnes 零配置可用,也支持命名管理任意 OpenAI 兼容接口、Ollama、LM Studio 与故障切换顺序。

  4. \n
  5. 03

    中国网络自动适配

    自动选择 npm、Node、PyPI 镜像,Worker、Pages、Release 三源容错,并提供一键代理配置。

  6. \n
  7. 04

    离线包经过真实验证

    发布包完成安装、启动、卸载冒烟;菜单 [7] 还能在目标平台按需构建自定义包。

  8. \n
  9. 05

    卸载知道自己该删什么

    菜单 [9]uninstall 只清理由 AgentBoot 管理的程序,默认保留配置、认证和会话。

  10. \n
  11. 06

    性能可以现场测量

    TLS 连接复用、知识库预建索引、上下文自动瘦身和流式中断保护;/bench 基于当前网络与模型实测。

  12. \n
\n
\n
\n\n
\n
\n
\n

你的工具箱

14 个 Agent,一个入口

\n

在线安装按需选择;标记为离线可用的 Agent 可连同依赖与运行时一起打包。

\n
\n\n
\n
\n
14个内置选项
\n 绿点表示支持离线安装\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
#Agent命令厂商离线
01CoCo AgentcocoBitCookLinux/macOS
02OpenCodeopencodeopencode.ai仅在线
03Hermes AgenthermesHermes需 Git
04Cline CLIclineCline支持
05CodeBuddy CLIcodebuddyTencent支持
06Pi Coding AgentpiEarendil Works支持
07Claude CodeclaudeAnthropic支持
08OpenAI Codex CLIcodexOpenAIAgnes 预置
09Qwen CodeqwenAlibabaAgnes 预置
10MiMo CodemimoXiaomi支持
11OpenClawopenclawOpenClaw支持
12Gemini CLIgeminiGoogle支持
13iFlow CLIiflowiFlow 心流支持
14AideraiderAider AI仅在线
\n
\n

Codex 与 Qwen 安装后自动预置 Agnes 免费模型;也可通过菜单 +add-agent 添加自己的 Agent。

\n
\n
\n
\n\n
\n
\n
\n

没有网络,也能安装

\n

可验证离线包,把完整运行环境一起带走

\n

v1.2.0 首批提供 Linux x64 与 Windows x64 Codex 精简包,CI 实际执行安装、版本启动与卸载。

\n
    \n
  • 已验证精简包:从 Releases 下载后直接带到目标机。
  • \n
  • 自建离线包:用菜单 [7]build-offline 在目标平台按需构建。
  • \n
  • 无需额外解压软件:运行包内 install-offline.ps1sh install-offline.sh
  • \n
\n
\n\n
\n

模型由你决定

\n

开箱即用,也完全允许自定义

\n
    \n
  • Agnes 免费模型:ab 零配置直接用,Codex 与 Qwen 自动接线。
  • \n
  • 自定义提供商:任意 OpenAI 兼容接口,命名管理、随时切换。
  • \n
  • 本地模型:内置 Ollama 与 LM Studio 预设,也可添加 vLLM。
  • \n
  • 故障切换:设置备用顺序,主模型失败后自动降级。
  • \n
\n
\n
\n
\n\n
\n
\n
\n

需要更多细节

文档直接说人话

\n

从安装、离线部署到自定义 Agent,按你当前要解决的问题继续阅读。

\n
\n \n
\n
\n\n
\n
\n

少折腾环境,早点开始创造

\n

AgentBoot 是 MIT 许可的开源项目。先用一条命令开始,所有选择依然掌握在你手里。

\n \n
\n
\n
\n\n \n\n\n","type":"text/html; charset=utf-8","etag":"\"91786b20ada44a56\""},"/en/index.html":{"body":"\n\n\n \n \n \n \n AgentBoot · Launch your AI agents with one command\n \n \n \n \n \n \n \n\n\n Skip to main content\n\n \n\n
\n
\n

v1.2.0 · Free and open source · Three platforms

\n

One command.
Agents ready.

\n

Bring installation, models, mirrors, offline packs, and safe uninstall into one clear menu. You choose the AI agents you need; AgentBoot installs them reliably.

\n \n
    \n
  • Choose from 14 agents
  • \n
  • Linux · macOS · Windows
  • \n
  • Offline installation
  • \n
  • MIT License
  • \n
\n
\n\n
\n \n \n \n
\n
\n \n agentboot\n
\n
\n

$ agentboot

\n

Checking system and network…

\n

Choose the agents you want to install

\n

Verify source and SHA-256

\n

Ready. Start building.

\n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n

Start here

\n

Copy one line. Start right away.

\n

After installation, run agentboot to open the console menu, or ab to chat directly with the built-in agent.

\n
\n
\n \n \n \n

The installer uses only project-controlled Worker, GitHub Pages, and GitHub Release sources, then verifies each installation package against a same-origin SHA-256 checksum.

\n
\n
\n\n
\n
\n
\n Linux / macOS\n \n
\n curl -fsSL https://boot.ide.pub/install.sh | sh\n
\n\n
\n
\n Windows PowerShell\n \n
\n powershell -NoProfile -ExecutionPolicy Bypass -Command \"iex ((New-Object Net.WebClient).DownloadString('https://boot.ide.pub/install.ps1'))\"\n
\n
\n
\n\n
    \n
  1. Install AgentBoot

    The command above takes about 10 seconds and installs only AgentBoot itself and the built-in agent.

  2. \n
  3. Choose the agents you need

    Run agentboot to install, uninstall, and configure agents from the menu.

  4. \n
  5. Get to work

    Launch your chosen agent, or run ab for a zero-configuration chat.

  6. \n
\n
\n\n
\n
\n

More than an install script

AgentBoot handles the complexity. You make the choices.

\n

No all-in-one bundle, and no failed setup left for you to debug. AgentBoot handles the parts most likely to break: environments, networks, sources, and uninstall boundaries.

\n
\n\n
\n
\n

A starting point that's always ready

\n

Even if other agents won't install, ab works right away

\n

The built-in agent uses a Python core with no third-party dependencies, the free Agnes model by default, an offline Linux knowledge base, persistent sessions, and safeguards against high-risk commands.

\n
\n\n
    \n
  1. 01

    Install on demand, not as a bundle

    Choose from 14 mainstream agents, including Claude Code, Codex, Qwen Code, OpenCode, MiMo, Cline, Pi, and CoCo.

  2. \n
  3. 02

    Manage model providers in one place

    Agnes works with zero configuration. You can also name and manage any OpenAI-compatible endpoint, Ollama, or LM Studio, and set a failover order.

  4. \n
  5. 03

    Automatically adapt to networks in China

    AgentBoot selects npm, Node, and PyPI mirrors automatically, fails over across Worker, Pages, and Release sources, and configures a proxy in one step.

  6. \n
  7. 04

    Offline packs are tested end to end

    Release packs pass install, launch, and uninstall smoke tests; menu [7] can also build a custom pack on demand on the target platform.

  8. \n
  9. 05

    Uninstall only what AgentBoot owns

    Menu [9] or uninstall removes AgentBoot-managed programs while keeping configuration, credentials, and sessions by default.

  10. \n
  11. 06

    Measure performance where it matters

    AgentBoot reuses TLS connections, prebuilds knowledge-base indexes, trims context automatically, and protects interrupted streams. /bench measures your current network and model.

  12. \n
\n
\n
\n\n
\n
\n
\n

Your toolbox

14 agents. One front door.

\n

Choose agents as needed for online installation. Agents marked as offline-ready can be packaged with their dependencies and runtimes.

\n
\n\n
\n
\n
14built-in choices
\n A green dot indicates support for offline installation\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
#AgentCommandVendorOffline
01CoCo AgentcocoBitCookLinux/macOS
02OpenCodeopencodeopencode.aiOnline only
03Hermes AgenthermesHermesRequires Git
04Cline CLIclineClineSupported
05CodeBuddy CLIcodebuddyTencentSupported
06Pi Coding AgentpiEarendil WorksSupported
07Claude CodeclaudeAnthropicSupported
08OpenAI Codex CLIcodexOpenAIAgnes preset
09Qwen CodeqwenAlibabaAgnes preset
10MiMo CodemimoXiaomiSupported
11OpenClawopenclawOpenClawSupported
12Gemini CLIgeminiGoogleSupported
13iFlow CLIiflowiFlowSupported
14AideraiderAider AIOnline only
\n
\n

Codex and Qwen are automatically configured with the free Agnes model after installation. Add your own agent with menu + or the add-agent command.

\n
\n
\n
\n\n
\n
\n
\n

Install without internet access

\n

Verified offline packs bring the complete runtime with you

\n

v1.2.0 initially provides slim Codex packs for Linux x64 and Windows x64. CI installs each pack, runs its version command, and uninstalls it.

\n
    \n
  • Verified slim packs: download from Releases and carry them to the target machine.
  • \n
  • Custom offline packs: use menu [7] or build-offline on the target platform.
  • \n
  • No extra extraction software: run install-offline.ps1 or sh install-offline.sh inside the pack.
  • \n
\n
\n\n
\n

Your models, your choice

\n

Ready out of the box and fully customizable

\n
    \n
  • Agnes free model: use ab with zero setup; Codex and Qwen are configured automatically.
  • \n
  • Custom providers: name, manage, and switch any OpenAI-compatible endpoint.
  • \n
  • Local models: Ollama and LM Studio presets, with vLLM available as a custom provider.
  • \n
  • Failover: set a fallback order and switch automatically when the primary fails.
  • \n
\n
\n
\n
\n\n
\n
\n
\n

More detail when you need it

Documentation that speaks plainly

\n

Start with the problem in front of you, from installation and offline deployment to custom agents.

\n
\n \n
\n
\n\n
\n
\n

Spend less time wrestling with setup. Start building sooner.

\n

AgentBoot is open source under the MIT License. Start with one command; every choice stays in your hands.

\n \n
\n
\n
\n\n \n\n\n","type":"text/html; charset=utf-8","etag":"\"e308bf93b5165b3d\""},"/404.html":{"body":"\n\n\n \n \n \n \n 页面未找到 · AgentBoot\n \n \n\n\n
\n
\n

404

\n

这条路径没有 Agent,回到启动台吧

\n

地址可能已变更,或只是多输入了一个字符。项目主页、安装命令和文档都还在。

\n \n
\n
\n\n\n","type":"text/html; charset=utf-8","etag":"\"92d469229bf5cde6\""},"/assets/site.css":{"body":":root {\n color-scheme: light;\n --ink-950: #12251d;\n --ink-800: #294238;\n --ink-600: #5b7067;\n --ink-400: #91a198;\n --paper: #fffdf5;\n --surface: #ffffff;\n --soft: #f2f7f2;\n --line: #dce7df;\n --green: #137a52;\n --green-dark: #0d5e3e;\n --green-soft: #e3f5eb;\n --sun: #ffd84d;\n --sun-soft: #fff3b9;\n --coral: #ff7a59;\n --sky: #dff3ff;\n --success: #137a52;\n --warning: #a65e00;\n --error: #b42318;\n --info: #176b87;\n --code: #10251d;\n --code-text: #ecfff4;\n --display: ui-rounded, \"Avenir Next Rounded\", \"SF Pro Rounded\", \"Segoe UI\", sans-serif;\n --body: Inter, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif;\n --mono: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n --ease: cubic-bezier(.2, .8, .2, 1);\n}\n\n* {\n box-sizing: border-box;\n}\n\nhtml {\n overflow-x: clip;\n scroll-behavior: smooth;\n scroll-padding-top: 88px;\n}\n\nbody {\n margin: 0;\n background: var(--paper);\n color: var(--ink-950);\n font-family: var(--body);\n font-size: 16px;\n line-height: 1.68;\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: antialiased;\n}\n\nbutton,\na {\n -webkit-tap-highlight-color: transparent;\n}\n\na {\n color: var(--green-dark);\n text-decoration-thickness: 1.5px;\n text-underline-offset: .2em;\n}\n\na:hover {\n color: var(--green);\n}\n\n:focus-visible {\n outline: 3px solid var(--sun);\n outline-offset: 3px;\n}\n\n.skip-link {\n position: fixed;\n z-index: 100;\n top: 12px;\n left: 12px;\n translate: 0 -150%;\n border: 1px solid var(--ink-950);\n border-radius: 10px;\n background: var(--surface);\n color: var(--ink-950);\n padding: 10px 14px;\n font-weight: 700;\n}\n\n.skip-link:focus {\n translate: 0;\n}\n\n.shell {\n width: min(1180px, calc(100% - 48px));\n margin-inline: auto;\n}\n\n.site-nav {\n position: relative;\n z-index: 10;\n display: flex;\n min-height: 76px;\n align-items: center;\n justify-content: space-between;\n gap: 24px;\n}\n\n.brand {\n display: inline-flex;\n min-height: 44px;\n align-items: center;\n gap: 11px;\n color: var(--ink-950);\n font-family: var(--display);\n font-size: 18px;\n font-weight: 800;\n letter-spacing: -.025em;\n text-decoration: none;\n}\n\n.brand-mark {\n position: relative;\n display: inline-grid;\n width: 34px;\n height: 34px;\n place-items: center;\n overflow: hidden;\n border: 2px solid var(--ink-950);\n border-radius: 11px;\n background: var(--sun);\n}\n\n.brand-mark::before,\n.brand-mark::after {\n content: \"\";\n position: absolute;\n height: 2px;\n border-radius: 999px;\n background: var(--ink-950);\n transform-origin: left center;\n}\n\n.brand-mark::before {\n width: 12px;\n translate: -1px -3px;\n rotate: 38deg;\n}\n\n.brand-mark::after {\n width: 10px;\n translate: 2px 5px;\n}\n\n.nav-links {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.nav-link,\n.language-link {\n display: inline-flex;\n min-height: 44px;\n align-items: center;\n justify-content: center;\n border-radius: 10px;\n color: var(--ink-800);\n padding: 8px 11px;\n font-size: 14px;\n font-weight: 650;\n text-decoration: none;\n}\n\n.nav-link:hover,\n.language-link:hover {\n background: var(--green-soft);\n color: var(--green-dark);\n}\n\n.language-link {\n border: 1px solid var(--line);\n background: rgba(255, 255, 255, .72);\n}\n\n.hero {\n position: relative;\n display: grid;\n min-height: 660px;\n align-items: center;\n grid-template-columns: minmax(0, 7fr) minmax(340px, 5fr);\n gap: clamp(32px, 6vw, 86px);\n padding-block: 72px 112px;\n}\n\n.hero::before {\n content: \"\";\n position: absolute;\n z-index: -2;\n top: 2%;\n right: 0;\n width: min(48vw, 620px);\n aspect-ratio: 1;\n border-radius: 50%;\n background: var(--sun-soft);\n}\n\n.hero::after {\n content: \"\";\n position: absolute;\n z-index: -1;\n bottom: 9%;\n left: 0;\n width: min(30vw, 380px);\n aspect-ratio: 1;\n border-radius: 50%;\n background: var(--sky);\n opacity: .7;\n}\n\n.eyebrow {\n display: inline-flex;\n align-items: center;\n gap: 9px;\n margin: 0 0 22px;\n color: var(--green-dark);\n font-size: 13px;\n font-weight: 750;\n letter-spacing: .08em;\n text-transform: uppercase;\n}\n\n.eyebrow::before {\n content: \"\";\n width: 9px;\n height: 9px;\n border-radius: 50%;\n background: var(--coral);\n box-shadow: 0 0 0 5px rgba(255, 122, 89, .14);\n}\n\nh1,\nh2,\nh3 {\n font-family: var(--display);\n text-wrap: balance;\n}\n\nh1 {\n max-width: 800px;\n margin: 0;\n font-size: clamp(48px, 7.1vw, 88px);\n font-weight: 800;\n letter-spacing: -.06em;\n line-height: .98;\n}\n\n.hero-highlight {\n position: relative;\n display: inline-block;\n white-space: nowrap;\n}\n\n.hero-highlight::after {\n content: \"\";\n position: absolute;\n z-index: -1;\n right: -.08em;\n bottom: .04em;\n left: -.02em;\n height: .24em;\n border-radius: 99px;\n background: var(--sun);\n rotate: -1.5deg;\n}\n\n.hero-copy {\n max-width: 650px;\n margin: 28px 0 0;\n color: var(--ink-600);\n font-size: clamp(17px, 2vw, 21px);\n line-height: 1.62;\n}\n\n.hero-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-top: 34px;\n}\n\n.button {\n display: inline-flex;\n min-height: 48px;\n align-items: center;\n justify-content: center;\n gap: 9px;\n border: 1px solid var(--ink-950);\n border-radius: 12px;\n padding: 11px 17px;\n font-size: 15px;\n font-weight: 750;\n line-height: 1;\n text-decoration: none;\n transition: translate 160ms var(--ease), background-color 160ms var(--ease), color 160ms var(--ease);\n}\n\n.button-primary {\n background: var(--green);\n color: #fff;\n border-color: var(--green);\n}\n\n.button-primary:hover {\n background: var(--green-dark);\n color: #fff;\n translate: 0 -2px;\n}\n\n.button-secondary {\n background: var(--surface);\n color: var(--ink-950);\n}\n\n.button-secondary:hover {\n background: var(--sun-soft);\n color: var(--ink-950);\n translate: 0 -2px;\n}\n\n.button svg,\n.text-link svg {\n width: 18px;\n height: 18px;\n flex: 0 0 auto;\n fill: none;\n stroke: currentColor;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 1.8;\n}\n\n.hero-proof {\n display: flex;\n flex-wrap: wrap;\n gap: 10px 18px;\n margin: 26px 0 0;\n padding: 0;\n color: var(--ink-600);\n font-size: 13px;\n font-weight: 650;\n list-style: none;\n}\n\n.hero-proof li {\n display: flex;\n align-items: center;\n gap: 7px;\n}\n\n.hero-proof li::before {\n content: \"\";\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--green);\n}\n\n.launch-visual {\n position: relative;\n min-height: 450px;\n}\n\n.orbit {\n position: absolute;\n inset: 0;\n margin: auto;\n border: 1px solid rgba(18, 37, 29, .18);\n border-radius: 50%;\n}\n\n.orbit-one {\n width: min(100%, 430px);\n aspect-ratio: 1;\n}\n\n.orbit-two {\n width: min(76%, 330px);\n aspect-ratio: 1;\n border-style: dashed;\n}\n\n.orbit-dot {\n position: absolute;\n top: 10%;\n right: 14%;\n width: 18px;\n height: 18px;\n border: 3px solid var(--paper);\n border-radius: 50%;\n background: var(--coral);\n box-shadow: 0 0 0 1px var(--ink-950);\n}\n\n.terminal {\n position: absolute;\n inset: 50% auto auto 50%;\n width: min(94%, 390px);\n overflow: hidden;\n border: 2px solid var(--ink-950);\n border-radius: 22px;\n background: var(--code);\n box-shadow: 18px 18px 0 var(--sun);\n color: var(--code-text);\n translate: -50% -50%;\n}\n\n.terminal-bar {\n display: flex;\n min-height: 48px;\n align-items: center;\n justify-content: space-between;\n border-bottom: 1px solid rgba(255, 255, 255, .13);\n padding: 0 17px;\n color: #b9d3c4;\n font-family: var(--mono);\n font-size: 12px;\n}\n\n.terminal-dots {\n display: flex;\n gap: 6px;\n}\n\n.terminal-dots i {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--coral);\n}\n\n.terminal-dots i:nth-child(2) {\n background: var(--sun);\n}\n\n.terminal-dots i:nth-child(3) {\n background: #57d49b;\n}\n\n.terminal-body {\n min-height: 238px;\n padding: 26px 22px;\n font-family: var(--mono);\n font-size: clamp(12px, 1.35vw, 14px);\n line-height: 1.8;\n}\n\n.terminal-body p {\n margin: 0 0 12px;\n}\n\n.prompt {\n color: #69e4a7;\n}\n\n.terminal-muted {\n color: #9bb7a8;\n}\n\n.terminal-check {\n color: var(--sun);\n}\n\n.section {\n padding-block: clamp(72px, 10vw, 120px);\n}\n\n.section-soft {\n position: relative;\n background: var(--soft);\n}\n\n.section-soft::before,\n.section-soft::after {\n content: \"\";\n position: absolute;\n right: 0;\n left: 0;\n height: 1px;\n background: var(--line);\n}\n\n.section-soft::before {\n top: 0;\n}\n\n.section-soft::after {\n bottom: 0;\n}\n\n.section-heading {\n display: grid;\n align-items: end;\n grid-template-columns: minmax(0, 4fr) minmax(280px, 3fr);\n gap: 32px;\n margin-bottom: 54px;\n}\n\n.kicker {\n margin: 0 0 12px;\n color: var(--green);\n font-size: 13px;\n font-weight: 750;\n letter-spacing: .08em;\n text-transform: uppercase;\n}\n\nh2 {\n max-width: 700px;\n margin: 0;\n font-size: clamp(34px, 4.8vw, 56px);\n font-weight: 800;\n letter-spacing: -.045em;\n line-height: 1.06;\n}\n\n.section-lede {\n max-width: 56ch;\n margin: 0;\n color: var(--ink-600);\n font-size: 17px;\n}\n\n.install-layout {\n display: grid;\n align-items: start;\n grid-template-columns: minmax(240px, 4fr) minmax(0, 8fr);\n gap: clamp(34px, 7vw, 88px);\n}\n\n.install-intro {\n position: sticky;\n top: 104px;\n}\n\n.install-intro h2 {\n font-size: clamp(34px, 4vw, 48px);\n}\n\n.install-intro p {\n color: var(--ink-600);\n}\n\n.security-note {\n display: grid;\n grid-template-columns: 38px 1fr;\n gap: 13px;\n margin-top: 28px;\n padding-top: 22px;\n border-top: 1px solid var(--line);\n color: var(--ink-600);\n font-size: 14px;\n}\n\n.security-icon {\n display: grid;\n width: 38px;\n height: 38px;\n place-items: center;\n border-radius: 12px;\n background: var(--green-soft);\n color: var(--green-dark);\n}\n\n.security-icon svg {\n width: 21px;\n height: 21px;\n fill: none;\n stroke: currentColor;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 1.8;\n}\n\n.security-note p {\n margin: 0;\n}\n\n.command-stack {\n display: grid;\n gap: 16px;\n}\n\n.command-card {\n overflow: hidden;\n border: 1px solid var(--ink-950);\n border-radius: 20px;\n background: var(--surface);\n}\n\n.command-head {\n display: flex;\n min-height: 56px;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border-bottom: 1px solid var(--line);\n padding: 10px 12px 10px 20px;\n}\n\n.command-platform {\n display: flex;\n align-items: center;\n gap: 11px;\n font-weight: 750;\n}\n\n.platform-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--coral);\n}\n\n.command-card:nth-child(2) .platform-dot {\n background: #2f80ed;\n}\n\n.copy-button {\n display: inline-flex;\n min-width: 90px;\n min-height: 44px;\n align-items: center;\n justify-content: center;\n gap: 7px;\n border: 1px solid var(--line);\n border-radius: 10px;\n background: var(--soft);\n color: var(--ink-950);\n padding: 8px 12px;\n font: 700 13px/1 var(--body);\n cursor: pointer;\n transition: background-color 160ms var(--ease), border-color 160ms var(--ease);\n}\n\n.copy-button:hover {\n border-color: var(--green);\n background: var(--green-soft);\n}\n\n.copy-button[data-state=\"success\"] {\n border-color: var(--green);\n background: var(--green-soft);\n color: var(--green-dark);\n}\n\n.copy-button[data-state=\"error\"] {\n border-color: var(--error);\n background: #fff0ed;\n color: var(--error);\n}\n\n.copy-button svg {\n width: 16px;\n height: 16px;\n fill: none;\n stroke: currentColor;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 1.8;\n}\n\n.command-code {\n display: block;\n margin: 0;\n overflow-x: auto;\n background: var(--code);\n color: var(--code-text);\n padding: 24px 20px;\n font-family: var(--mono);\n font-size: 13px;\n line-height: 1.75;\n white-space: pre;\n scrollbar-color: #557266 var(--code);\n}\n\n.command-code::selection,\n.terminal ::selection {\n background: var(--sun);\n color: var(--ink-950);\n}\n\n.steps {\n display: grid;\n margin: 48px 0 0;\n padding: 0;\n counter-reset: step;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n list-style: none;\n}\n\n.step {\n position: relative;\n min-height: 180px;\n padding: 30px clamp(20px, 3vw, 34px);\n counter-increment: step;\n border-top: 1px solid var(--line);\n border-bottom: 1px solid var(--line);\n}\n\n.step + .step {\n border-left: 1px solid var(--line);\n}\n\n.step::before {\n content: \"0\" counter(step);\n display: block;\n margin-bottom: 30px;\n color: var(--coral);\n font: 750 13px/1 var(--mono);\n}\n\n.step h3 {\n margin: 0 0 8px;\n font-size: 19px;\n font-weight: 750;\n letter-spacing: -.02em;\n}\n\n.step p {\n margin: 0;\n color: var(--ink-600);\n font-size: 14px;\n}\n\n.inline-code,\n.rich-text code,\n.agent-table code,\n.feature-row code {\n border: 1px solid var(--line);\n border-radius: 6px;\n background: var(--surface);\n color: var(--green-dark);\n padding: .12em .42em;\n font-family: var(--mono);\n font-size: .88em;\n}\n\n.feature-layout {\n display: grid;\n align-items: start;\n grid-template-columns: minmax(280px, 5fr) minmax(0, 7fr);\n gap: clamp(40px, 8vw, 100px);\n}\n\n.feature-spotlight {\n position: sticky;\n top: 104px;\n overflow: hidden;\n border: 1px solid var(--ink-950);\n border-radius: 24px;\n background: var(--sun);\n padding: clamp(28px, 4vw, 44px);\n}\n\n.feature-spotlight::after {\n content: \"ab\";\n position: absolute;\n right: 0;\n bottom: -58px;\n color: rgba(18, 37, 29, .09);\n font: 800 clamp(120px, 18vw, 220px)/1 var(--display);\n letter-spacing: -.08em;\n}\n\n.feature-spotlight .kicker {\n color: var(--ink-800);\n}\n\n.feature-spotlight h3 {\n max-width: 470px;\n margin: 0;\n font-size: clamp(30px, 4vw, 48px);\n font-weight: 800;\n letter-spacing: -.045em;\n line-height: 1.08;\n}\n\n.feature-spotlight p {\n position: relative;\n z-index: 1;\n max-width: 48ch;\n margin: 24px 0 0;\n color: var(--ink-800);\n}\n\n.feature-list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.feature-row {\n display: grid;\n padding: 25px 0 28px;\n border-top: 1px solid var(--line);\n grid-template-columns: 44px 1fr;\n gap: 18px;\n}\n\n.feature-row:last-child {\n border-bottom: 1px solid var(--line);\n}\n\n.feature-index {\n color: var(--green);\n font: 750 12px/1.5 var(--mono);\n}\n\n.feature-row h3 {\n margin: -4px 0 7px;\n font-size: 20px;\n font-weight: 750;\n letter-spacing: -.025em;\n}\n\n.feature-row p {\n max-width: 60ch;\n margin: 0;\n color: var(--ink-600);\n font-size: 14px;\n}\n\n.agent-panel {\n overflow: hidden;\n border: 1px solid var(--ink-950);\n border-radius: 24px;\n background: var(--surface);\n}\n\n.agent-panel-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 20px;\n border-bottom: 1px solid var(--line);\n padding: 20px 24px;\n}\n\n.agent-count {\n display: flex;\n align-items: baseline;\n gap: 9px;\n}\n\n.agent-count strong {\n font: 800 38px/1 var(--display);\n letter-spacing: -.04em;\n}\n\n.agent-count span,\n.agent-legend {\n color: var(--ink-600);\n font-size: 13px;\n}\n\n.table-scroll {\n max-width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.agent-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 14px;\n}\n\n.agent-table th,\n.agent-table td {\n min-height: 52px;\n padding: 14px 20px;\n border-bottom: 1px solid var(--line);\n text-align: left;\n vertical-align: middle;\n}\n\n.agent-table th {\n background: var(--soft);\n color: var(--ink-600);\n font-size: 12px;\n font-weight: 750;\n letter-spacing: .04em;\n text-transform: uppercase;\n}\n\n.agent-table tr:last-child td {\n border-bottom: 0;\n}\n\n.agent-table tbody tr:hover {\n background: #fffdf3;\n}\n\n.agent-name {\n font-weight: 750;\n}\n\n.status {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n white-space: nowrap;\n}\n\n.status::before {\n content: \"\";\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--ink-400);\n}\n\n.status-ready {\n color: var(--success);\n font-weight: 650;\n}\n\n.status-ready::before {\n background: var(--success);\n}\n\n.agent-footnote {\n margin: 0;\n border-top: 1px solid var(--line);\n background: var(--sun-soft);\n color: var(--ink-800);\n padding: 16px 22px;\n font-size: 13px;\n}\n\n.detail-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 1px;\n border: 1px solid var(--line);\n background: var(--line);\n}\n\n.detail-block {\n min-height: 320px;\n background: var(--paper);\n padding: clamp(30px, 5vw, 58px);\n}\n\n.detail-block:nth-child(2) {\n background: var(--sky);\n}\n\n.detail-block h3 {\n max-width: 470px;\n margin: 0 0 20px;\n font-size: clamp(27px, 3vw, 38px);\n font-weight: 800;\n letter-spacing: -.04em;\n line-height: 1.1;\n}\n\n.rich-text {\n color: var(--ink-800);\n}\n\n.rich-text p {\n margin: 0 0 16px;\n}\n\n.rich-text ul {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.rich-text li {\n position: relative;\n padding: 11px 0 11px 24px;\n border-top: 1px solid rgba(18, 37, 29, .15);\n}\n\n.rich-text li::before {\n content: \"\";\n position: absolute;\n top: 20px;\n left: 2px;\n width: 8px;\n height: 8px;\n border-radius: 2px;\n background: var(--green);\n rotate: 45deg;\n}\n\n.docs-list {\n display: grid;\n margin: 0;\n padding: 0;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 12px;\n list-style: none;\n}\n\n.docs-link {\n display: flex;\n min-height: 128px;\n flex-direction: column;\n justify-content: space-between;\n gap: 24px;\n border: 1px solid var(--line);\n border-radius: 18px;\n background: var(--surface);\n color: var(--ink-950);\n padding: 22px;\n text-decoration: none;\n transition: border-color 160ms var(--ease), translate 160ms var(--ease);\n}\n\n.docs-link:hover {\n border-color: var(--green);\n color: var(--green-dark);\n translate: 0 -3px;\n}\n\n.docs-link strong {\n font-size: 16px;\n}\n\n.docs-link span {\n color: var(--ink-600);\n font-size: 13px;\n}\n\n.text-link {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n font-weight: 750;\n}\n\n.final-call {\n position: relative;\n overflow: hidden;\n contain: paint;\n border: 1px solid var(--ink-950);\n border-radius: 28px;\n background: var(--green);\n color: #fff;\n padding: clamp(36px, 7vw, 76px);\n}\n\n.final-call::after {\n content: \"\";\n position: absolute;\n right: -80px;\n bottom: -120px;\n width: 320px;\n height: 320px;\n border: 58px solid var(--sun);\n border-radius: 50%;\n opacity: .98;\n}\n\n.final-call h2 {\n position: relative;\n z-index: 1;\n max-width: 700px;\n font-size: clamp(36px, 6vw, 66px);\n}\n\n.final-call p {\n position: relative;\n z-index: 1;\n max-width: 55ch;\n margin: 22px 0 0;\n color: #dff8e9;\n font-size: 17px;\n}\n\n.final-call .hero-actions {\n position: relative;\n z-index: 1;\n}\n\n.final-call .button-primary {\n border-color: var(--sun);\n background: var(--sun);\n color: var(--ink-950);\n}\n\n.final-call .button-primary:hover {\n border-color: #ffe277;\n background: #ffe277;\n color: var(--ink-950);\n}\n\n.final-call .button-secondary {\n border-color: rgba(255, 255, 255, .5);\n background: transparent;\n color: #fff;\n}\n\n.final-call .button-secondary:hover {\n border-color: #fff;\n background: rgba(255, 255, 255, .1);\n color: #fff;\n}\n\n.site-footer {\n padding-block: 34px 44px;\n}\n\n.footer-row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 30px;\n}\n\n.footer-note {\n max-width: 680px;\n margin: 13px 0 0;\n color: var(--ink-600);\n font-size: 13px;\n}\n\n.footer-links {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 6px 18px;\n font-size: 13px;\n font-weight: 650;\n}\n\n.footer-links a {\n min-height: 44px;\n display: inline-flex;\n align-items: center;\n}\n\n.not-found {\n display: grid;\n min-height: 100svh;\n place-items: center;\n padding: 40px 24px;\n}\n\n.not-found-card {\n position: relative;\n width: min(720px, 100%);\n overflow: hidden;\n border: 1px solid var(--ink-950);\n border-radius: 28px;\n background: var(--surface);\n padding: clamp(32px, 8vw, 72px);\n box-shadow: 18px 18px 0 var(--sun);\n}\n\n.not-found-code {\n margin: 0 0 28px;\n color: var(--coral);\n font: 800 clamp(60px, 15vw, 132px)/.8 var(--display);\n letter-spacing: -.07em;\n}\n\n.not-found h1 {\n max-width: 620px;\n font-size: clamp(34px, 7vw, 58px);\n line-height: 1.06;\n}\n\n.not-found p {\n max-width: 50ch;\n margin: 22px 0 0;\n color: var(--ink-600);\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .hero-copy,\n .hero-actions,\n .hero-proof {\n animation: rise-in 620ms var(--ease) both;\n }\n\n .hero-actions {\n animation-delay: 100ms;\n }\n\n .hero-proof {\n animation-delay: 180ms;\n }\n\n .terminal {\n animation: terminal-in 720ms 80ms var(--ease) both;\n }\n\n .orbit-dot {\n animation: pulse 2.8s var(--ease) infinite;\n }\n\n @keyframes rise-in {\n from { opacity: 0; translate: 0 16px; }\n to { opacity: 1; translate: 0; }\n }\n\n @keyframes terminal-in {\n from { opacity: 0; translate: -46% -46%; }\n to { opacity: 1; translate: -50% -50%; }\n }\n\n @keyframes pulse {\n 50% { scale: 1.14; }\n }\n}\n\n@media (max-width: 900px) {\n .hero {\n min-height: auto;\n grid-template-columns: 1fr;\n padding-block: 54px 90px;\n }\n\n .launch-visual {\n min-height: 410px;\n }\n\n .section-heading,\n .install-layout,\n .feature-layout {\n grid-template-columns: 1fr;\n }\n\n .install-intro,\n .feature-spotlight {\n position: static;\n }\n\n .install-intro {\n display: grid;\n align-items: end;\n grid-template-columns: 1fr 1fr;\n gap: 30px;\n }\n\n .security-note {\n margin: 0;\n }\n\n .feature-spotlight {\n min-height: 310px;\n }\n}\n\n@media (max-width: 720px) {\n .shell {\n width: min(100% - 32px, 1180px);\n }\n\n .site-nav {\n min-height: 68px;\n }\n\n .nav-link {\n display: none;\n }\n\n .hero {\n gap: 42px;\n padding-top: 46px;\n }\n\n h1 {\n font-size: clamp(44px, 14vw, 68px);\n }\n\n .hero-copy {\n font-size: 17px;\n }\n\n .launch-visual {\n min-height: 350px;\n }\n\n .orbit-one {\n width: min(94%, 350px);\n }\n\n .orbit-two {\n width: min(72%, 270px);\n }\n\n .terminal {\n box-shadow: 11px 11px 0 var(--sun);\n }\n\n .terminal-body {\n min-height: 210px;\n padding: 22px 18px;\n }\n\n .section-heading {\n align-items: start;\n gap: 18px;\n margin-bottom: 38px;\n }\n\n .install-intro {\n display: block;\n }\n\n .security-note {\n margin-top: 24px;\n }\n\n .steps,\n .detail-grid,\n .docs-list {\n grid-template-columns: 1fr;\n }\n\n .step {\n min-height: auto;\n border-bottom: 0;\n padding: 24px 4px;\n }\n\n .step + .step {\n border-left: 0;\n }\n\n .step:last-child {\n border-bottom: 1px solid var(--line);\n }\n\n .step::before {\n margin-bottom: 16px;\n }\n\n .agent-panel-head,\n .footer-row {\n align-items: flex-start;\n flex-direction: column;\n }\n\n .agent-table th,\n .agent-table td {\n padding-inline: 14px;\n }\n\n .agent-table th:nth-child(4),\n .agent-table td:nth-child(4) {\n display: none;\n }\n\n .footer-links {\n justify-content: flex-start;\n }\n\n .final-call::after {\n right: -140px;\n bottom: -180px;\n }\n}\n\n@media (max-width: 480px) {\n .brand {\n font-size: 16px;\n }\n\n .brand-mark {\n width: 32px;\n height: 32px;\n }\n\n .language-link {\n padding-inline: 10px;\n }\n\n .hero-actions,\n .hero-actions .button {\n width: 100%;\n }\n\n .hero-proof {\n display: grid;\n grid-template-columns: 1fr 1fr;\n }\n\n .launch-visual {\n min-height: 320px;\n }\n\n .terminal {\n border-radius: 17px;\n }\n\n .terminal-bar {\n min-height: 42px;\n }\n\n .terminal-body {\n min-height: 190px;\n font-size: 11px;\n }\n\n .command-head {\n padding-left: 14px;\n }\n\n .copy-button {\n min-width: 82px;\n }\n\n .command-code {\n padding: 19px 14px;\n font-size: 11px;\n }\n\n .feature-spotlight {\n min-height: 330px;\n }\n\n .feature-row {\n grid-template-columns: 34px 1fr;\n gap: 10px;\n }\n\n .agent-table th:first-child,\n .agent-table td:first-child,\n .agent-table th:nth-child(4),\n .agent-table td:nth-child(4) {\n display: none;\n }\n\n .agent-table {\n font-size: 12px;\n }\n\n .agent-table th,\n .agent-table td {\n padding-inline: 10px;\n }\n\n .agent-legend {\n display: none;\n }\n\n .not-found-card {\n box-shadow: 10px 10px 0 var(--sun);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n html {\n scroll-behavior: auto;\n }\n\n *,\n *::before,\n *::after {\n animation-duration: .01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: .01ms !important;\n }\n}\n\n@media print {\n .site-nav,\n .hero-actions,\n .copy-button,\n .final-call,\n .site-footer {\n display: none;\n }\n\n body {\n background: #fff;\n }\n\n .section,\n .hero {\n min-height: auto;\n padding-block: 28px;\n }\n}\n","type":"text/css; charset=utf-8","etag":"\"93e52ab55ce71853\""},"/assets/site.js":{"body":"(function () {\n \"use strict\";\n\n var copyButtons = document.querySelectorAll(\"[data-copy]\");\n\n function legacyCopy(text) {\n var input = document.createElement(\"textarea\");\n input.value = text;\n input.setAttribute(\"readonly\", \"\");\n input.style.position = \"fixed\";\n input.style.opacity = \"0\";\n document.body.appendChild(input);\n input.select();\n var copied = document.execCommand(\"copy\");\n input.remove();\n return copied ? Promise.resolve() : Promise.reject(new Error(\"copy failed\"));\n }\n\n function copyText(text) {\n if (navigator.clipboard && window.isSecureContext) {\n return navigator.clipboard.writeText(text);\n }\n return legacyCopy(text);\n }\n\n copyButtons.forEach(function (button) {\n var idleLabel = button.getAttribute(\"data-label-idle\") || \"Copy\";\n var successLabel = button.getAttribute(\"data-label-success\") || \"Copied\";\n var errorLabel = button.getAttribute(\"data-label-error\") || \"Select command\";\n var label = button.querySelector(\"[data-copy-label]\");\n var timer;\n\n button.addEventListener(\"click\", function () {\n window.clearTimeout(timer);\n copyText(button.getAttribute(\"data-copy\") || \"\").then(function () {\n button.setAttribute(\"data-state\", \"success\");\n label.textContent = successLabel;\n }).catch(function () {\n button.setAttribute(\"data-state\", \"error\");\n label.textContent = errorLabel;\n }).finally(function () {\n timer = window.setTimeout(function () {\n button.removeAttribute(\"data-state\");\n label.textContent = idleLabel;\n }, 1800);\n });\n });\n });\n}());\n","type":"text/javascript; charset=utf-8","etag":"\"e32ad7eff5bfef9a\""},"/assets/favicon.svg":{"body":"\n \n \n\n","type":"image/svg+xml; charset=utf-8","etag":"\"97f7d8aaef171778\""}}); diff --git a/cloudflare/worker.js b/cloudflare/worker.js index a66de95..e514c85 100644 --- a/cloudflare/worker.js +++ b/cloudflare/worker.js @@ -1,21 +1,15 @@ /** - * AgentBoot 分发 Worker(Cloudflare Workers 免费版) - * ================================================= - * 绑定:Workers 名称为 boot;自定义域 boot.ide.pub(路由 boot.ide.pub/*) - * 端点: - * GET / 中文说明页 - * GET /install.sh → 转发 GitHub Release 的 install.sh(curl|sh 入口) - * GET /install.ps1 → 转发 GitHub Release 的 install.ps1 - * GET /rel/<文件名> → 转发 GitHub Release 资产(在线/离线安装包) - * GET /gh/main.tar.gz → 转发 codeload 主分支源码包 - * GET /health → 健康检查 - * 说明:*.workers.dev 在中国大陆通常不可直接访问,生产入口请使用 - * 绑定的自定义域 boot.ide.pub(Cloudflare 任播网络)。 + * AgentBoot distribution Worker. + * Pages are generated from pages/ by scripts/sync-web-assets.py. */ +import { WEB_ASSETS } from "./web-assets.js"; + const REPO = "bit-cook/AgentBoot"; -const TAG = "v1.1.0"; +const TAG = "v1.2.0"; const GH_REL = `https://github.com/${REPO}/releases/download/${TAG}`; +const ASSET_CACHE = "public, max-age=31536000, immutable"; +const PAGE_CACHE = "public, max-age=300, stale-while-revalidate=3600"; export default { async fetch(request) { @@ -30,7 +24,7 @@ export default { await Promise.all(required.map(async (name) => { try { const response = await fetch(`${GH_REL}/${name}`, { - method: "HEAD", headers: { "User-Agent": "AgentBoot-Worker/1.1" }, + method: "HEAD", headers: { "User-Agent": "AgentBoot-Worker/1.2" }, cf: { cacheEverything: false }, }); assets[name] = response.status; @@ -57,15 +51,17 @@ export default { : `https://codeload.github.com/${REPO}/tar.gz/refs/heads/main`; return proxy(target, ext === "zip" ? "application/zip" : "application/x-gzip", 300); } - if (path === "/") return page("zh"); - if (path === "/en") return page("en"); - - return text("AgentBoot Worker · 404 Not Found\n试:/install.sh 或 /rel/\n", 404); + if (path === "/") return webResponse(request, "/index.html", PAGE_CACHE); + if (path === "/en") return webResponse(request, "/en/index.html", PAGE_CACHE); + if (path === "/assets/site.css" || path === "/assets/site.js" || path === "/assets/favicon.svg") { + return webResponse(request, path, ASSET_CACHE); + } + return webResponse(request, "/404.html", "no-store", 404); }, }; async function proxy(target, contentType, cacheTtl, incoming = null) { - const requestHeaders = new Headers({ "User-Agent": "AgentBoot-Worker/1.1" }); + const requestHeaders = new Headers({ "User-Agent": "AgentBoot-Worker/1.2" }); if (incoming) { for (const name of ["Range", "If-Range", "If-None-Match", "If-Modified-Since"]) { const value = incoming.headers.get(name); @@ -86,6 +82,28 @@ async function proxy(target, contentType, cacheTtl, incoming = null) { return new Response(resp.body, { status: resp.status, headers }); } +function webResponse(request, route, cacheControl, status = 200) { + const asset = WEB_ASSETS[route]; + if (request.headers.get("If-None-Match") === asset.etag) { + return new Response(null, { status: 304, headers: webHeaders(asset, cacheControl) }); + } + return new Response(request.method === "HEAD" ? null : asset.body, { + status, + headers: webHeaders(asset, cacheControl), + }); +} + +function webHeaders(asset, cacheControl) { + return { + "Cache-Control": cacheControl, + "Content-Type": asset.type, + "ETag": asset.etag, + "Referrer-Policy": "strict-origin-when-cross-origin", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "DENY", + }; +} + function text(body, status = 200) { return new Response(body, { status, @@ -99,339 +117,3 @@ function json(obj, status = 200) { headers: { "Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*" }, }); } - -const PAGE_ZH = ` - - - - -AgentBoot · 一键安装 AI Agent 启动器 - - - - -
- -
-

AgentBoot

-

极简 · 极速 · 开箱即用的 AI Agent 启动器 —— 一条命令装好,主流 Agent 菜单自选

-
- v1.1.0Linux · macOS · Windows界面中文 - 14 个 Agent 自选离线安装中国镜像自适应MIT -
-

中文 | English

-
- -

⚡ 一键安装

-

Linux / macOS

-
curl -fsSL https://boot.ide.pub/install.sh | sh -
-

Windows(PowerShell)

-
powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object Net.WebClient).DownloadString('https://boot.ide.pub/install.ps1'))" -
-
安装器只使用项目控制的 Worker / GitHub Pages / GitHub Release 三源,并对包与同源 SHA-256 一起校验。
- -

🚀 三步上手

-
    -
  1. 安装 AgentBoot
    上面一条命令,10 秒完成;只会装 AgentBoot 本体与内置 Agent。
  2. -
  3. 打开控制台菜单
    agentboot —— 环境体检 / 安装与卸载 Agent / 模型配置 / 镜像代理 / 自定义离线包。
  4. -
  5. 或直接对话
    ab —— 内置保底 Agent,默认 Agnes 免费模型,零配置开箱即用。
  6. -
- -

✨ 特性

-
-
📦 菜单自选安装(不是全家桶)

14 个主流 Agent 按需勾选:Claude Code、Codex、Qwen Code、OpenCode、CodeBuddy、MiMo、Cline、Pi、CoCo…

-
🛟 内置保底 Agent(ab)

其他都装不上时它一定能用:零第三方依赖 Python 核心、Agnes 免费模型、离线 Linux 知识库、会话持久化。

-
🧠 模型提供商管理器

Agnes 零配置开箱;自定义提供商命名管理;Ollama / LM Studio 本地模型;故障切换顺序。

-
🇨🇳 中国网络自适应

自动切换 npm / Node / PyPI 镜像;Worker / Pages / Release 三源容错;代理一键配置。

-
📴 已验证离线 & 按需构建

Release 精简包通过真实安装、启动与卸载冒烟;菜单 [7] 可在目标平台自选 Agent 构建。

-
🧹 可追溯安全卸载

菜单 [9] 或 uninstall 命令批量卸载;只清理由 AgentBoot 管理的程序,默认保留配置、认证与会话。

-
⚡ 可测性能

TLS 连接复用、知识库预建索引、上下文自动瘦身与流式中断保护;/bench 按当前网络与模型现场测量。

-
- -

🤖 支持的 Agent(14 个)

-
- - - - - - - - - - - - - - - -
#Agent命令厂商离线
1CoCo AgentcocoBitCookLinux/macOS
2OpenCodeopencodeopencode.ai仅在线
3Hermes AgenthermesHermes✓(需 Git)
4Cline CLIclineCline
5CodeBuddy CLIcodebuddyTencent
6Pi Coding AgentpiEarendil Works
7Claude CodeclaudeAnthropic
8OpenAI Codex CLIcodexOpenAI✓(Agnes 预置)
9Qwen CodeqwenAlibaba✓(Agnes 预置)
10MiMo CodemimoXiaomi
11OpenClawopenclawOpenClaw
12Gemini CLIgeminiGoogle
13iFlow CLIiflowiFlow 心流
14AideraiderAider AI仅在线(pip)
-
✓ = 支持离线安装(离线包内置完整依赖与运行时)。Codex / Qwen 安装后自动预置 Agnes 免费模型;自定义 Agent 可通过菜单 +add-agent 命令添加。
- -

📴 离线安装

-

无网机器?两条路:

-
    -
  • 已验证精简包:v1.1.0 首批提供 Linux x64 / Windows x64 的 Codex 包,CI 实际执行安装、版本启动与卸载;
  • -
  • 自建离线包:菜单 [7]build-offline 在目标平台自选 Agent;Hermes 必须在对应平台构建。
  • -
-

目标机无需联网、无需解压软件(Windows 资源管理器 / 系统自带 tar / 自解压脚本三选一),运行包内 install-offline.ps1sh install-offline.sh 即可。

- -

🧠 模型:开箱即用 + 完全自定义

-
    -
  • Agnes 免费模型(官方预设):ab 零配置直接用;Codex / Qwen 安装后自动接线;
  • -
  • 自定义提供商:任意 OpenAI 兼容接口,命名管理、随时增删切换;
  • -
  • 本地模型:内置 Ollama / LM Studio 预设,也可把 vLLM 添加为自定义 OpenAI 兼容提供商;
  • -
  • 故障切换:设置备用顺序,主模型失败自动降级。
  • -
- -

📚 文档

- - - - - - -`; - -const PAGE_EN = ` - - - - -AgentBoot · One-command AI Agent Launcher - - - - -
- -
-

English | 中文

-

AgentBoot

-

Minimal, fast, ready-to-run AI Agent launcher — one command to install, mainstream agents to choose from.

-
- v1.1.0Linux · macOS · WindowsChinese UI default - 14 agentsOffline installChina-network adaptiveMIT -
-
- -

⚡ One-command install

-

Linux / macOS

-
curl -fsSL https://boot.ide.pub/install.sh | sh -
-

Windows (PowerShell)

-
powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object Net.WebClient).DownloadString('https://boot.ide.pub/install.ps1'))" -
-
The installer uses project-controlled Worker / GitHub Pages / GitHub Release origins and verifies each archive against its same-origin SHA-256.
- -

🚀 Three steps

-
    -
  1. Install AgentBoot
    One command, ~10 seconds; only AgentBoot itself and the built-in agent are installed.
  2. -
  3. Open the console menu
    agentboot — environment check / install or uninstall agents / model providers / mirrors & proxy / custom offline builds.
  4. -
  5. Or just chat
    ab — the built-in fallback agent with the free Agnes model, zero config.
  6. -
- -

✨ Features

-
-
📦 Choose what to install (no bundle bloat)

14 mainstream agents to pick from: Claude Code, Codex, Qwen Code, OpenCode, CodeBuddy, MiMo, Cline, Pi, CoCo…

-
🛟 Built-in fallback agent (ab)

A zero-third-party-dependency Python core with Agnes, an offline Linux knowledge base, and session persistence.

-
🧠 Model provider manager

Agnes out of the box; named custom providers; Ollama / LM Studio local models; failover order.

-
🇨🇳 China network adaptive

npm / Node / PyPI mirrors, Worker/Pages/Release fallback, and one-click proxy configuration.

-
📴 Verified offline & custom builds

Release packs pass real install/run/uninstall smoke tests; menu [7] builds selected Agents on their target platform.

-
🧹 Ownership-aware uninstall

Menu [9] or the uninstall command removes AgentBoot-managed programs in batches while preserving config, credentials, and sessions by default.

-
⚡ Measurable performance

TLS reuse, pre-indexed KB, context trimming, and stream protection; /bench measures the current network and provider.

-
- -

🤖 Supported agents (14)

-
- - - - - - - - - - - - - - - -
#AgentCommandVendorOffline
1CoCo AgentcocoBitCookLinux/macOS
2OpenCodeopencodeopencode.aionline only
3Hermes AgenthermesHermes✓ (needs Git)
4Cline CLIclineCline
5CodeBuddy CLIcodebuddyTencent
6Pi Coding AgentpiEarendil Works
7Claude CodeclaudeAnthropic
8OpenAI Codex CLIcodexOpenAI✓ (Agnes preset)
9Qwen CodeqwenAlibaba✓ (Agnes preset)
10MiMo CodemimoXiaomi
11OpenClawopenclawOpenClaw
12Gemini CLIgeminiGoogle
13iFlow CLIiflowiFlow
14AideraiderAider AIonline only (pip)
-
✓ = offline install supported (dependencies and runtimes bundled). Codex / Qwen are auto-wired to the free Agnes model after install. Add your own agents via menu + or the add-agent command.
- -

📴 Offline install

-

No internet on the target machine? Two options:

-
    -
  • Verified slim packs: v1.1.0 initially provides Linux x64 / Windows x64 Codex packs after CI installs, starts, and uninstalls them;
  • -
  • Custom packs: menu [7] or build-offline on the target platform; Hermes must be built natively.
  • -
-

No unzip software needed (Windows Explorer / system tar / self-extracting script), then run install-offline.ps1 or sh install-offline.sh.

- -

🧠 Models: works out of the box + fully customizable

-
    -
  • Agnes free model (official preset): ab uses it with zero config; Codex / Qwen are auto-wired;
  • -
  • Custom providers: any OpenAI-compatible endpoint, named and managed;
  • -
  • Local models: Ollama / LM Studio presets; add vLLM as a custom OpenAI-compatible provider;
  • -
  • Failover: set a fallback order and the primary failure switches automatically.
  • -
- -

📚 Docs

- - - - - - -`; - -function page(lang) { - const html = lang === "en" ? PAGE_EN : PAGE_ZH; - return new Response(html, { headers: { "Content-Type": "text/html; charset=utf-8" } }); -} diff --git a/core/agent.py b/core/agent.py index bc68f99..9b88a7e 100644 --- a/core/agent.py +++ b/core/agent.py @@ -27,7 +27,7 @@ try: VERSION = open(os.path.join(APP_DIR, "VERSION"), "r", encoding="ascii").read().strip() except OSError: - VERSION = "1.1.0" + VERSION = "1.2.0" AB_HOME = os.environ.get("AGENTBOOT_HOME") or os.path.join(os.path.expanduser("~"), ".agentboot") CONFIG_PATH = os.path.join(AB_HOME, "config.json") KB_DIR = os.path.join(APP_DIR, "tools", "linux-kb") @@ -214,11 +214,21 @@ def _validate_model_transport(scheme, host, api_key): # 连接池:复用 TLS 连接,砍掉每轮对话的握手开销(极限性能核心) _POOL = {} +_SSL_CONTEXTS = {} + + +def _ssl_context(): + import ssl + insecure = os.environ.get("AGENTBOOT_INSECURE") == "1" + context = _SSL_CONTEXTS.get(insecure) + if context is None: + context = ssl._create_unverified_context() if insecure else ssl.create_default_context() + _SSL_CONTEXTS[insecure] = context + return context def _connect(scheme, host, port, timeout=180): import http.client - import ssl from urllib.parse import urlsplit proxy_url = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy") if not proxy_url: @@ -236,9 +246,7 @@ def _connect(scheme, host, port, timeout=180): if conn is not None: return conn if scheme == "https": - ctx = ssl.create_default_context() - if os.environ.get("AGENTBOOT_INSECURE") == "1": - ctx = ssl._create_unverified_context() + ctx = _ssl_context() if proxy: conn = http.client.HTTPSConnection(proxy.hostname, proxy.port or (443 if proxy.scheme == "https" else 80), timeout=timeout, context=ctx) @@ -295,7 +303,7 @@ def chat(cfg, messages, stream_cb=None, tools=None, max_tokens=None, temperature if p.get("api_key"): headers["Authorization"] = "Bearer " + p["api_key"] - payload = json.dumps(body, ensure_ascii=False).encode("utf-8") + payload = json.dumps(body, ensure_ascii=False, separators=(",", ":")).encode("utf-8") last_err = None for attempt in range(3): @@ -455,7 +463,7 @@ def _ttfb(cfg, prompt="回复:1"): headers = {"Content-Type": "application/json", "Connection": "keep-alive"} if p.get("api_key"): headers["Authorization"] = "Bearer " + p["api_key"] - payload = json.dumps(body, ensure_ascii=False).encode("utf-8") + payload = json.dumps(body, ensure_ascii=False, separators=(",", ":")).encode("utf-8") t0 = time.perf_counter() try: conn = _connect(scheme, host, port) diff --git a/core/launch.py b/core/launch.py new file mode 100644 index 0000000..3006c81 --- /dev/null +++ b/core/launch.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +"""Tiny installed-command dispatcher; importing enables Python bytecode caching.""" + +import sys + + +def main(): + target = sys.argv[1] if len(sys.argv) > 1 else "menu" + sys.argv = [sys.argv[0]] + sys.argv[2:] + if target == "agent": + import agent + agent.main() + elif target == "menu": + import menu + menu.main() + else: + raise SystemExit("unknown AgentBoot launcher target: %s" % target) + + +if __name__ == "__main__": + main() diff --git a/core/menu.py b/core/menu.py index f267d82..026a0b0 100644 --- a/core/menu.py +++ b/core/menu.py @@ -17,7 +17,6 @@ python menu.py mirror auto|off|cn|proxy http://127.0.0.1:7890 """ import json -import hashlib from contextlib import contextmanager import os import re @@ -26,10 +25,8 @@ import subprocess import sys import platform -import tempfile import time import urllib.parse -import urllib.request sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import agent # noqa: E402 复用配置与模型能力 @@ -324,15 +321,15 @@ def node_exe(): return os.path.join(d, "node.exe") if not POSIX else os.path.join(d, "bin", "node") -def npm_cmd(minimum=None): +def npm_cmd(minimum=None, version_cache=None): """优先系统 npm,其次运行时自带 npm。""" n = shutil.which("npm") system_node = shutil.which("node") - if n and node_ok(system_node, minimum): + if n and node_ok(system_node, minimum, version_cache): return n d = runtime_node_dir() cand = os.path.join(d, "npm.cmd") if not POSIX else os.path.join(d, "bin", "npm") - return cand if os.path.exists(cand) and node_ok(node_exe(), minimum) else None + return cand if os.path.exists(cand) and node_ok(node_exe(), minimum, version_cache) else None def child_env(): @@ -376,14 +373,19 @@ def _version_satisfies(current, requirement): return False -def node_ok(path=None, minimum=None): +def node_ok(path=None, minimum=None, version_cache=None): try: executable = path or shutil.which("node") or node_exe() - out = subprocess.run([executable, "--version"], - capture_output=True, text=True, timeout=10) - if out.returncode == 0: + current = version_cache.get(executable) if version_cache is not None else None + if current is None: + out = subprocess.run([executable, "--version"], + capture_output=True, text=True, timeout=10) + if out.returncode != 0: + return False current = _version_tuple(out.stdout.strip()) - return bool(current and _version_satisfies(current, minimum or ">=18")) + if current and version_cache is not None: + version_cache[executable] = current + return bool(current and _version_satisfies(current, minimum or ">=18")) except Exception: pass return False @@ -391,6 +393,8 @@ def node_ok(path=None, minimum=None): def ensure_node(minimum=None): """确保 Node 满足 Agent 最低版本;不足时部署便携运行时。""" + import hashlib + from urllib import request as urllib_request sysnode = shutil.which("node") if sysnode and node_ok(sysnode, minimum): return sysnode @@ -421,8 +425,8 @@ def ensure_node(minimum=None): for u in urls: try: log_info("下载 Node 运行时:%s" % u) - req = urllib.request.Request(u, headers={"User-Agent": "AgentBoot/1.0"}) - with urllib.request.urlopen(req, timeout=60) as r, open(archive, "wb") as f: + req = urllib_request.Request(u, headers={"User-Agent": "AgentBoot/1.0"}) + with urllib_request.urlopen(req, timeout=60) as r, open(archive, "wb") as f: while True: chunk = r.read(1 << 20) if not chunk: @@ -437,8 +441,8 @@ def ensure_node(minimum=None): try: if not os.path.exists(sums_path): sums_url = "%s/%s/SHASUMS256.txt" % (NODE_MIRROR_GLOBAL, NODE_VERSION) - req = urllib.request.Request(sums_url, headers={"User-Agent": "AgentBoot/1.0"}) - with urllib.request.urlopen(req, timeout=60) as response, open(sums_path, "wb") as output: + req = urllib_request.Request(sums_url, headers={"User-Agent": "AgentBoot/1.0"}) + with urllib_request.urlopen(req, timeout=60) as response, open(sums_path, "wb") as output: shutil.copyfileobj(response, output) expected = None with open(sums_path, "r", encoding="ascii") as sums: @@ -757,24 +761,35 @@ def uninstall_agents(ids, purge=False): # ---------------------------------------------------------------- 在线安装 -def npm_install(pkg, minimum=None): - npm = npm_cmd(minimum) +def npm_install(pkg, minimum=None, context=None): + packages = [pkg] if isinstance(pkg, str) else list(pkg) + if not packages: + return True + context = context if context is not None else {} + versions = context.setdefault("node_versions", {}) + npm = npm_cmd(minimum, versions) if not npm: got = ensure_node(minimum) if not got: log_err("需要 Node.js %s(无法自动部署,请检查网络)" % (minimum or ">=18")) return False - npm = npm_cmd(minimum) + versions.clear() + npm = npm_cmd(minimum, versions) if not npm: log_err("Node 已就绪但未找到匹配的 npm") return False ensure_npm_prefix() - cmd = [npm, "install", "-g", pkg, "--prefix", NPM_PREFIX, - "--no-audit", "--no-fund"] - if cn_mode(): + cmd = [npm, "install", "-g"] + packages + ["--prefix", NPM_PREFIX, + "--no-audit", "--no-fund", "--prefer-offline", + "--progress=false", "--loglevel=error"] + if "cn" not in context: + context["cn"] = cn_mode() + if context["cn"]: cmd += ["--registry", NPM_MIRROR] log_info("$ %s" % " ".join(cmd)) - r = subprocess.run(cmd, env=child_env()) + if "env" not in context: + context["env"] = child_env() + r = subprocess.run(cmd, env=context["env"]) return r.returncode == 0 @@ -782,6 +797,7 @@ def install_online(ids): agents = load_registry() by_id = {a["id"]: a for a in agents} ok_list, fail_list = [], [] + npm_context, ready = {}, [] for aid in ids: a = by_id.get(aid) if not a: @@ -800,6 +816,25 @@ def install_online(ids): log_err(t("menu.install_os_limit") % (a["name"], "/".join(os_limits), plat_id())) fail_list.append(aid) continue + ready.append(a) + + npm_groups = {} + for a in ready: + if a.get("method", "npm") == "npm" and not a.get("special_install"): + npm_groups.setdefault(a.get("node") or ">=18", []).append(a) + batched = set() + for minimum, group in npm_groups.items(): + if len(group) < 2: + continue + packages = [a["npm"] for a in group] + log_info("批量安装 %d 个 npm Agent(共用依赖解析与连接)" % len(group)) + if npm_install(packages, minimum, npm_context): + batched.update(a["id"] for a in group) + else: + log_info("批量安装失败,回退逐个安装以定位问题") + + for a in ready: + aid = a["id"] method = a.get("method", "npm") ok = False if a.get("special_install") == "hermes": @@ -808,7 +843,7 @@ def install_online(ids): if not ok: log_err("专用流程失败:可检查 Git 是否安装、或配置代理后重试") elif method == "npm": - ok = npm_install(a["npm"], a.get("node")) + ok = aid in batched or npm_install(a["npm"], a.get("node"), npm_context) elif method == "script": ok = install_via_script(a) elif method == "pip": @@ -880,11 +915,13 @@ def install_via_script(a): def _download_script(url, suffix): - request = urllib.request.Request(url, headers={"User-Agent": "AgentBoot/1.0"}) + import tempfile + from urllib import request as urllib_request + request = urllib_request.Request(url, headers={"User-Agent": "AgentBoot/1.0"}) fd, path = tempfile.mkstemp(prefix="agentboot-script-", suffix=suffix) try: total = 0 - with os.fdopen(fd, "wb") as output, urllib.request.urlopen(request, timeout=60) as response: + with os.fdopen(fd, "wb") as output, urllib_request.urlopen(request, timeout=60) as response: final_url = urllib.parse.urlsplit(response.geturl()) if final_url.scheme.lower() != "https": raise ValueError("安装脚本重定向到了非 HTTPS 地址") @@ -1020,6 +1057,7 @@ def _download_mirror(urls, dest): def _seed_uv(pkg_root): """预置 uv 二进制到 /.uv_bin 并写入 marker,绕过 postinstall 的 GitHub 直连下载。""" + import hashlib import tarfile import zipfile version, asset_info, sha = _hermes_uv_meta(pkg_root) @@ -1098,7 +1136,8 @@ def install_hermes_special(a): return False ensure_npm_prefix() cmd = [npm, "install", "--ignore-scripts", "-g", a["npm"], - "--prefix", NPM_PREFIX, "--no-audit", "--no-fund"] + "--prefix", NPM_PREFIX, "--no-audit", "--no-fund", "--prefer-offline", + "--progress=false", "--loglevel=error"] if cn_mode(): cmd += ["--registry", NPM_MIRROR] log_info("$ %s" % " ".join(cmd)) @@ -2044,8 +2083,10 @@ def menu_mirror(): def resolve_lang(): """语言解析:环境变量 AGENTBOOT_LANG 优先,其次配置文件,默认中文。""" - lang = os.environ.get("AGENTBOOT_LANG") or agent.load_config().get("lang") or "zh" + cfg = agent.load_config() + lang = os.environ.get("AGENTBOOT_LANG") or cfg.get("lang") or "zh" i18n.set_lang(lang) + return cfg def set_lang_persist(lang, cfg=None): @@ -2079,9 +2120,8 @@ def banner(): print(i18n.t("menu.tagline") + "\n") -def main_menu(): - resolve_lang() - cfg = agent.load_config() +def main_menu(cfg=None): + cfg = cfg if isinstance(cfg, dict) else resolve_lang() agents = load_registry() while True: banner() @@ -2132,11 +2172,11 @@ def main_menu(): def main(): - resolve_lang() + cfg = resolve_lang() agent._utf8_console() argv = sys.argv[1:] if not argv: - main_menu() + main_menu(cfg) return cmd = argv[0] if cmd == "lang": @@ -2244,7 +2284,7 @@ def main(): elif cmd in ("version", "--version"): print("AgentBoot 控制台 v%s" % VERSION) elif cmd == "menu": - main_menu() + main_menu(cfg) else: print(__doc__) diff --git a/docs/en/install-guide.md b/docs/en/install-guide.md index 62263bc..1ebb748 100644 --- a/docs/en/install-guide.md +++ b/docs/en/install-guide.md @@ -1,6 +1,6 @@ # AgentBoot Installation Guide -> **v1.1.0** · Linux / macOS / Windows · CLI UI in Chinese by default (switchable to English: `agentboot lang en`) +> **v1.2.0** · Linux / macOS / Windows · CLI UI in Chinese by default (switchable to English: `agentboot lang en`) > AgentBoot is a minimal, fast, ready-to-run AI Agent launcher: it ships a built-in fallback agent (free **Agnes** model by default), while the other agents (Claude Code, Codex, Qwen Code, OpenCode, CodeBuddy, MiMo Code, Cline, Pi, CoCo, …) are installed **from a menu of your choice**. **目录 / Table of contents**: [One-command online install](#one-command-online-install) · [One-command offline install](#one-command-offline-install) · [Custom agents](#custom-agents-beyond-the-registry) · [Custom offline packages](#custom-offline-packages-slim) · [China network](#china-network-adaptive) · [Model providers](#model-providers) · [Built-in agent](#built-in-agent-ab) · [Upgrade & uninstall](#upgrade--uninstall) · [Troubleshooting](#online-install-troubleshooting) · [FAQ](#faq) @@ -43,9 +43,9 @@ Three steps: **① get an offline package → ② copy & extract → ③ run the ### Step 1 — Get an offline package - **Option A**: download from [Releases](https://github.com/bit-cook/AgentBoot/releases) - - `AgentBoot-offline-v1.1.0-win-x64-codex.zip` (Windows x64) - - `AgentBoot-offline-v1.1.0-linux-x64-codex.tar.gz` (Linux x64) - - `AgentBoot-offline-v1.1.0-linux-x64-codex-sfx.sh` (Linux x64 self-extracting file) + - `AgentBoot-offline-v1.2.0-win-x64-codex.zip` (Windows x64) + - `AgentBoot-offline-v1.2.0-linux-x64-codex.tar.gz` (Linux x64) + - `AgentBoot-offline-v1.2.0-linux-x64-codex-sfx.sh` (Linux x64 self-extracting file) - **Option B — build your own slim package** (recommended; see [Custom offline packages](#custom-offline-packages-slim)): pick platforms and agents, output goes to `dist/`. @@ -54,9 +54,9 @@ Three steps: **① get an offline package → ② copy & extract → ③ run the | Target machine | How | |---|---| | Windows (any) | Right-click the ZIP → Extract All (built into Explorer) | -| Windows 10/11 (CLI) | `tar -xf AgentBoot-offline-v1.1.0-win-x64-codex.zip` | -| Linux x64 | `tar -xzf AgentBoot-offline-v1.1.0-linux-x64-codex.tar.gz -C ~` | -| Minimal Linux x64 | `sh AgentBoot-offline-v1.1.0-linux-x64-codex-sfx.sh` | +| Windows 10/11 (CLI) | `tar -xf AgentBoot-offline-v1.2.0-win-x64-codex.zip` | +| Linux x64 | `tar -xzf AgentBoot-offline-v1.2.0-linux-x64-codex.tar.gz -C ~` | +| Minimal Linux x64 | `sh AgentBoot-offline-v1.2.0-linux-x64-codex-sfx.sh` | ### Step 3 — Run the offline installer diff --git "a/docs/zh/\345\256\211\350\243\205\346\214\207\345\215\227.md" "b/docs/zh/\345\256\211\350\243\205\346\214\207\345\215\227.md" index 2a2fbf5..9f9537f 100644 --- "a/docs/zh/\345\256\211\350\243\205\346\214\207\345\215\227.md" +++ "b/docs/zh/\345\256\211\350\243\205\346\214\207\345\215\227.md" @@ -1,6 +1,6 @@ # AgentBoot 安装指南 -> **版本 v1.1.0** · 支持 Linux / macOS / Windows · 界面默认中文(切换:`agentboot lang en`) +> **版本 v1.2.0** · 支持 Linux / macOS / Windows · 界面默认中文(切换:`agentboot lang en`) > 语言 / Language: 中文(本页) | [English](../en/install-guide.md) | [根目录中文指南](../../安装指南.md) > AgentBoot 是一个极简、极速、开箱即用的 AI Agent 启动器:内置保底 Agent,其余 Agent 通过菜单自选安装。离线能力以注册表和目标平台构建验证为准;Aider 与 OpenCode 当前仅在线安装,Hermes 必须在目标平台原生构建。 @@ -61,9 +61,9 @@ curl -fsSL https://bit-cook.github.io/AgentBoot/install.sh | sh - **方式 A(推荐)**:到 GitHub Releases 下载现成的离线包 `https://github.com/bit-cook/AgentBoot/releases` → 选择 - - `AgentBoot-offline-v1.1.0-win-x64-codex.zip`(Windows x64) - - `AgentBoot-offline-v1.1.0-linux-x64-codex.tar.gz`(Linux x64) - - `AgentBoot-offline-v1.1.0-linux-x64-codex-sfx.sh`(Linux x64 自解压单文件) + - `AgentBoot-offline-v1.2.0-win-x64-codex.zip`(Windows x64) + - `AgentBoot-offline-v1.2.0-linux-x64-codex.tar.gz`(Linux x64) + - `AgentBoot-offline-v1.2.0-linux-x64-codex-sfx.sh`(Linux x64 自解压单文件) - **方式 B(自建)**:在有网的 Linux/macOS 或 Windows 机器上运行项目自带的构建脚本, 自动从 npmmirror 镜像拉取全部 Agent 离线载荷并打包: @@ -83,9 +83,9 @@ curl -fsSL https://bit-cook.github.io/AgentBoot/install.sh | sh | 目标机器情况 | 解压方法 | |---|---| | **Windows(任意版本)** | 直接**右键 ZIP → 全部解压缩**。Windows 资源管理器自带 ZIP 支持,无需安装任何软件 | -| **Windows 10/11(命令行党)** | 系统自带 `tar`:`tar -xf AgentBoot-offline-v1.1.0-win-x64-codex.zip` | -| **Linux x64** | 系统自带 `tar`:`tar -xzf AgentBoot-offline-v1.1.0-linux-x64-codex.tar.gz -C ~` | -| **Linux x64 极简环境** | 用自解压单文件:`sh AgentBoot-offline-v1.1.0-linux-x64-codex-sfx.sh` | +| **Windows 10/11(命令行党)** | 系统自带 `tar`:`tar -xf AgentBoot-offline-v1.2.0-win-x64-codex.zip` | +| **Linux x64** | 系统自带 `tar`:`tar -xzf AgentBoot-offline-v1.2.0-linux-x64-codex.tar.gz -C ~` | +| **Linux x64 极简环境** | 用自解压单文件:`sh AgentBoot-offline-v1.2.0-linux-x64-codex-sfx.sh` | > 自解压包 `*-sfx.sh` 本身就是一个 shell 脚本 + 内嵌 base64 数据,`sh xxx-sfx.sh` 即自动解压并直接进入安装流程,非常适合只有最小化系统的服务器。 diff --git a/install.sh b/install.sh index 5727b0f..1c86fbe 100755 --- a/install.sh +++ b/install.sh @@ -9,7 +9,7 @@ set -eu REPO="bit-cook/AgentBoot" -TAG="v1.1.0" +TAG="v1.2.0" TARBALL="agentboot-online-${TAG}.tar.gz" BOOT_BASE="https://boot.ide.pub" GH="https://github.com/${REPO}/releases/download/${TAG}" @@ -156,7 +156,7 @@ OLD_APP="${AB_ROOT}/app.old.$$" rm -rf "$NEW_APP" "$OLD_APP" mkdir -p "$NEW_APP" cp -R "${SRC_DIR}/." "$NEW_APP/" -if [ ! -f "$NEW_APP/core/menu.py" ] || [ ! -f "$NEW_APP/core/agent.py" ]; then +if [ ! -f "$NEW_APP/core/menu.py" ] || [ ! -f "$NEW_APP/core/agent.py" ] || [ ! -f "$NEW_APP/core/launch.py" ]; then err "安装包结构无效,保留现有版本" rm -rf "$NEW_APP" exit 1 @@ -182,13 +182,13 @@ cat > "$agentboot_tmp" < "$ab_tmp" </dev/null 2>&1 || true "$PY" "${APP_DIR}/core/agent.py" doctor 2>/dev/null || true # ---------- 7. 完成 ---------- diff --git a/pages/404.html b/pages/404.html new file mode 100644 index 0000000..874843f --- /dev/null +++ b/pages/404.html @@ -0,0 +1,25 @@ + + + + + + + + 页面未找到 · AgentBoot + + + + +
+
+ +

这条路径没有 Agent,回到启动台吧

+

地址可能已变更,或只是多输入了一个字符。项目主页、安装命令和文档都还在。

+ +
+
+ + diff --git a/pages/agentboot-online-v1.1.0.tar.gz b/pages/agentboot-online-v1.1.0.tar.gz deleted file mode 100644 index 27a2f93..0000000 Binary files a/pages/agentboot-online-v1.1.0.tar.gz and /dev/null differ diff --git a/pages/agentboot-online-v1.1.0.tar.gz.sha256 b/pages/agentboot-online-v1.1.0.tar.gz.sha256 deleted file mode 100644 index af8f13b..0000000 --- a/pages/agentboot-online-v1.1.0.tar.gz.sha256 +++ /dev/null @@ -1 +0,0 @@ -a597f71008061bfb8c71ceae1934e08e1dac638f02219c19eb0822950db1320e agentboot-online-v1.1.0.tar.gz diff --git a/pages/agentboot-online-v1.1.0.zip b/pages/agentboot-online-v1.1.0.zip deleted file mode 100644 index f656f16..0000000 Binary files a/pages/agentboot-online-v1.1.0.zip and /dev/null differ diff --git a/pages/agentboot-online-v1.1.0.zip.sha256 b/pages/agentboot-online-v1.1.0.zip.sha256 deleted file mode 100644 index e9b8874..0000000 --- a/pages/agentboot-online-v1.1.0.zip.sha256 +++ /dev/null @@ -1 +0,0 @@ -83d19577916aed010dd8780a9aab2c94157e07693be2aa4fc5a5fd3fa5856d06 agentboot-online-v1.1.0.zip diff --git a/pages/agentboot-online-v1.2.0.tar.gz.sha256 b/pages/agentboot-online-v1.2.0.tar.gz.sha256 new file mode 100644 index 0000000..fbb9bed --- /dev/null +++ b/pages/agentboot-online-v1.2.0.tar.gz.sha256 @@ -0,0 +1 @@ +5a6ac9b3193dd72859f8b5dab90dccaeb44a03819ee0810ab41ff15867d6f582 agentboot-online-v1.2.0.tar.gz diff --git a/pages/agentboot-online-v1.2.0.zip.sha256 b/pages/agentboot-online-v1.2.0.zip.sha256 new file mode 100644 index 0000000..fb548c1 --- /dev/null +++ b/pages/agentboot-online-v1.2.0.zip.sha256 @@ -0,0 +1 @@ +bae9d13ec1cab8f3612978e15bc3a315e7873ea53c940e9f1d7da601dcf5031f agentboot-online-v1.2.0.zip diff --git a/pages/assets/favicon.svg b/pages/assets/favicon.svg new file mode 100644 index 0000000..4919996 --- /dev/null +++ b/pages/assets/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pages/assets/site.css b/pages/assets/site.css new file mode 100644 index 0000000..e76326e --- /dev/null +++ b/pages/assets/site.css @@ -0,0 +1,1487 @@ +:root { + color-scheme: light; + --ink-950: #12251d; + --ink-800: #294238; + --ink-600: #5b7067; + --ink-400: #91a198; + --paper: #fffdf5; + --surface: #ffffff; + --soft: #f2f7f2; + --line: #dce7df; + --green: #137a52; + --green-dark: #0d5e3e; + --green-soft: #e3f5eb; + --sun: #ffd84d; + --sun-soft: #fff3b9; + --coral: #ff7a59; + --sky: #dff3ff; + --success: #137a52; + --warning: #a65e00; + --error: #b42318; + --info: #176b87; + --code: #10251d; + --code-text: #ecfff4; + --display: ui-rounded, "Avenir Next Rounded", "SF Pro Rounded", "Segoe UI", sans-serif; + --body: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; + --mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace; + --ease: cubic-bezier(.2, .8, .2, 1); +} + +* { + box-sizing: border-box; +} + +html { + overflow-x: clip; + scroll-behavior: smooth; + scroll-padding-top: 88px; +} + +body { + margin: 0; + background: var(--paper); + color: var(--ink-950); + font-family: var(--body); + font-size: 16px; + line-height: 1.68; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; +} + +button, +a { + -webkit-tap-highlight-color: transparent; +} + +a { + color: var(--green-dark); + text-decoration-thickness: 1.5px; + text-underline-offset: .2em; +} + +a:hover { + color: var(--green); +} + +:focus-visible { + outline: 3px solid var(--sun); + outline-offset: 3px; +} + +.skip-link { + position: fixed; + z-index: 100; + top: 12px; + left: 12px; + translate: 0 -150%; + border: 1px solid var(--ink-950); + border-radius: 10px; + background: var(--surface); + color: var(--ink-950); + padding: 10px 14px; + font-weight: 700; +} + +.skip-link:focus { + translate: 0; +} + +.shell { + width: min(1180px, calc(100% - 48px)); + margin-inline: auto; +} + +.site-nav { + position: relative; + z-index: 10; + display: flex; + min-height: 76px; + align-items: center; + justify-content: space-between; + gap: 24px; +} + +.brand { + display: inline-flex; + min-height: 44px; + align-items: center; + gap: 11px; + color: var(--ink-950); + font-family: var(--display); + font-size: 18px; + font-weight: 800; + letter-spacing: -.025em; + text-decoration: none; +} + +.brand-mark { + position: relative; + display: inline-grid; + width: 34px; + height: 34px; + place-items: center; + overflow: hidden; + border: 2px solid var(--ink-950); + border-radius: 11px; + background: var(--sun); +} + +.brand-mark::before, +.brand-mark::after { + content: ""; + position: absolute; + height: 2px; + border-radius: 999px; + background: var(--ink-950); + transform-origin: left center; +} + +.brand-mark::before { + width: 12px; + translate: -1px -3px; + rotate: 38deg; +} + +.brand-mark::after { + width: 10px; + translate: 2px 5px; +} + +.nav-links { + display: flex; + align-items: center; + gap: 6px; +} + +.nav-link, +.language-link { + display: inline-flex; + min-height: 44px; + align-items: center; + justify-content: center; + border-radius: 10px; + color: var(--ink-800); + padding: 8px 11px; + font-size: 14px; + font-weight: 650; + text-decoration: none; +} + +.nav-link:hover, +.language-link:hover { + background: var(--green-soft); + color: var(--green-dark); +} + +.language-link { + border: 1px solid var(--line); + background: rgba(255, 255, 255, .72); +} + +.hero { + position: relative; + display: grid; + min-height: 660px; + align-items: center; + grid-template-columns: minmax(0, 7fr) minmax(340px, 5fr); + gap: clamp(32px, 6vw, 86px); + padding-block: 72px 112px; +} + +.hero::before { + content: ""; + position: absolute; + z-index: -2; + top: 2%; + right: 0; + width: min(48vw, 620px); + aspect-ratio: 1; + border-radius: 50%; + background: var(--sun-soft); +} + +.hero::after { + content: ""; + position: absolute; + z-index: -1; + bottom: 9%; + left: 0; + width: min(30vw, 380px); + aspect-ratio: 1; + border-radius: 50%; + background: var(--sky); + opacity: .7; +} + +.eyebrow { + display: inline-flex; + align-items: center; + gap: 9px; + margin: 0 0 22px; + color: var(--green-dark); + font-size: 13px; + font-weight: 750; + letter-spacing: .08em; + text-transform: uppercase; +} + +.eyebrow::before { + content: ""; + width: 9px; + height: 9px; + border-radius: 50%; + background: var(--coral); + box-shadow: 0 0 0 5px rgba(255, 122, 89, .14); +} + +h1, +h2, +h3 { + font-family: var(--display); + text-wrap: balance; +} + +h1 { + max-width: 800px; + margin: 0; + font-size: clamp(48px, 7.1vw, 88px); + font-weight: 800; + letter-spacing: -.06em; + line-height: .98; +} + +.hero-highlight { + position: relative; + display: inline-block; + white-space: nowrap; +} + +.hero-highlight::after { + content: ""; + position: absolute; + z-index: -1; + right: -.08em; + bottom: .04em; + left: -.02em; + height: .24em; + border-radius: 99px; + background: var(--sun); + rotate: -1.5deg; +} + +.hero-copy { + max-width: 650px; + margin: 28px 0 0; + color: var(--ink-600); + font-size: clamp(17px, 2vw, 21px); + line-height: 1.62; +} + +.hero-actions { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-top: 34px; +} + +.button { + display: inline-flex; + min-height: 48px; + align-items: center; + justify-content: center; + gap: 9px; + border: 1px solid var(--ink-950); + border-radius: 12px; + padding: 11px 17px; + font-size: 15px; + font-weight: 750; + line-height: 1; + text-decoration: none; + transition: translate 160ms var(--ease), background-color 160ms var(--ease), color 160ms var(--ease); +} + +.button-primary { + background: var(--green); + color: #fff; + border-color: var(--green); +} + +.button-primary:hover { + background: var(--green-dark); + color: #fff; + translate: 0 -2px; +} + +.button-secondary { + background: var(--surface); + color: var(--ink-950); +} + +.button-secondary:hover { + background: var(--sun-soft); + color: var(--ink-950); + translate: 0 -2px; +} + +.button svg, +.text-link svg { + width: 18px; + height: 18px; + flex: 0 0 auto; + fill: none; + stroke: currentColor; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 1.8; +} + +.hero-proof { + display: flex; + flex-wrap: wrap; + gap: 10px 18px; + margin: 26px 0 0; + padding: 0; + color: var(--ink-600); + font-size: 13px; + font-weight: 650; + list-style: none; +} + +.hero-proof li { + display: flex; + align-items: center; + gap: 7px; +} + +.hero-proof li::before { + content: ""; + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--green); +} + +.launch-visual { + position: relative; + min-height: 450px; +} + +.orbit { + position: absolute; + inset: 0; + margin: auto; + border: 1px solid rgba(18, 37, 29, .18); + border-radius: 50%; +} + +.orbit-one { + width: min(100%, 430px); + aspect-ratio: 1; +} + +.orbit-two { + width: min(76%, 330px); + aspect-ratio: 1; + border-style: dashed; +} + +.orbit-dot { + position: absolute; + top: 10%; + right: 14%; + width: 18px; + height: 18px; + border: 3px solid var(--paper); + border-radius: 50%; + background: var(--coral); + box-shadow: 0 0 0 1px var(--ink-950); +} + +.terminal { + position: absolute; + inset: 50% auto auto 50%; + width: min(94%, 390px); + overflow: hidden; + border: 2px solid var(--ink-950); + border-radius: 22px; + background: var(--code); + box-shadow: 18px 18px 0 var(--sun); + color: var(--code-text); + translate: -50% -50%; +} + +.terminal-bar { + display: flex; + min-height: 48px; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid rgba(255, 255, 255, .13); + padding: 0 17px; + color: #b9d3c4; + font-family: var(--mono); + font-size: 12px; +} + +.terminal-dots { + display: flex; + gap: 6px; +} + +.terminal-dots i { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--coral); +} + +.terminal-dots i:nth-child(2) { + background: var(--sun); +} + +.terminal-dots i:nth-child(3) { + background: #57d49b; +} + +.terminal-body { + min-height: 238px; + padding: 26px 22px; + font-family: var(--mono); + font-size: clamp(12px, 1.35vw, 14px); + line-height: 1.8; +} + +.terminal-body p { + margin: 0 0 12px; +} + +.prompt { + color: #69e4a7; +} + +.terminal-muted { + color: #9bb7a8; +} + +.terminal-check { + color: var(--sun); +} + +.section { + padding-block: clamp(72px, 10vw, 120px); +} + +.section-soft { + position: relative; + background: var(--soft); +} + +.section-soft::before, +.section-soft::after { + content: ""; + position: absolute; + right: 0; + left: 0; + height: 1px; + background: var(--line); +} + +.section-soft::before { + top: 0; +} + +.section-soft::after { + bottom: 0; +} + +.section-heading { + display: grid; + align-items: end; + grid-template-columns: minmax(0, 4fr) minmax(280px, 3fr); + gap: 32px; + margin-bottom: 54px; +} + +.kicker { + margin: 0 0 12px; + color: var(--green); + font-size: 13px; + font-weight: 750; + letter-spacing: .08em; + text-transform: uppercase; +} + +h2 { + max-width: 700px; + margin: 0; + font-size: clamp(34px, 4.8vw, 56px); + font-weight: 800; + letter-spacing: -.045em; + line-height: 1.06; +} + +.section-lede { + max-width: 56ch; + margin: 0; + color: var(--ink-600); + font-size: 17px; +} + +.install-layout { + display: grid; + align-items: start; + grid-template-columns: minmax(240px, 4fr) minmax(0, 8fr); + gap: clamp(34px, 7vw, 88px); +} + +.install-intro { + position: sticky; + top: 104px; +} + +.install-intro h2 { + font-size: clamp(34px, 4vw, 48px); +} + +.install-intro p { + color: var(--ink-600); +} + +.security-note { + display: grid; + grid-template-columns: 38px 1fr; + gap: 13px; + margin-top: 28px; + padding-top: 22px; + border-top: 1px solid var(--line); + color: var(--ink-600); + font-size: 14px; +} + +.security-icon { + display: grid; + width: 38px; + height: 38px; + place-items: center; + border-radius: 12px; + background: var(--green-soft); + color: var(--green-dark); +} + +.security-icon svg { + width: 21px; + height: 21px; + fill: none; + stroke: currentColor; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 1.8; +} + +.security-note p { + margin: 0; +} + +.command-stack { + display: grid; + gap: 16px; +} + +.command-card { + overflow: hidden; + border: 1px solid var(--ink-950); + border-radius: 20px; + background: var(--surface); +} + +.command-head { + display: flex; + min-height: 56px; + align-items: center; + justify-content: space-between; + gap: 12px; + border-bottom: 1px solid var(--line); + padding: 10px 12px 10px 20px; +} + +.command-platform { + display: flex; + align-items: center; + gap: 11px; + font-weight: 750; +} + +.platform-dot { + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--coral); +} + +.command-card:nth-child(2) .platform-dot { + background: #2f80ed; +} + +.copy-button { + display: inline-flex; + min-width: 90px; + min-height: 44px; + align-items: center; + justify-content: center; + gap: 7px; + border: 1px solid var(--line); + border-radius: 10px; + background: var(--soft); + color: var(--ink-950); + padding: 8px 12px; + font: 700 13px/1 var(--body); + cursor: pointer; + transition: background-color 160ms var(--ease), border-color 160ms var(--ease); +} + +.copy-button:hover { + border-color: var(--green); + background: var(--green-soft); +} + +.copy-button[data-state="success"] { + border-color: var(--green); + background: var(--green-soft); + color: var(--green-dark); +} + +.copy-button[data-state="error"] { + border-color: var(--error); + background: #fff0ed; + color: var(--error); +} + +.copy-button svg { + width: 16px; + height: 16px; + fill: none; + stroke: currentColor; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 1.8; +} + +.command-code { + display: block; + margin: 0; + overflow-x: auto; + background: var(--code); + color: var(--code-text); + padding: 24px 20px; + font-family: var(--mono); + font-size: 13px; + line-height: 1.75; + white-space: pre; + scrollbar-color: #557266 var(--code); +} + +.command-code::selection, +.terminal ::selection { + background: var(--sun); + color: var(--ink-950); +} + +.steps { + display: grid; + margin: 48px 0 0; + padding: 0; + counter-reset: step; + grid-template-columns: repeat(3, minmax(0, 1fr)); + list-style: none; +} + +.step { + position: relative; + min-height: 180px; + padding: 30px clamp(20px, 3vw, 34px); + counter-increment: step; + border-top: 1px solid var(--line); + border-bottom: 1px solid var(--line); +} + +.step + .step { + border-left: 1px solid var(--line); +} + +.step::before { + content: "0" counter(step); + display: block; + margin-bottom: 30px; + color: var(--coral); + font: 750 13px/1 var(--mono); +} + +.step h3 { + margin: 0 0 8px; + font-size: 19px; + font-weight: 750; + letter-spacing: -.02em; +} + +.step p { + margin: 0; + color: var(--ink-600); + font-size: 14px; +} + +.inline-code, +.rich-text code, +.agent-table code, +.feature-row code { + border: 1px solid var(--line); + border-radius: 6px; + background: var(--surface); + color: var(--green-dark); + padding: .12em .42em; + font-family: var(--mono); + font-size: .88em; +} + +.feature-layout { + display: grid; + align-items: start; + grid-template-columns: minmax(280px, 5fr) minmax(0, 7fr); + gap: clamp(40px, 8vw, 100px); +} + +.feature-spotlight { + position: sticky; + top: 104px; + overflow: hidden; + border: 1px solid var(--ink-950); + border-radius: 24px; + background: var(--sun); + padding: clamp(28px, 4vw, 44px); +} + +.feature-spotlight::after { + content: "ab"; + position: absolute; + right: 0; + bottom: -58px; + color: rgba(18, 37, 29, .09); + font: 800 clamp(120px, 18vw, 220px)/1 var(--display); + letter-spacing: -.08em; +} + +.feature-spotlight .kicker { + color: var(--ink-800); +} + +.feature-spotlight h3 { + max-width: 470px; + margin: 0; + font-size: clamp(30px, 4vw, 48px); + font-weight: 800; + letter-spacing: -.045em; + line-height: 1.08; +} + +.feature-spotlight p { + position: relative; + z-index: 1; + max-width: 48ch; + margin: 24px 0 0; + color: var(--ink-800); +} + +.feature-list { + margin: 0; + padding: 0; + list-style: none; +} + +.feature-row { + display: grid; + padding: 25px 0 28px; + border-top: 1px solid var(--line); + grid-template-columns: 44px 1fr; + gap: 18px; +} + +.feature-row:last-child { + border-bottom: 1px solid var(--line); +} + +.feature-index { + color: var(--green); + font: 750 12px/1.5 var(--mono); +} + +.feature-row h3 { + margin: -4px 0 7px; + font-size: 20px; + font-weight: 750; + letter-spacing: -.025em; +} + +.feature-row p { + max-width: 60ch; + margin: 0; + color: var(--ink-600); + font-size: 14px; +} + +.agent-panel { + overflow: hidden; + border: 1px solid var(--ink-950); + border-radius: 24px; + background: var(--surface); +} + +.agent-panel-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + border-bottom: 1px solid var(--line); + padding: 20px 24px; +} + +.agent-count { + display: flex; + align-items: baseline; + gap: 9px; +} + +.agent-count strong { + font: 800 38px/1 var(--display); + letter-spacing: -.04em; +} + +.agent-count span, +.agent-legend { + color: var(--ink-600); + font-size: 13px; +} + +.table-scroll { + max-width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + +.agent-table { + width: 100%; + border-collapse: collapse; + font-size: 14px; +} + +.agent-table th, +.agent-table td { + min-height: 52px; + padding: 14px 20px; + border-bottom: 1px solid var(--line); + text-align: left; + vertical-align: middle; +} + +.agent-table th { + background: var(--soft); + color: var(--ink-600); + font-size: 12px; + font-weight: 750; + letter-spacing: .04em; + text-transform: uppercase; +} + +.agent-table tr:last-child td { + border-bottom: 0; +} + +.agent-table tbody tr:hover { + background: #fffdf3; +} + +.agent-name { + font-weight: 750; +} + +.status { + display: inline-flex; + align-items: center; + gap: 7px; + white-space: nowrap; +} + +.status::before { + content: ""; + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--ink-400); +} + +.status-ready { + color: var(--success); + font-weight: 650; +} + +.status-ready::before { + background: var(--success); +} + +.agent-footnote { + margin: 0; + border-top: 1px solid var(--line); + background: var(--sun-soft); + color: var(--ink-800); + padding: 16px 22px; + font-size: 13px; +} + +.detail-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1px; + border: 1px solid var(--line); + background: var(--line); +} + +.detail-block { + min-height: 320px; + background: var(--paper); + padding: clamp(30px, 5vw, 58px); +} + +.detail-block:nth-child(2) { + background: var(--sky); +} + +.detail-block h3 { + max-width: 470px; + margin: 0 0 20px; + font-size: clamp(27px, 3vw, 38px); + font-weight: 800; + letter-spacing: -.04em; + line-height: 1.1; +} + +.rich-text { + color: var(--ink-800); +} + +.rich-text p { + margin: 0 0 16px; +} + +.rich-text ul { + margin: 0; + padding: 0; + list-style: none; +} + +.rich-text li { + position: relative; + padding: 11px 0 11px 24px; + border-top: 1px solid rgba(18, 37, 29, .15); +} + +.rich-text li::before { + content: ""; + position: absolute; + top: 20px; + left: 2px; + width: 8px; + height: 8px; + border-radius: 2px; + background: var(--green); + rotate: 45deg; +} + +.docs-list { + display: grid; + margin: 0; + padding: 0; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + list-style: none; +} + +.docs-link { + display: flex; + min-height: 128px; + flex-direction: column; + justify-content: space-between; + gap: 24px; + border: 1px solid var(--line); + border-radius: 18px; + background: var(--surface); + color: var(--ink-950); + padding: 22px; + text-decoration: none; + transition: border-color 160ms var(--ease), translate 160ms var(--ease); +} + +.docs-link:hover { + border-color: var(--green); + color: var(--green-dark); + translate: 0 -3px; +} + +.docs-link strong { + font-size: 16px; +} + +.docs-link span { + color: var(--ink-600); + font-size: 13px; +} + +.text-link { + display: inline-flex; + align-items: center; + gap: 7px; + font-weight: 750; +} + +.final-call { + position: relative; + overflow: hidden; + contain: paint; + border: 1px solid var(--ink-950); + border-radius: 28px; + background: var(--green); + color: #fff; + padding: clamp(36px, 7vw, 76px); +} + +.final-call::after { + content: ""; + position: absolute; + right: -80px; + bottom: -120px; + width: 320px; + height: 320px; + border: 58px solid var(--sun); + border-radius: 50%; + opacity: .98; +} + +.final-call h2 { + position: relative; + z-index: 1; + max-width: 700px; + font-size: clamp(36px, 6vw, 66px); +} + +.final-call p { + position: relative; + z-index: 1; + max-width: 55ch; + margin: 22px 0 0; + color: #dff8e9; + font-size: 17px; +} + +.final-call .hero-actions { + position: relative; + z-index: 1; +} + +.final-call .button-primary { + border-color: var(--sun); + background: var(--sun); + color: var(--ink-950); +} + +.final-call .button-primary:hover { + border-color: #ffe277; + background: #ffe277; + color: var(--ink-950); +} + +.final-call .button-secondary { + border-color: rgba(255, 255, 255, .5); + background: transparent; + color: #fff; +} + +.final-call .button-secondary:hover { + border-color: #fff; + background: rgba(255, 255, 255, .1); + color: #fff; +} + +.site-footer { + padding-block: 34px 44px; +} + +.footer-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 30px; +} + +.footer-note { + max-width: 680px; + margin: 13px 0 0; + color: var(--ink-600); + font-size: 13px; +} + +.footer-links { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 6px 18px; + font-size: 13px; + font-weight: 650; +} + +.footer-links a { + min-height: 44px; + display: inline-flex; + align-items: center; +} + +.not-found { + display: grid; + min-height: 100svh; + place-items: center; + padding: 40px 24px; +} + +.not-found-card { + position: relative; + width: min(720px, 100%); + overflow: hidden; + border: 1px solid var(--ink-950); + border-radius: 28px; + background: var(--surface); + padding: clamp(32px, 8vw, 72px); + box-shadow: 18px 18px 0 var(--sun); +} + +.not-found-code { + margin: 0 0 28px; + color: var(--coral); + font: 800 clamp(60px, 15vw, 132px)/.8 var(--display); + letter-spacing: -.07em; +} + +.not-found h1 { + max-width: 620px; + font-size: clamp(34px, 7vw, 58px); + line-height: 1.06; +} + +.not-found p { + max-width: 50ch; + margin: 22px 0 0; + color: var(--ink-600); +} + +@media (prefers-reduced-motion: no-preference) { + .hero-copy, + .hero-actions, + .hero-proof { + animation: rise-in 620ms var(--ease) both; + } + + .hero-actions { + animation-delay: 100ms; + } + + .hero-proof { + animation-delay: 180ms; + } + + .terminal { + animation: terminal-in 720ms 80ms var(--ease) both; + } + + .orbit-dot { + animation: pulse 2.8s var(--ease) infinite; + } + + @keyframes rise-in { + from { opacity: 0; translate: 0 16px; } + to { opacity: 1; translate: 0; } + } + + @keyframes terminal-in { + from { opacity: 0; translate: -46% -46%; } + to { opacity: 1; translate: -50% -50%; } + } + + @keyframes pulse { + 50% { scale: 1.14; } + } +} + +@media (max-width: 900px) { + .hero { + min-height: auto; + grid-template-columns: 1fr; + padding-block: 54px 90px; + } + + .launch-visual { + min-height: 410px; + } + + .section-heading, + .install-layout, + .feature-layout { + grid-template-columns: 1fr; + } + + .install-intro, + .feature-spotlight { + position: static; + } + + .install-intro { + display: grid; + align-items: end; + grid-template-columns: 1fr 1fr; + gap: 30px; + } + + .security-note { + margin: 0; + } + + .feature-spotlight { + min-height: 310px; + } +} + +@media (max-width: 720px) { + .shell { + width: min(100% - 32px, 1180px); + } + + .site-nav { + min-height: 68px; + } + + .nav-link { + display: none; + } + + .hero { + gap: 42px; + padding-top: 46px; + } + + h1 { + font-size: clamp(44px, 14vw, 68px); + } + + .hero-copy { + font-size: 17px; + } + + .launch-visual { + min-height: 350px; + } + + .orbit-one { + width: min(94%, 350px); + } + + .orbit-two { + width: min(72%, 270px); + } + + .terminal { + box-shadow: 11px 11px 0 var(--sun); + } + + .terminal-body { + min-height: 210px; + padding: 22px 18px; + } + + .section-heading { + align-items: start; + gap: 18px; + margin-bottom: 38px; + } + + .install-intro { + display: block; + } + + .security-note { + margin-top: 24px; + } + + .steps, + .detail-grid, + .docs-list { + grid-template-columns: 1fr; + } + + .step { + min-height: auto; + border-bottom: 0; + padding: 24px 4px; + } + + .step + .step { + border-left: 0; + } + + .step:last-child { + border-bottom: 1px solid var(--line); + } + + .step::before { + margin-bottom: 16px; + } + + .agent-panel-head, + .footer-row { + align-items: flex-start; + flex-direction: column; + } + + .agent-table th, + .agent-table td { + padding-inline: 14px; + } + + .agent-table th:nth-child(4), + .agent-table td:nth-child(4) { + display: none; + } + + .footer-links { + justify-content: flex-start; + } + + .final-call::after { + right: -140px; + bottom: -180px; + } +} + +@media (max-width: 480px) { + .brand { + font-size: 16px; + } + + .brand-mark { + width: 32px; + height: 32px; + } + + .language-link { + padding-inline: 10px; + } + + .hero-actions, + .hero-actions .button { + width: 100%; + } + + .hero-proof { + display: grid; + grid-template-columns: 1fr 1fr; + } + + .launch-visual { + min-height: 320px; + } + + .terminal { + border-radius: 17px; + } + + .terminal-bar { + min-height: 42px; + } + + .terminal-body { + min-height: 190px; + font-size: 11px; + } + + .command-head { + padding-left: 14px; + } + + .copy-button { + min-width: 82px; + } + + .command-code { + padding: 19px 14px; + font-size: 11px; + } + + .feature-spotlight { + min-height: 330px; + } + + .feature-row { + grid-template-columns: 34px 1fr; + gap: 10px; + } + + .agent-table th:first-child, + .agent-table td:first-child, + .agent-table th:nth-child(4), + .agent-table td:nth-child(4) { + display: none; + } + + .agent-table { + font-size: 12px; + } + + .agent-table th, + .agent-table td { + padding-inline: 10px; + } + + .agent-legend { + display: none; + } + + .not-found-card { + box-shadow: 10px 10px 0 var(--sun); + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: .01ms !important; + animation-iteration-count: 1 !important; + transition-duration: .01ms !important; + } +} + +@media print { + .site-nav, + .hero-actions, + .copy-button, + .final-call, + .site-footer { + display: none; + } + + body { + background: #fff; + } + + .section, + .hero { + min-height: auto; + padding-block: 28px; + } +} diff --git a/pages/assets/site.js b/pages/assets/site.js new file mode 100644 index 0000000..6072760 --- /dev/null +++ b/pages/assets/site.js @@ -0,0 +1,49 @@ +(function () { + "use strict"; + + var copyButtons = document.querySelectorAll("[data-copy]"); + + function legacyCopy(text) { + var input = document.createElement("textarea"); + input.value = text; + input.setAttribute("readonly", ""); + input.style.position = "fixed"; + input.style.opacity = "0"; + document.body.appendChild(input); + input.select(); + var copied = document.execCommand("copy"); + input.remove(); + return copied ? Promise.resolve() : Promise.reject(new Error("copy failed")); + } + + function copyText(text) { + if (navigator.clipboard && window.isSecureContext) { + return navigator.clipboard.writeText(text); + } + return legacyCopy(text); + } + + copyButtons.forEach(function (button) { + var idleLabel = button.getAttribute("data-label-idle") || "Copy"; + var successLabel = button.getAttribute("data-label-success") || "Copied"; + var errorLabel = button.getAttribute("data-label-error") || "Select command"; + var label = button.querySelector("[data-copy-label]"); + var timer; + + button.addEventListener("click", function () { + window.clearTimeout(timer); + copyText(button.getAttribute("data-copy") || "").then(function () { + button.setAttribute("data-state", "success"); + label.textContent = successLabel; + }).catch(function () { + button.setAttribute("data-state", "error"); + label.textContent = errorLabel; + }).finally(function () { + timer = window.setTimeout(function () { + button.removeAttribute("data-state"); + label.textContent = idleLabel; + }, 1800); + }); + }); + }); +}()); diff --git a/pages/en/index.html b/pages/en/index.html index 9bb8216..e906cee 100644 --- a/pages/en/index.html +++ b/pages/en/index.html @@ -1,164 +1,255 @@ - - -AgentBoot · One-command AI Agent Launcher - - + + + + + AgentBoot · Launch your AI agents with one command + + + + + + + -
- -
-

English | 中文

-

AgentBoot

-

Minimal, fast, ready-to-run AI Agent launcher — one command to install, mainstream agents to choose from.

-
- v1.1.0Linux · macOS · WindowsChinese UI default - 14 agentsOffline installChina-network adaptiveMIT -
-
- -

⚡ One-command install

-

Linux / macOS

-
curl -fsSL https://boot.ide.pub/install.sh | sh -
-

Windows (PowerShell)

-
powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object Net.WebClient).DownloadString('https://boot.ide.pub/install.ps1'))" -
-
The installer uses project-controlled Worker / GitHub Pages / GitHub Release origins and verifies each archive against its same-origin SHA-256.
- -

🚀 Three steps

-
    -
  1. Install AgentBoot
    One command, ~10 seconds; only AgentBoot itself and the built-in agent are installed.
  2. -
  3. Open the console menu
    agentboot — environment check / install or uninstall agents / model providers / mirrors & proxy / custom offline builds.
  4. -
  5. Or just chat
    ab — the built-in fallback agent with the free Agnes model, zero config.
  6. -
- -

✨ Features

-
-
📦 Choose what to install (no bundle bloat)

14 mainstream agents to pick from: Claude Code, Codex, Qwen Code, OpenCode, CodeBuddy, MiMo, Cline, Pi, CoCo…

-
🛟 Built-in fallback agent (ab)

A zero-third-party-dependency Python core with Agnes, an offline Linux knowledge base, and session persistence.

-
🧠 Model provider manager

Agnes out of the box; named custom providers; Ollama / LM Studio local models; failover order.

-
🇨🇳 China network adaptive

npm / Node / PyPI mirrors, Worker/Pages/Release fallback, and one-click proxy configuration.

-
📴 Verified offline & custom builds

Release packs pass real install/run/uninstall smoke tests; menu [7] builds selected Agents on their target platform.

-
🧹 Ownership-aware uninstall

Menu [9] or the uninstall command removes AgentBoot-managed programs in batches while preserving config, credentials, and sessions by default.

-
⚡ Measurable performance

TLS reuse, pre-indexed KB, context trimming, and stream protection; /bench measures the current network and provider.

-
- -

🤖 Supported agents (14)

-
- - - - - - - - - - - - - - - -
#AgentCommandVendorOffline
1CoCo AgentcocoBitCookLinux/macOS
2OpenCodeopencodeopencode.aionline only
3Hermes AgenthermesHermes✓ (needs Git)
4Cline CLIclineCline
5CodeBuddy CLIcodebuddyTencent
6Pi Coding AgentpiEarendil Works
7Claude CodeclaudeAnthropic
8OpenAI Codex CLIcodexOpenAI✓ (Agnes preset)
9Qwen CodeqwenAlibaba✓ (Agnes preset)
10MiMo CodemimoXiaomi
11OpenClawopenclawOpenClaw
12Gemini CLIgeminiGoogle
13iFlow CLIiflowiFlow
14AideraiderAider AIonline only (pip)
-
✓ = offline install supported (dependencies and runtimes bundled). Codex / Qwen are auto-wired to the free Agnes model after install. Add your own agents via menu + or the add-agent command.
- -

📴 Offline install

-

No internet on the target machine? Two options:

-
    -
  • Verified slim packs: v1.1.0 initially provides Linux x64 / Windows x64 Codex packs after CI installs, starts, and uninstalls them;
  • -
  • Custom packs: menu [7] or build-offline on the target platform; Hermes must be built natively.
  • -
-

No unzip software needed (Windows Explorer / system tar / self-extracting script), then run install-offline.ps1 or sh install-offline.sh.

- -

🧠 Models: works out of the box + fully customizable

-
    -
  • Agnes free model (official preset): ab uses it with zero config; Codex / Qwen are auto-wired;
  • -
  • Custom providers: any OpenAI-compatible endpoint, named and managed;
  • -
  • Local models: Ollama / LM Studio presets; add vLLM as a custom OpenAI-compatible provider;
  • -
  • Failover: set a fallback order and the primary failure switches automatically.
  • -
- -

📚 Docs

- - - - - + + + + +
+
+

v1.2.0 · Free and open source · Three platforms

+

One command.
Agents ready.

+

Bring installation, models, mirrors, offline packs, and safe uninstall into one clear menu. You choose the AI agents you need; AgentBoot installs them reliably.

+ +
    +
  • Choose from 14 agents
  • +
  • Linux · macOS · Windows
  • +
  • Offline installation
  • +
  • MIT License
  • +
+
+ +
+ + + +
+
+ + agentboot +
+
+

$ agentboot

+

Checking system and network…

+

Choose the agents you want to install

+

Verify source and SHA-256

+

Ready. Start building.

+
+
+
+
+ +
+
+
+
+
+

Start here

+

Copy one line. Start right away.

+

After installation, run agentboot to open the console menu, or ab to chat directly with the built-in agent.

+
+
+ +

The installer uses only project-controlled Worker, GitHub Pages, and GitHub Release sources, then verifies each installation package against a same-origin SHA-256 checksum.

+
+
+ +
+
+
+ Linux / macOS + +
+ curl -fsSL https://boot.ide.pub/install.sh | sh +
+ +
+
+ Windows PowerShell + +
+ powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object Net.WebClient).DownloadString('https://boot.ide.pub/install.ps1'))" +
+
+
+ +
    +
  1. Install AgentBoot

    The command above takes about 10 seconds and installs only AgentBoot itself and the built-in agent.

  2. +
  3. Choose the agents you need

    Run agentboot to install, uninstall, and configure agents from the menu.

  4. +
  5. Get to work

    Launch your chosen agent, or run ab for a zero-configuration chat.

  6. +
+
+ +
+
+

More than an install script

AgentBoot handles the complexity. You make the choices.

+

No all-in-one bundle, and no failed setup left for you to debug. AgentBoot handles the parts most likely to break: environments, networks, sources, and uninstall boundaries.

+
+ +
+
+

A starting point that's always ready

+

Even if other agents won't install, ab works right away

+

The built-in agent uses a Python core with no third-party dependencies, the free Agnes model by default, an offline Linux knowledge base, persistent sessions, and safeguards against high-risk commands.

+
+ +
    +
  1. 01

    Install on demand, not as a bundle

    Choose from 14 mainstream agents, including Claude Code, Codex, Qwen Code, OpenCode, MiMo, Cline, Pi, and CoCo.

  2. +
  3. 02

    Manage model providers in one place

    Agnes works with zero configuration. You can also name and manage any OpenAI-compatible endpoint, Ollama, or LM Studio, and set a failover order.

  4. +
  5. 03

    Automatically adapt to networks in China

    AgentBoot selects npm, Node, and PyPI mirrors automatically, fails over across Worker, Pages, and Release sources, and configures a proxy in one step.

  6. +
  7. 04

    Offline packs are tested end to end

    Release packs pass install, launch, and uninstall smoke tests; menu [7] can also build a custom pack on demand on the target platform.

  8. +
  9. 05

    Uninstall only what AgentBoot owns

    Menu [9] or uninstall removes AgentBoot-managed programs while keeping configuration, credentials, and sessions by default.

  10. +
  11. 06

    Measure performance where it matters

    AgentBoot reuses TLS connections, prebuilds knowledge-base indexes, trims context automatically, and protects interrupted streams. /bench measures your current network and model.

  12. +
+
+
+ +
+
+
+

Your toolbox

14 agents. One front door.

+

Choose agents as needed for online installation. Agents marked as offline-ready can be packaged with their dependencies and runtimes.

+
+ +
+
+
14built-in choices
+ A green dot indicates support for offline installation +
+
+ + + + + + + + + + + + + + + + + + +
#AgentCommandVendorOffline
01CoCo AgentcocoBitCookLinux/macOS
02OpenCodeopencodeopencode.aiOnline only
03Hermes AgenthermesHermesRequires Git
04Cline CLIclineClineSupported
05CodeBuddy CLIcodebuddyTencentSupported
06Pi Coding AgentpiEarendil WorksSupported
07Claude CodeclaudeAnthropicSupported
08OpenAI Codex CLIcodexOpenAIAgnes preset
09Qwen CodeqwenAlibabaAgnes preset
10MiMo CodemimoXiaomiSupported
11OpenClawopenclawOpenClawSupported
12Gemini CLIgeminiGoogleSupported
13iFlow CLIiflowiFlowSupported
14AideraiderAider AIOnline only
+
+

Codex and Qwen are automatically configured with the free Agnes model after installation. Add your own agent with menu + or the add-agent command.

+
+
+
+ +
+
+
+

Install without internet access

+

Verified offline packs bring the complete runtime with you

+

v1.2.0 initially provides slim Codex packs for Linux x64 and Windows x64. CI installs each pack, runs its version command, and uninstalls it.

+
    +
  • Verified slim packs: download from Releases and carry them to the target machine.
  • +
  • Custom offline packs: use menu [7] or build-offline on the target platform.
  • +
  • No extra extraction software: run install-offline.ps1 or sh install-offline.sh inside the pack.
  • +
+
+ +
+

Your models, your choice

+

Ready out of the box and fully customizable

+
    +
  • Agnes free model: use ab with zero setup; Codex and Qwen are configured automatically.
  • +
  • Custom providers: name, manage, and switch any OpenAI-compatible endpoint.
  • +
  • Local models: Ollama and LM Studio presets, with vLLM available as a custom provider.
  • +
  • Failover: set a fallback order and switch automatically when the primary fails.
  • +
+
+
+
+ +
+
+
+

More detail when you need it

Documentation that speaks plainly

+

Start with the problem in front of you, from installation and offline deployment to custom agents.

+
+ +
+
+ +
+
+

Spend less time wrestling with setup. Start building sooner.

+

AgentBoot is open source under the MIT License. Start with one command; every choice stays in your hands.

+ +
+
+
+ + diff --git a/pages/index.html b/pages/index.html index bcf0cd0..2e01173 100644 --- a/pages/index.html +++ b/pages/index.html @@ -1,164 +1,255 @@ - - -AgentBoot · 一键安装 AI Agent 启动器 - - + + + + + AgentBoot · 一条命令,启动你的 AI Agent + + + + + + + -
- -
-

AgentBoot

-

极简 · 极速 · 开箱即用的 AI Agent 启动器 —— 一条命令装好,主流 Agent 菜单自选

-
- v1.1.0Linux · macOS · Windows界面中文 - 14 个 Agent 自选离线安装中国镜像自适应MIT -
-

中文 | English

-
- -

⚡ 一键安装

-

Linux / macOS

-
curl -fsSL https://boot.ide.pub/install.sh | sh -
-

Windows(PowerShell)

-
powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object Net.WebClient).DownloadString('https://boot.ide.pub/install.ps1'))" -
-
安装器只使用项目控制的 Worker / GitHub Pages / GitHub Release 三源,并对包与同源 SHA-256 一起校验。
- -

🚀 三步上手

-
    -
  1. 安装 AgentBoot
    上面一条命令,10 秒完成;只会装 AgentBoot 本体与内置 Agent。
  2. -
  3. 打开控制台菜单
    agentboot —— 环境体检 / 安装与卸载 Agent / 模型配置 / 镜像代理 / 自定义离线包。
  4. -
  5. 或直接对话
    ab —— 内置保底 Agent,默认 Agnes 免费模型,零配置开箱即用。
  6. -
- -

✨ 特性

-
-
📦 菜单自选安装(不是全家桶)

14 个主流 Agent 按需勾选:Claude Code、Codex、Qwen Code、OpenCode、CodeBuddy、MiMo、Cline、Pi、CoCo…

-
🛟 内置保底 Agent(ab)

其他都装不上时它一定能用:零第三方依赖 Python 核心、Agnes 免费模型、离线 Linux 知识库、会话持久化。

-
🧠 模型提供商管理器

Agnes 零配置开箱;自定义提供商命名管理;Ollama / LM Studio 本地模型;故障切换顺序。

-
🇨🇳 中国网络自适应

自动切换 npm / Node / PyPI 镜像;Worker / Pages / Release 三源容错;代理一键配置。

-
📴 已验证离线 & 按需构建

Release 精简包通过真实安装、启动与卸载冒烟;菜单 [7] 可在目标平台自选 Agent 构建。

-
🧹 可追溯安全卸载

菜单 [9] 或 uninstall 命令批量卸载;只清理由 AgentBoot 管理的程序,默认保留配置、认证与会话。

-
⚡ 可测性能

TLS 连接复用、知识库预建索引、上下文自动瘦身与流式中断保护;/bench 按当前网络与模型现场测量。

-
- -

🤖 支持的 Agent(14 个)

-
- - - - - - - - - - - - - - - -
#Agent命令厂商离线
1CoCo AgentcocoBitCookLinux/macOS
2OpenCodeopencodeopencode.ai仅在线
3Hermes AgenthermesHermes✓(需 Git)
4Cline CLIclineCline
5CodeBuddy CLIcodebuddyTencent
6Pi Coding AgentpiEarendil Works
7Claude CodeclaudeAnthropic
8OpenAI Codex CLIcodexOpenAI✓(Agnes 预置)
9Qwen CodeqwenAlibaba✓(Agnes 预置)
10MiMo CodemimoXiaomi
11OpenClawopenclawOpenClaw
12Gemini CLIgeminiGoogle
13iFlow CLIiflowiFlow 心流
14AideraiderAider AI仅在线(pip)
-
✓ = 支持离线安装(离线包内置完整依赖与运行时)。Codex / Qwen 安装后自动预置 Agnes 免费模型;自定义 Agent 可通过菜单 +add-agent 命令添加。
- -

📴 离线安装

-

无网机器?两条路:

-
    -
  • 已验证精简包:v1.1.0 首批提供 Linux x64 / Windows x64 的 Codex 包,CI 实际执行安装、版本启动与卸载;
  • -
  • 自建离线包:菜单 [7]build-offline 在目标平台自选 Agent;Hermes 必须在对应平台构建。
  • -
-

目标机无需联网、无需解压软件(Windows 资源管理器 / 系统自带 tar / 自解压脚本三选一),运行包内 install-offline.ps1sh install-offline.sh 即可。

- -

🧠 模型:开箱即用 + 完全自定义

-
    -
  • Agnes 免费模型(官方预设):ab 零配置直接用;Codex / Qwen 安装后自动接线;
  • -
  • 自定义提供商:任意 OpenAI 兼容接口,命名管理、随时增删切换;
  • -
  • 本地模型:内置 Ollama / LM Studio 预设,也可把 vLLM 添加为自定义 OpenAI 兼容提供商;
  • -
  • 故障切换:设置备用顺序,主模型失败自动降级。
  • -
- -

📚 文档

- - -
- MIT License · github.com/bit-cook/AgentBoot - · 主入口 boot.ide.pub(Cloudflare) - · 本页为 GitHub Pages 镜像入口 -
ab 内置 Agent 默认使用 Agnes 免费模型;执行命令类操作默认拦截高危行为。 -
- - + + + + +
+
+

v1.2.0 · 免费开源 · 三平台可用

+

一条命令,
Agent 就位

+

把安装、模型、镜像、离线包和安全卸载收进一个清楚的菜单。你选择要用的 AI Agent,AgentBoot 负责把它可靠地装好。

+ +
    +
  • 14 个 Agent 按需选择
  • +
  • Linux · macOS · Windows
  • +
  • 离线安装
  • +
  • MIT License
  • +
+
+ +
+ + + +
+
+ + agentboot +
+
+

$ agentboot

+

检查系统环境与网络…

+

选择你想安装的 Agent

+

校验来源与 SHA-256

+

安装完成,开始创造。

+
+
+
+
+ +
+
+
+
+
+

从这里开始

+

复制一行,马上启动

+

安装完成后输入 agentboot 打开控制台菜单,或输入 ab 直接与内置 Agent 对话。

+
+
+ +

安装器仅使用项目控制的 Worker、GitHub Pages 与 GitHub Release,并对安装包执行同源 SHA-256 校验。

+
+
+ +
+
+
+ Linux / macOS + +
+ curl -fsSL https://boot.ide.pub/install.sh | sh +
+ +
+
+ Windows PowerShell + +
+ powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object Net.WebClient).DownloadString('https://boot.ide.pub/install.ps1'))" +
+
+
+ +
    +
  1. 安装 AgentBoot

    上面一条命令约 10 秒完成,只安装 AgentBoot 本体与内置 Agent。

  2. +
  3. 选择需要的 Agent

    运行 agentboot,在菜单中安装、卸载和配置。

  4. +
  5. 开始工作

    运行选好的 Agent;也可以直接输入 ab 零配置对话。

  6. +
+
+ +
+
+

不仅是安装脚本

复杂留在里面,选择留给你

+

不塞给你一个全家桶,也不把失败留给你排查。AgentBoot 把最容易出错的环境、网络、来源与卸载边界统一处理。

+
+ +
+
+

始终可用的起点

+

即使其他 Agent 装不上,ab 也能马上工作

+

零第三方依赖 Python 核心,默认接入 Agnes 免费模型,并内置离线 Linux 知识库、会话持久化和高危命令拦截。

+
+ +
    +
  1. 01

    按需安装,不装全家桶

    14 个主流 Agent 自由选择:Claude Code、Codex、Qwen Code、OpenCode、MiMo、Cline、Pi、CoCo 等。

  2. +
  3. 02

    模型提供商统一管理

    Agnes 零配置可用,也支持命名管理任意 OpenAI 兼容接口、Ollama、LM Studio 与故障切换顺序。

  4. +
  5. 03

    中国网络自动适配

    自动选择 npm、Node、PyPI 镜像,Worker、Pages、Release 三源容错,并提供一键代理配置。

  6. +
  7. 04

    离线包经过真实验证

    发布包完成安装、启动、卸载冒烟;菜单 [7] 还能在目标平台按需构建自定义包。

  8. +
  9. 05

    卸载知道自己该删什么

    菜单 [9]uninstall 只清理由 AgentBoot 管理的程序,默认保留配置、认证和会话。

  10. +
  11. 06

    性能可以现场测量

    TLS 连接复用、知识库预建索引、上下文自动瘦身和流式中断保护;/bench 基于当前网络与模型实测。

  12. +
+
+
+ +
+
+
+

你的工具箱

14 个 Agent,一个入口

+

在线安装按需选择;标记为离线可用的 Agent 可连同依赖与运行时一起打包。

+
+ +
+
+
14个内置选项
+ 绿点表示支持离线安装 +
+
+ + + + + + + + + + + + + + + + + + +
#Agent命令厂商离线
01CoCo AgentcocoBitCookLinux/macOS
02OpenCodeopencodeopencode.ai仅在线
03Hermes AgenthermesHermes需 Git
04Cline CLIclineCline支持
05CodeBuddy CLIcodebuddyTencent支持
06Pi Coding AgentpiEarendil Works支持
07Claude CodeclaudeAnthropic支持
08OpenAI Codex CLIcodexOpenAIAgnes 预置
09Qwen CodeqwenAlibabaAgnes 预置
10MiMo CodemimoXiaomi支持
11OpenClawopenclawOpenClaw支持
12Gemini CLIgeminiGoogle支持
13iFlow CLIiflowiFlow 心流支持
14AideraiderAider AI仅在线
+
+

Codex 与 Qwen 安装后自动预置 Agnes 免费模型;也可通过菜单 +add-agent 添加自己的 Agent。

+
+
+
+ +
+
+
+

没有网络,也能安装

+

可验证离线包,把完整运行环境一起带走

+

v1.2.0 首批提供 Linux x64 与 Windows x64 Codex 精简包,CI 实际执行安装、版本启动与卸载。

+
    +
  • 已验证精简包:从 Releases 下载后直接带到目标机。
  • +
  • 自建离线包:用菜单 [7]build-offline 在目标平台按需构建。
  • +
  • 无需额外解压软件:运行包内 install-offline.ps1sh install-offline.sh
  • +
+
+ +
+

模型由你决定

+

开箱即用,也完全允许自定义

+
    +
  • Agnes 免费模型:ab 零配置直接用,Codex 与 Qwen 自动接线。
  • +
  • 自定义提供商:任意 OpenAI 兼容接口,命名管理、随时切换。
  • +
  • 本地模型:内置 Ollama 与 LM Studio 预设,也可添加 vLLM。
  • +
  • 故障切换:设置备用顺序,主模型失败后自动降级。
  • +
+
+
+
+ +
+
+
+

需要更多细节

文档直接说人话

+

从安装、离线部署到自定义 Agent,按你当前要解决的问题继续阅读。

+
+ +
+
+ +
+
+

少折腾环境,早点开始创造

+

AgentBoot 是 MIT 许可的开源项目。先用一条命令开始,所有选择依然掌握在你手里。

+ +
+
+
+ + diff --git a/pages/install.ps1 b/pages/install.ps1 index 6509068..d403b90 100644 --- a/pages/install.ps1 +++ b/pages/install.ps1 @@ -9,7 +9,7 @@ $ProgressPreference = 'SilentlyContinue' try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch {} $Repo = 'bit-cook/AgentBoot' -$Tag = 'v1.1.0' +$Tag = 'v1.2.0' $ZipName = "agentboot-online-$Tag.zip" $BootBase = 'https://boot.ide.pub' $GH = "https://github.com/$Repo/releases/download/$Tag" @@ -58,7 +58,8 @@ function Install-AppAtomic([string]$source, [string]$destination) { New-Item -ItemType Directory -Path $newApp -Force | Out-Null Copy-Item -Path (Join-Path $source '*') -Destination $newApp -Recurse -Force if (-not (Test-Path (Join-Path $newApp 'core\menu.py')) -or - -not (Test-Path (Join-Path $newApp 'core\agent.py'))) { + -not (Test-Path (Join-Path $newApp 'core\agent.py')) -or + -not (Test-Path (Join-Path $newApp 'core\launch.py'))) { throw '安装包结构无效' } if (Test-Path $destination) { Move-Item $destination $oldApp } @@ -240,7 +241,7 @@ rem AgentBoot 控制台 set "AB_INSTALL=%~dp0.." set "PYTHON=$pyCommand" if exist "%AB_INSTALL%\runtime\python\python.exe" set "PYTHON=%AB_INSTALL%\runtime\python\python.exe" -"%PYTHON%" "%AB_INSTALL%\app\core\menu.py" %* +"%PYTHON%" "%AB_INSTALL%\app\core\launch.py" menu %* "@ $abLauncher = @" @@ -249,7 +250,7 @@ rem AgentBoot 内置最小 Agent set "AB_INSTALL=%~dp0.." set "PYTHON=$pyCommand" if exist "%AB_INSTALL%\runtime\python\python.exe" set "PYTHON=%AB_INSTALL%\runtime\python\python.exe" -"%PYTHON%" "%AB_INSTALL%\app\core\agent.py" %* +"%PYTHON%" "%AB_INSTALL%\app\core\launch.py" agent %* "@ Set-LauncherAtomic (Join-Path $BinDir 'agentboot.cmd') $agentbootLauncher Set-LauncherAtomic (Join-Path $BinDir 'ab.cmd') $abLauncher diff --git a/pages/install.sh b/pages/install.sh index 5727b0f..1c86fbe 100755 --- a/pages/install.sh +++ b/pages/install.sh @@ -9,7 +9,7 @@ set -eu REPO="bit-cook/AgentBoot" -TAG="v1.1.0" +TAG="v1.2.0" TARBALL="agentboot-online-${TAG}.tar.gz" BOOT_BASE="https://boot.ide.pub" GH="https://github.com/${REPO}/releases/download/${TAG}" @@ -156,7 +156,7 @@ OLD_APP="${AB_ROOT}/app.old.$$" rm -rf "$NEW_APP" "$OLD_APP" mkdir -p "$NEW_APP" cp -R "${SRC_DIR}/." "$NEW_APP/" -if [ ! -f "$NEW_APP/core/menu.py" ] || [ ! -f "$NEW_APP/core/agent.py" ]; then +if [ ! -f "$NEW_APP/core/menu.py" ] || [ ! -f "$NEW_APP/core/agent.py" ] || [ ! -f "$NEW_APP/core/launch.py" ]; then err "安装包结构无效,保留现有版本" rm -rf "$NEW_APP" exit 1 @@ -182,13 +182,13 @@ cat > "$agentboot_tmp" < "$ab_tmp" </dev/null 2>&1 || true "$PY" "${APP_DIR}/core/agent.py" doctor 2>/dev/null || true # ---------- 7. 完成 ---------- diff --git a/performance-baseline.json b/performance-baseline.json new file mode 100644 index 0000000..a544498 --- /dev/null +++ b/performance-baseline.json @@ -0,0 +1,16 @@ +{ + "metrics": { + "startup_ms": 182.878, + "ab_version_ms": 119.849, + "menu_version_ms": 216.862, + "menu_ready_ms": 215.563, + "agent_run_ms": 167.357, + "install_ms": 218.45, + "homepage_gzip_bytes": 11026.0 + }, + "score": 100.0, + "sampling": { + "rounds": 3, + "runs_per_round": 15 + } +} diff --git a/scripts/benchmark-performance.py b/scripts/benchmark-performance.py new file mode 100644 index 0000000..7a25837 --- /dev/null +++ b/scripts/benchmark-performance.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 +"""Deterministic AgentBoot performance benchmark (stdlib only). + +This benchmark intentionally excludes public-network latency. It measures the +fixed cost AgentBoot controls: process startup, menu readiness, local model +request handling, install orchestration, and critical-page transfer size. +""" + +from __future__ import annotations + +import argparse +from contextlib import contextmanager +import gzip +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +import json +import os +from pathlib import Path +import shutil +import statistics +import subprocess +import sys +import tempfile +import threading +import time + + +ROOT = Path(__file__).resolve().parents[1] +PYTHON = sys.executable + + +class ModelHandler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def do_POST(self): + length = int(self.headers.get("Content-Length", "0")) + self.rfile.read(length) + body = json.dumps({ + "choices": [{"message": {"content": "ok", "tool_calls": []}}] + }, separators=(",", ":")).encode() + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def log_message(self, _format, *_args): + pass + + +@contextmanager +def model_server(): + server = ThreadingHTTPServer(("127.0.0.1", 0), ModelHandler) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + try: + yield server.server_address[1] + finally: + server.shutdown() + server.server_close() + thread.join(timeout=2) + + +def write_config(home: Path, port: int | None = None) -> None: + home.mkdir(parents=True, exist_ok=True) + config = { + "active": "bench" if port else "agnes", + "providers": ({ + "bench": { + "base_url": "http://127.0.0.1:%d/v1" % port, + "api_key": "", + "model": "bench", + } + } if port else {}), + "confirm": "safe", + "max_steps": 1, + "lang": "zh", + } + (home / "config.json").write_text(json.dumps(config), encoding="utf-8") + + +def elapsed_ms(command: list[str], env: dict[str, str], stdin: bytes | None = None) -> float: + start = time.perf_counter() + subprocess.run( + command, + cwd=ROOT, + env=env, + input=stdin, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + check=True, + timeout=15, + ) + return (time.perf_counter() - start) * 1000.0 + + +def median_command(command, env, runs, stdin=None) -> float: + elapsed_ms(command, env, stdin) + values = [elapsed_ms(command, env, stdin) for _ in range(runs)] + return statistics.median(values) + + +def fake_toolchain(root: Path) -> Path: + binary = root / "fake-bin" + binary.mkdir() + node = binary / "node" + npm = binary / "npm" + node.write_text("#!/bin/sh\nprintf 'v22.23.2\\n'\n", encoding="utf-8") + npm.write_text( + "#!/bin/sh\n" + "prefix=''\n" + "while [ $# -gt 0 ]; do\n" + " if [ \"$1\" = --prefix ]; then shift; prefix=$1; fi\n" + " shift\n" + "done\n" + "[ -n \"$prefix\" ] || exit 2\n" + "mkdir -p \"$prefix/bin\"\n" + "printf '#!/bin/sh\\nexit 0\\n' > \"$prefix/bin/codex\"\n" + "chmod +x \"$prefix/bin/codex\"\n", + encoding="utf-8", + ) + node.chmod(0o755) + npm.chmod(0o755) + return binary + + +def clean_install_home(root: Path) -> None: + for name in ("agentboot-home", "home"): + shutil.rmtree(root / name, ignore_errors=True) + (root / name).mkdir() + write_config(root / "agentboot-home") + + +def install_metric(root: Path, runs: int) -> float: + binary = fake_toolchain(root) + env = dict(os.environ) + env.update({ + "AGENTBOOT_HOME": str(root / "agentboot-home"), + "AGENTBOOT_MIRROR": "off", + "HOME": str(root / "home"), + "PATH": str(binary) + os.pathsep + env.get("PATH", ""), + }) + command = [PYTHON, "core/menu.py", "install", "codex"] + values = [] + for index in range(runs + 1): + clean_install_home(root) + value = elapsed_ms(command, env) + if index: + values.append(value) + return statistics.median(values) + + +def homepage_bytes() -> int: + paths = ( + ROOT / "pages" / "index.html", + ROOT / "pages" / "assets" / "site.css", + ROOT / "pages" / "assets" / "site.js", + ) + return sum(len(gzip.compress(path.read_bytes(), compresslevel=9)) for path in paths) + + +def measure(runs: int) -> dict[str, float]: + with tempfile.TemporaryDirectory(prefix="agentboot-perf-") as tmp: + root = Path(tmp) + home = root / "agentboot-home" + write_config(home) + env = dict(os.environ) + env.update({"AGENTBOOT_HOME": str(home), "AGENTBOOT_MIRROR": "off"}) + + ab_version = median_command([PYTHON, "core/agent.py", "version"], env, runs) + menu_version = median_command([PYTHON, "core/menu.py", "version"], env, runs) + menu_ready = median_command([PYTHON, "core/menu.py"], env, runs, b"0\n") + + with model_server() as port: + write_config(home, port) + agent_run = median_command( + [PYTHON, "core/agent.py", "run", "ping"], env, runs) + + install = install_metric(root, max(3, runs // 2)) + + return { + "startup_ms": round(statistics.fmean((ab_version, menu_version, menu_ready)), 3), + "ab_version_ms": round(ab_version, 3), + "menu_version_ms": round(menu_version, 3), + "menu_ready_ms": round(menu_ready, 3), + "agent_run_ms": round(agent_run, 3), + "install_ms": round(install, 3), + "homepage_gzip_bytes": float(homepage_bytes()), + } + + +def score(metrics: dict[str, float], baseline: dict[str, float]) -> float: + keys = ("startup_ms", "agent_run_ms", "install_ms", "homepage_gzip_bytes") + return round(100.0 * statistics.fmean(metrics[key] / baseline[key] for key in keys), 4) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--runs", type=int, default=9) + parser.add_argument("--baseline", type=Path) + parser.add_argument("--write-baseline", type=Path) + args = parser.parse_args() + if args.runs < 3: + parser.error("--runs must be at least 3") + + metrics = measure(args.runs) + output = {"metrics": metrics} + if args.baseline: + baseline = json.loads(args.baseline.read_text(encoding="utf-8"))["metrics"] + output["score"] = score(metrics, baseline) + else: + output["score"] = 100.0 + if args.write_baseline: + args.write_baseline.write_text(json.dumps(output, indent=2) + "\n", encoding="utf-8") + print(json.dumps(output, indent=2)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/build-online.py b/scripts/build-online.py index b303287..aec1a83 100644 --- a/scripts/build-online.py +++ b/scripts/build-online.py @@ -21,8 +21,13 @@ ".gitattributes", ".gitignore", "CHANGELOG.md", "LICENSE", "VERSION", "README.md", "README.en.md", "install.bat", "install.sh", "安装指南.md", ) -DIRECTORIES = ("agents", "cloudflare", "core", "docs", "scripts", "tools") +DIRECTORIES = ("agents", "core", "docs", "scripts", "tools") SKIP_PARTS = {"__pycache__", "node_modules", "payloads", "dist"} +SKIP_FILES = { + "scripts/benchmark-performance.py", + "scripts/sync-web-assets.py", + "scripts/verify-live-release.py", +} def package_files(): @@ -34,7 +39,8 @@ def package_files(): for dirname in DIRECTORIES: for path in (ROOT / dirname).rglob("*"): rel = path.relative_to(ROOT) - if path.is_file() and not (set(rel.parts) & SKIP_PARTS) and path.suffix != ".pyc": + if path.is_file() and not (set(rel.parts) & SKIP_PARTS) and \ + rel.as_posix() not in SKIP_FILES and path.suffix != ".pyc": files.append(path) return sorted(files, key=lambda path: path.relative_to(ROOT).as_posix()) diff --git a/scripts/install-offline.ps1 b/scripts/install-offline.ps1 index 96a9ada..6fdfb4f 100644 --- a/scripts/install-offline.ps1 +++ b/scripts/install-offline.ps1 @@ -133,7 +133,9 @@ foreach ($d in 'core', 'agents', 'tools', 'scripts') { foreach ($f in 'VERSION', 'README.md', '安装指南.md', 'LICENSE', 'CHANGELOG.md', 'install.sh', 'install.bat') { if (Test-Path (Join-Path $ScriptDir $f)) { Copy-Item (Join-Path $ScriptDir $f) $newApp -Force } } -if (-not (Test-Path (Join-Path $newApp 'core\menu.py')) -or -not (Test-Path (Join-Path $newApp 'core\agent.py'))) { +if (-not (Test-Path (Join-Path $newApp 'core\menu.py')) -or + -not (Test-Path (Join-Path $newApp 'core\agent.py')) -or + -not (Test-Path (Join-Path $newApp 'core\launch.py'))) { Remove-Item $newApp -Recurse -Force -ErrorAction SilentlyContinue; throw '离线包结构无效' } if (Test-Path $AppDir) { Move-Item $AppDir $oldApp } @@ -184,7 +186,7 @@ rem AgentBoot 控制台 set "AB_INSTALL=%~dp0.." set "PYTHON=$pyCommand" if exist "%AB_INSTALL%\runtime\python\python.exe" set "PYTHON=%AB_INSTALL%\runtime\python\python.exe" -"%PYTHON%" "%AB_INSTALL%\app\core\menu.py" %* +"%PYTHON%" "%AB_INSTALL%\app\core\launch.py" menu %* "@ $abLauncher = @" @echo off @@ -192,7 +194,7 @@ rem AgentBoot 内置最小 Agent set "AB_INSTALL=%~dp0.." set "PYTHON=$pyCommand" if exist "%AB_INSTALL%\runtime\python\python.exe" set "PYTHON=%AB_INSTALL%\runtime\python\python.exe" -"%PYTHON%" "%AB_INSTALL%\app\core\agent.py" %* +"%PYTHON%" "%AB_INSTALL%\app\core\launch.py" agent %* "@ Set-LauncherAtomic (Join-Path $BinDir 'agentboot.cmd') $agentbootLauncher Set-LauncherAtomic (Join-Path $BinDir 'ab.cmd') $abLauncher diff --git a/scripts/install-offline.sh b/scripts/install-offline.sh index 95f5300..1585063 100755 --- a/scripts/install-offline.sh +++ b/scripts/install-offline.sh @@ -86,7 +86,7 @@ cp -R "${SCRIPT_DIR}/scripts" "$NEW_APP/" for f in VERSION README.md 安装指南.md LICENSE CHANGELOG.md install.sh install.bat install-offline.sh install-offline.ps1; do [ -f "${SCRIPT_DIR}/$f" ] && cp -f "${SCRIPT_DIR}/$f" "$NEW_APP/" || true done -if [ ! -f "$NEW_APP/core/menu.py" ] || [ ! -f "$NEW_APP/core/agent.py" ]; then +if [ ! -f "$NEW_APP/core/menu.py" ] || [ ! -f "$NEW_APP/core/agent.py" ] || [ ! -f "$NEW_APP/core/launch.py" ]; then err "离线包结构无效,保留现有版本"; rm -rf "$NEW_APP"; exit 1 fi [ -d "$APP_DIR" ] && mv "$APP_DIR" "$OLD_APP" @@ -107,13 +107,13 @@ cat > "$agentboot_tmp" < "$ab_tmp" < str: + records = {} + for route, (path, content_type) in SOURCES.items(): + body = path.read_text(encoding="utf-8") + digest = hashlib.sha256(body.encode()).hexdigest()[:16] + records[route] = {"body": body, "type": content_type, "etag": '"%s"' % digest} + payload = json.dumps(records, ensure_ascii=False, separators=(",", ":")) + return ( + "// Generated by scripts/sync-web-assets.py; do not edit by hand.\n" + f"export const WEB_ASSETS = Object.freeze({payload});\n" + ) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--check", action="store_true", help="fail if the generated file is stale") + parser.add_argument("--mirror-root", type=Path, help="sync pages into /AgentBoot") + args = parser.parse_args() + expected = render() + ok = True + if args.check: + if not OUTPUT.exists() or OUTPUT.read_text(encoding="utf-8") != expected: + print("cloudflare/web-assets.js is stale; run scripts/sync-web-assets.py", file=sys.stderr) + ok = False + else: + OUTPUT.write_text(expected, encoding="utf-8") + + if args.mirror_root: + mirror = args.mirror_root.resolve() / "AgentBoot" + for relative in MIRROR_PATHS: + source = ROOT / "pages" / relative + target = mirror / relative + if target.is_file() and target.read_bytes() == source.read_bytes(): + continue + if args.check: + print(f"mirror is stale: {target}", file=sys.stderr) + ok = False + else: + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(source, target) + print(f"updated {target}") + return 0 if ok else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/tools/stage_application.py b/scripts/tools/stage_application.py index eeae122..a801822 100644 --- a/scripts/tools/stage_application.py +++ b/scripts/tools/stage_application.py @@ -8,7 +8,7 @@ FILES = ( "VERSION", "LICENSE", "CHANGELOG.md", "README.md", "README.en.md", "install.sh", "install.bat", "安装指南.md", - "agents/registry.json", "core/agent.py", "core/i18n.py", "core/menu.py", + "agents/registry.json", "core/agent.py", "core/i18n.py", "core/launch.py", "core/menu.py", "docs/en/install-guide.md", "docs/zh/安装指南.md", "scripts/build-offline.ps1", "scripts/build-offline.sh", "scripts/build-online.py", "scripts/install-offline.ps1", "scripts/install-offline.sh", "scripts/install.ps1", "scripts/verify-live-release.py", diff --git a/tests/test_agent_lifecycle_methods.py b/tests/test_agent_lifecycle_methods.py index 7918a71..d12179e 100644 --- a/tests/test_agent_lifecycle_methods.py +++ b/tests/test_agent_lifecycle_methods.py @@ -17,6 +17,37 @@ class AgentLifecycleTests(unittest.TestCase): + def test_matching_node_requirements_share_one_npm_install(self): + agents = [ + {"id": "one", "name": "One", "vendor": "T", "desc": "", "bin": "one", + "method": "npm", "npm": "pkg-one", "node": ">=18"}, + {"id": "two", "name": "Two", "vendor": "T", "desc": "", "bin": "two", + "method": "npm", "npm": "pkg-two", "node": ">=18"}, + ] + with mock.patch.object(menu, "load_registry", return_value=agents), \ + mock.patch.object(menu, "npm_install", return_value=True) as install, \ + mock.patch.object(menu, "find_bin", side_effect=lambda name: "/managed/" + name), \ + mock.patch.object(menu, "wire_agnes", return_value=({}, [])), \ + mock.patch.object(menu, "record_install"), \ + mock.patch.object(menu, "ensure_path_registered"): + self.assertEqual(menu.install_online(["one", "two"]), []) + install.assert_called_once() + self.assertEqual(install.call_args.args[0], ["pkg-one", "pkg-two"]) + self.assertEqual(install.call_args.args[1], ">=18") + + def test_npm_batch_reuses_mirror_and_environment_detection(self): + context = {} + completed = subprocess.CompletedProcess([], 0) + with mock.patch.object(menu, "npm_cmd", return_value="npm"), \ + mock.patch.object(menu, "ensure_npm_prefix"), \ + mock.patch.object(menu, "cn_mode", return_value=False) as mirror, \ + mock.patch.object(menu, "child_env", return_value={}) as child_env, \ + mock.patch.object(menu.subprocess, "run", return_value=completed): + self.assertTrue(menu.npm_install("one", ">=18", context)) + self.assertTrue(menu.npm_install("two", ">=20", context)) + mirror.assert_called_once_with() + child_env.assert_called_once_with() + def test_opencode_not_advertised_offline_until_postinstall_is_supported(self): registry = json.loads((ROOT / "agents/registry.json").read_text(encoding="utf-8")) opencode = next(agent for agent in registry["agents"] if agent["id"] == "opencode") diff --git a/tests/test_docs_consistency.py b/tests/test_docs_consistency.py index 3a3bdf6..f690335 100644 --- a/tests/test_docs_consistency.py +++ b/tests/test_docs_consistency.py @@ -12,7 +12,7 @@ class DocumentationConsistencyTests(unittest.TestCase): def test_chinese_guides_share_release_critical_facts(self): guides = [(ROOT / "安装指南.md").read_text(encoding="utf-8"), (ROOT / "docs/zh/安装指南.md").read_text(encoding="utf-8")] - facts = ("v1.1.0", "win-x64-codex.zip", "linux-x64-codex.tar.gz", + facts = ("v1.2.0", "win-x64-codex.zip", "linux-x64-codex.tar.gz", "PAYLOAD_SHA256SUMS.txt", "agentboot uninstall", "--purge", "Hermes", "目标平台") for fact in facts: @@ -22,21 +22,39 @@ def test_chinese_guides_share_release_critical_facts(self): def test_web_surfaces_share_release_critical_facts(self): surfaces = [(ROOT / "pages/index.html").read_text(encoding="utf-8"), (ROOT / "pages/en/index.html").read_text(encoding="utf-8"), - (ROOT / "cloudflare/worker.js").read_text(encoding="utf-8")] - for fact in ("v1.1.0", "Codex", "uninstall"): + (ROOT / "cloudflare/web-assets.js").read_text(encoding="utf-8")] + for fact in ("v1.2.0", "Codex", "uninstall"): for surface in surfaces: self.assertIn(fact, surface, fact) - def test_web_surfaces_include_mobile_overflow_and_touch_fixes(self): - surfaces = [(ROOT / "pages/index.html").read_text(encoding="utf-8"), - (ROOT / "pages/en/index.html").read_text(encoding="utf-8"), - (ROOT / "cloudflare/worker.js").read_text(encoding="utf-8")] - for surface in surfaces: + def test_pages_include_accessible_responsive_interactions(self): + pages = [(ROOT / "pages/index.html").read_text(encoding="utf-8"), + (ROOT / "pages/en/index.html").read_text(encoding="utf-8")] + css = (ROOT / "pages/assets/site.css").read_text(encoding="utf-8") + script = (ROOT / "pages/assets/site.js").read_text(encoding="utf-8") + for surface in pages: self.assertIn("table-scroll", surface) - self.assertIn("minmax(min(100%,280px),1fr)", surface) - self.assertIn("min-height:44px", surface) self.assertIn("aria-live", surface) self.assertIn("=18", cache)) + self.assertTrue(menu.node_ok("/managed/node", ">=22", cache)) + self.assertEqual(run.call_count, 1) + def result(self, version, code=0): return subprocess.CompletedProcess([], code, stdout=version, stderr="") @@ -36,7 +44,9 @@ def test_npm_install_requests_agent_minimum(self): mock.patch.object(menu, "npm_install", return_value=False) as install, \ mock.patch.object(menu, "ensure_path_registered"): menu.install_online(["openclaw"]) - install.assert_called_once_with("openclaw", ">=22.22.0") + install.assert_called_once() + self.assertEqual(install.call_args.args[:2], ("openclaw", ">=22.22.0")) + self.assertIsInstance(install.call_args.args[2], dict) def test_portable_runtime_satisfies_strictest_registry_requirement(self): self.assertEqual(menu.NODE_VERSION, "v22.23.2") diff --git a/tests/test_script_install.py b/tests/test_script_install.py index 090dfb8..259b67d 100644 --- a/tests/test_script_install.py +++ b/tests/test_script_install.py @@ -71,7 +71,7 @@ def geturl(self): def read(self, _size): return b"echo unsafe" - with mock.patch.object(menu.urllib.request, "urlopen", return_value=Response()), \ + with mock.patch("urllib.request.urlopen", return_value=Response()), \ self.assertRaisesRegex(ValueError, "非 HTTPS"): menu._download_script("https://example.test/install.sh", ".sh") diff --git a/tests/test_version_consistency.py b/tests/test_version_consistency.py index 317d646..e9e7966 100644 --- a/tests/test_version_consistency.py +++ b/tests/test_version_consistency.py @@ -35,6 +35,12 @@ def test_web_and_guides_show_current_version(self): text = (ROOT / relative).read_text(encoding="utf-8") self.assertIn(TAG, text, relative) + def test_web_asset_cache_busters_match_version(self): + for relative in ("pages/index.html", "pages/en/index.html"): + text = (ROOT / relative).read_text(encoding="utf-8") + self.assertIn("site.css?v=%s" % VERSION, text, relative) + self.assertIn("site.js?v=%s" % VERSION, text, relative) + def test_online_builder_names_current_version(self): text = (ROOT / "scripts/build-online.py").read_text(encoding="utf-8") self.assertIn('ROOT / "VERSION"', text) diff --git "a/\345\256\211\350\243\205\346\214\207\345\215\227.md" "b/\345\256\211\350\243\205\346\214\207\345\215\227.md" index d113265..ab6d88a 100644 --- "a/\345\256\211\350\243\205\346\214\207\345\215\227.md" +++ "b/\345\256\211\350\243\205\346\214\207\345\215\227.md" @@ -1,6 +1,6 @@ # AgentBoot 安装指南 -> **版本 v1.1.0** · 支持 Linux / macOS / Windows · 界面默认中文(切换:`agentboot lang en`) +> **版本 v1.2.0** · 支持 Linux / macOS / Windows · 界面默认中文(切换:`agentboot lang en`) > 语言 / Language: 中文(本页) | [English](docs/en/install-guide.md) | 中文副本:[docs/zh/安装指南.md](docs/zh/安装指南.md) > AgentBoot 是一个极简、极速、开箱即用的 AI Agent 启动器:内置一个保底最小 Agent(默认 Agnes 免费模型),其余 Agent(CoCo Agent、OpenCode、Hermes Agent、Cline、CodeBuddy、Pi、Claude Code、Codex、Qwen Code、MiMo Code、OpenClaw、Gemini CLI、iFlow CLI、Aider)通过**菜单自选安装**。除 Aider(pip 生态)外,**全部 Agent 支持离线安装**(离线包内置完整运行时,含 Hermes 的 Python venv 与 CoCo 的私有 Node)。 @@ -61,9 +61,9 @@ curl -fsSL https://bit-cook.github.io/AgentBoot/install.sh | sh - **方式 A(推荐)**:到 GitHub Releases 下载现成的离线包 `https://github.com/bit-cook/AgentBoot/releases` → 选择 - - `AgentBoot-offline-v1.1.0-win-x64-codex.zip`(Windows x64) - - `AgentBoot-offline-v1.1.0-linux-x64-codex.tar.gz`(Linux x64) - - `AgentBoot-offline-v1.1.0-linux-x64-codex-sfx.sh`(Linux x64 自解压单文件) + - `AgentBoot-offline-v1.2.0-win-x64-codex.zip`(Windows x64) + - `AgentBoot-offline-v1.2.0-linux-x64-codex.tar.gz`(Linux x64) + - `AgentBoot-offline-v1.2.0-linux-x64-codex-sfx.sh`(Linux x64 自解压单文件) - **方式 B(自建)**:在有网的 Linux/macOS 或 Windows 机器上运行项目自带的构建脚本, 自动从 npmmirror 镜像拉取全部 Agent 离线载荷并打包: @@ -83,9 +83,9 @@ curl -fsSL https://bit-cook.github.io/AgentBoot/install.sh | sh | 目标机器情况 | 解压方法 | |---|---| | **Windows(任意版本)** | 直接**右键 ZIP → 全部解压缩**。Windows 资源管理器自带 ZIP 支持,无需安装任何软件 | -| **Windows 10/11(命令行党)** | 系统自带 `tar`:`tar -xf AgentBoot-offline-v1.1.0-win-x64-codex.zip` | -| **Linux x64** | 系统自带 `tar`:`tar -xzf AgentBoot-offline-v1.1.0-linux-x64-codex.tar.gz -C ~` | -| **Linux x64 极简环境** | 用自解压单文件:`sh AgentBoot-offline-v1.1.0-linux-x64-codex-sfx.sh` | +| **Windows 10/11(命令行党)** | 系统自带 `tar`:`tar -xf AgentBoot-offline-v1.2.0-win-x64-codex.zip` | +| **Linux x64** | 系统自带 `tar`:`tar -xzf AgentBoot-offline-v1.2.0-linux-x64-codex.tar.gz -C ~` | +| **Linux x64 极简环境** | 用自解压单文件:`sh AgentBoot-offline-v1.2.0-linux-x64-codex-sfx.sh` | > 自解压包 `*-sfx.sh` 本身就是一个 shell 脚本 + 内嵌 base64 数据,`sh xxx-sfx.sh` 即自动解压并直接进入安装流程,非常适合只有最小化系统的服务器。