release: prepare v2.1.4 distribution

This commit is contained in:
2026-09-14 18:35:46 +08:00
parent bf7a1e8d25
commit 536e657506
9 changed files with 283 additions and 74 deletions
+8
View File
@@ -21,3 +21,11 @@ jobs:
- name: Syntax checks
run: npm run check
- name: Tests
run: npm test
- name: Validate extension metadata
run: |
jq empty apps/browser-extension/manifest.json
jq empty apps/browser-extension/rules/bypass-headers.json
+136 -68
View File
@@ -1,85 +1,153 @@
# AI Parallel
AI Parallel is a monorepo for comparing and orchestrating multiple AI web experiences from one workspace.
AI Parallel 是一个 Chrome / Edge Manifest V3 扩展:在一个工作区中打开多个 AI 网页,一次输入,并行发送,并按需收集、比较和导出回答。
The first app is a Chrome/Edge Manifest V3 extension. It reuses the user's
existing authenticated web sessions and embeds the selected model sites as
live provider iframes in a single workspace. Providers that are not iframe-safe,
currently Grok, run in a controlled top-level tab through the same adapter contract.
> No API keys. No proxy server. AI Parallel reuses your existing provider web sessions locally in the browser.
## Current app
## 功能
```text
apps/browser-extension
- 多模型网页工作区:ChatGPT、DeepSeek、智谱清言、Qwen、Kimi、Claude、Gemini、Grok
- 一次输入并行发送到多个 Provider
- Compare Drawer:收集当前回答并安全显示为纯文本
- 导出 Markdown、JSON,或下载 `.md` 文件
- Agent Handoff:把问题和多模型回答交给目标模型继续处理
- 本地 Prompt Library
- 布局切换与 Provider 独立刷新/打开
默认启用 ChatGPT、DeepSeek、智谱清言、Qwen 和 Kimi。Claude、Gemini 与 Grok 可按需开启。
## Provider 运行模式
| Provider | 运行方式 | 说明 |
| --- | --- | --- |
| ChatGPT | 工作区 iframe | 使用原站页面和当前登录会话 |
| DeepSeek | 工作区 iframe | 使用原站页面和当前登录会话 |
| 智谱清言 | 工作区 iframe | 使用原站页面和当前登录会话 |
| Qwen | 工作区 iframe | 使用原站页面和当前登录会话 |
| Kimi | 工作区 iframe | 使用原站页面和当前登录会话 |
| Claude | 工作区 iframe | DOM Adapter 为 best effort |
| Gemini | 工作区 iframe | DOM Adapter 为 best effort |
| Grok | 受控顶层标签页 | 避免登录 iframe 与 WebSocket timeout |
Grok 官网依赖已登录的实时 WebSocket,认证页也禁止 iframe。AI Parallel 会打开或复用正常的 `grok.com` 标签页,然后通过受限消息桥完成 Prompt 发送、回答收集和 Handoff。
## 从 GitHub Release 安装
1. 打开 [Releases](https://github.com/CoderLambert/ai-parallel/releases/latest)。
2. 下载 `ai-parallel-browser-extension-vX.Y.Z.zip`
3. 解压 ZIP,保留解压目录,不要在安装后删除或移动它。
4. Chrome 打开 `chrome://extensions/`Edge 打开 `edge://extensions/`
5. 开启右上角“开发者模式”。
6. 点击“加载已解压的扩展程序”。
7. 选择解压得到的 `ai-parallel-browser-extension` 文件夹。
8. 固定 AI Parallel 图标,点击即可打开工作区。
Chrome 不能直接把普通 ZIP 当成未打包扩展安装,必须先解压。
### 校验下载文件
每个 Release 都提供 SHA-256 文件:
```bash
sha256sum -c ai-parallel-browser-extension-vX.Y.Z.zip.sha256
```
Workspace v2.1 supports:
## 首次使用
- ChatGPT
- DeepSeek
- 智谱清言
- Qwen
- Kimi
- Claude (best-effort response adapter)
- Gemini (best-effort response adapter)
- Grok (best-effort response adapter)
- Local Prompt Library
- Comparison and Agent Handoff
1. 先在各 Provider 官网完成登录。
2. 点击 AI Parallel 图标打开工作区。
3. 在顶部选择 Provider。
4. 在底部输入 Prompt,按 `Ctrl+Enter` / `Cmd+Enter` 或点击“发送”。
5. 等待原站生成回答,点击 `Compare` 收集结果。
The default comparison set is ChatGPT, DeepSeek, 智谱清言, Qwen and Kimi.
Grok 第一次参与发送时会打开或复用官网标签页。扩展升级后,如果旧 Grok 标签页没有新版桥接脚本,AI Parallel 会自动刷新该标签页一次。
## Workspace architecture
## 更新
### 手动更新
下载新 Release,解压并覆盖原目录,然后在 `chrome://extensions/` / `edge://extensions/` 中点击 AI Parallel 的“重新加载”。如果替换了目录位置,需要删除旧扩展后重新“加载已解压的扩展程序”。
### Linux:使用 `ai-parallel-sync`
从 Release 下载 `ai-parallel-sync` 后安装:
```bash
install -Dm755 ai-parallel-sync ~/.local/bin/ai-parallel-sync
ai-parallel-sync
```
首次运行会把扩展同步到:
```text
~/.local/share/ai-parallel/browser-extension
```
第一次在浏览器中加载这个固定目录。以后只需运行 `ai-parallel-sync`,再到扩展管理页点击“重新加载”。命令默认同步最新 `v*` Release 标签,需要 `git``node``npm``rsync`
## 常见问题
### `accounts.x.ai refused to connect`
不要在 iframe 中登录 Grok。当前版本会让 Grok 在正常顶层标签页中运行。请确认已更新扩展,并完全关闭后重新打开旧的 AI Parallel 工作区标签页。
### Grok 显示 WebSocket `timeout`
确认普通 `https://grok.com/` 标签页可以聊天。AI Parallel v2.1.4 起不再把 Grok 嵌入 iframe;如果仍看到旧 iframe,请在扩展管理页重新加载扩展并重新打开工作区。
### `ERR_BLOCKED_BY_CLIENT`
这通常来自广告或隐私扩展。若被阻止的是 Provider 的核心接口,请暂时关闭对应站点的拦截后重试;单纯的统计/广告请求失败通常不影响聊天。
### Provider 显示“未就绪”或发送失败
- 重新加载对应面板。
- 确认已经登录原站。
- 在扩展管理页重新加载 AI Parallel,并重新打开工作区。
- Provider 网页 DOM 会更新;选择器 Adapter 属于 best effort,需要随原站变化维护。
## 权限与隐私
AI Parallel 请求:
- `storage`:保存 Provider 选择、草稿和 Prompt Library。
- `tabs`:打开/聚焦工作区和 Grok 顶层标签页。
- `declarativeNetRequest*`:仅对已配置 Provider 的 `sub_frame` 响应移除 iframe 限制头。
- Provider host permissions:注入本地 Adapter,并与原站页面交互。
扩展不保存 Provider 密码,不请求 Cookie 权限,不把 Prompt 放进目标 URL,也不把回答发送到 AI Parallel 自有服务器。回答只在用户点击 Compare 后收集到当前扩展页面,并按纯文本渲染。
## 本地开发
要求 Node.js 18+
```bash
git clone https://github.com/CoderLambert/ai-parallel.git
cd ai-parallel
npm run check
npm test
```
从源码加载时,在扩展管理页选择 `apps/browser-extension`
生成 Release 包:
```bash
npm run package:extension
```
产物位于 `dist/`
## 架构
```text
AI Parallel Workspace
├─ ChatGPT iframe ─ Content Script ─┐
├─ DeepSeek iframe ─ Content Script ├─ postMessage bridge
├─ Qwen iframe ───── Content Script ├─ native provider rendering
├─ Kimi iframe ───── Content Script ┘
└─ Grok tab ─ Service Worker ─ tabs.sendMessage bridge
├─ iframe Providers ─ postMessage ─ Provider Adapter
└─ Grok top-level tab ─ service worker ─ tabs.sendMessage ─ Grok Adapter
```
Prompts are not transported in destination URLs. The workspace sends one-time
jobs to iframe providers through an origin-checked `postMessage` bridge and to
tab-mode providers through a provider-scoped service-worker bridge.
Provider Adapter 统一提供 `sendPrompt()``collectResponse()``newChat()`。详细设计见 [docs/architecture.md](docs/architecture.md)。第三方架构参考和署名见 [ATTRIBUTION.md](ATTRIBUTION.md)。
## Repository structure
## 项目状态
```text
ai-parallel/
├── apps/
│ └── browser-extension/
│ ├── content/
│ ├── workspace/
│ ├── icons/
│ ├── manifest.json
│ └── service-worker.js
├── docs/
│ └── architecture.md
├── .github/workflows/
├── package.json
└── pnpm-workspace.yaml
```
Shared packages will be extracted only when a second client or genuine cross-app duplication requires them.
## Development
```bash
npm run check
```
Load `apps/browser-extension` as an unpacked extension from `chrome://extensions/` or `edge://extensions/`.
Clicking the extension icon opens or focuses the full-page Workspace.
## Security and reliability
- Uses only the host permissions required by supported providers.
- Reuses existing browser login sessions; it does not store provider credentials.
- Does not put prompt text into provider URLs.
- Renders collected model output as safe text/structured blocks rather than injecting remote HTML.
- Provider DOM adapters are best-effort because AI web UIs are not stable public APIs.
See [`docs/architecture.md`](docs/architecture.md) for the runtime design and evolution plan.
AI Web UI 不是稳定 APIProvider DOM 或安全策略变化可能导致 Adapter 暂时失效。欢迎通过 GitHub Issues 提交复现步骤、页面截图和控制台错误。
+7 -2
View File
@@ -26,9 +26,14 @@ The original provider page remains visible directly. Compare can collect the
latest visible response on demand for safe text rendering and export; it does
not inject provider HTML or replace the native iframe view.
## Install
## Install from a release
Open `chrome://extensions`, enable Developer mode, choose **Load unpacked**, and select this directory.
Download and extract `ai-parallel-browser-extension-vX.Y.Z.zip`. Open
`chrome://extensions` or `edge://extensions`, enable Developer mode, choose
**Load unpacked**, and select the extracted `ai-parallel-browser-extension`
directory. Keep that directory in place after installation.
When loading from a source checkout, select `apps/browser-extension` directly.
Grok authentication and chat run in a top-level browser tab because `accounts.x.ai`
does not permit iframe login and Grok's real-time WebSocket is not iframe-safe. AI
+1
View File
@@ -3,6 +3,7 @@
"name": "AI Parallel",
"version": "2.1.4",
"description": "Compare multiple AI web chats side by side in one live workspace.",
"homepage_url": "https://github.com/CoderLambert/ai-parallel",
"minimum_chrome_version": "120",
"permissions": [
"storage",
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "@ai-parallel/browser-extension",
"private": true,
"version": "2.0.1",
"description": "Manifest V3 iframe workspace for comparing AI web chats in parallel.",
"version": "2.1.4",
"description": "Manifest V3 workspace for comparing AI web chats in parallel.",
"scripts": {
"check": "node --check service-worker.js && node --check popup.js && node --check content/frame-bridge.js && node --check workspace/workspace.js"
}
+36
View File
@@ -0,0 +1,36 @@
# AI Parallel v2.1.4
首个公开 GitHub Release,提供可直接解压加载的 Chrome/Edge Manifest V3 扩展包。
## 主要功能
- ChatGPT、DeepSeek、智谱清言、Qwen、Kimi、Claude、Gemini 多面板网页工作区
- Grok 受控顶层标签页模式,保留官网登录状态和 WebSocket 实时连接
- 一次输入,并行发送到已选模型
- Compare Drawer 收集回答并导出 Markdown、JSON 或 `.md` 文件
- Agent Handoff:将比较上下文发送给目标模型
- 本地 Prompt Library
## Grok 兼容性
Grok 的认证页禁止 iframe,且其已登录 WebSocket 在扩展 iframe 中不稳定。此版本不再强行嵌入 Grok,而是复用正常的 `grok.com` 顶层标签页,并通过受限的扩展消息桥执行发送与回答收集。
## 安装
1. 下载 `ai-parallel-browser-extension-v2.1.4.zip`
2. 解压 ZIP。
3. 打开 `chrome://extensions/``edge://extensions/`
4. 开启开发者模式,点击“加载已解压的扩展程序”。
5. 选择解压后的 `ai-parallel-browser-extension` 文件夹。
## 校验
下载同名 `.sha256` 文件后执行:
```bash
sha256sum -c ai-parallel-browser-extension-v2.1.4.zip.sha256
```
## 更新
手动更新时覆盖或重新解压到固定目录,然后在扩展管理页点击“重新加载”。Linux 用户也可以下载本 Release 的 `ai-parallel-sync`,安装到 `~/.local/bin/` 后用同名命令同步后续版本。
+3 -2
View File
@@ -1,10 +1,11 @@
{
"name": "ai-parallel",
"private": true,
"version": "0.3.0",
"version": "2.1.4",
"description": "Monorepo for parallel AI workflows across browser, web and desktop clients.",
"scripts": {
"check": "node --check apps/browser-extension/service-worker.js && node --check apps/browser-extension/popup.js && node --check apps/browser-extension/content/providers/core.js && node --check apps/browser-extension/content/providers/chatgpt.js && node --check apps/browser-extension/content/providers/deepseek.js && node --check apps/browser-extension/content/providers/qwen.js && node --check apps/browser-extension/content/providers/kimi.js && node --check apps/browser-extension/content/providers/zhipu.js && node --check apps/browser-extension/content/providers/claude.js && node --check apps/browser-extension/content/providers/gemini.js && node --check apps/browser-extension/content/providers/grok.js && node --check apps/browser-extension/content/frame-bridge.js && node --check apps/browser-extension/workspace/context-utils.js && node --check apps/browser-extension/workspace/workspace.js",
"test": "node --test tests/*.test.js"
"test": "node --test tests/*.test.js",
"package:extension": "bash scripts/package-extension.sh"
}
}
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
set -Eeuo pipefail
REPO_URL="https://github.com/CoderLambert/ai-parallel.git"
BASE_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/ai-parallel"
REPO_DIR="$BASE_DIR/repo"
EXT_DIR="$BASE_DIR/browser-extension"
log() { printf '\033[1;34m[ai-parallel]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[✓]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m[✗]\033[0m %s\n' "$*" >&2; exit 1; }
command -v git >/dev/null || fail "未找到 git"
command -v node >/dev/null || fail "未找到 node"
command -v npm >/dev/null || fail "未找到 npm"
command -v rsync >/dev/null || fail "未找到 rsync"
mkdir -p "$BASE_DIR"
if [[ ! -d "$REPO_DIR/.git" ]]; then
log "首次安装,克隆仓库..."
git clone "$REPO_URL" "$REPO_DIR"
else
log "检查 GitHub Release..."
if [[ -n "$(git -C "$REPO_DIR" status --porcelain)" ]]; then
fail "同步缓存存在未提交修改:$REPO_DIR"
fi
fi
git -C "$REPO_DIR" fetch origin --tags --prune
LATEST_TAG="$(git -C "$REPO_DIR" tag --list 'v*' --sort=-v:refname | sed -n '1p')"
[[ -n "$LATEST_TAG" ]] || fail "仓库中尚无可用的版本标签"
git -C "$REPO_DIR" checkout --detach "$LATEST_TAG"
ok "使用最新 Release$LATEST_TAG"
log "执行代码检查..."
(cd "$REPO_DIR" && npm run check && npm test)
ok "代码检查通过"
MANIFEST="$REPO_DIR/apps/browser-extension/manifest.json"
[[ -f "$MANIFEST" ]] || fail "找不到 manifest.json"
VERSION="$(node -p "require('$MANIFEST').version")"
log "同步浏览器扩展..."
mkdir -p "$EXT_DIR"
rsync -a --delete --exclude='node_modules' "$REPO_DIR/apps/browser-extension/" "$EXT_DIR/"
COMMIT="$(git -C "$REPO_DIR" rev-parse --short HEAD)"
ok "已同步 AI Parallel v$VERSION ($LATEST_TAG, $COMMIT)"
printf '\n扩展目录:%s\n' "$EXT_DIR"
printf '请在 chrome://extensions/ 或 edge://extensions/ 点击「重新加载」。\n'
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
MANIFEST="$REPO_DIR/apps/browser-extension/manifest.json"
OUTPUT_DIR="${1:-$REPO_DIR/dist}"
command -v git >/dev/null || { echo "git is required" >&2; exit 1; }
command -v node >/dev/null || { echo "node is required" >&2; exit 1; }
command -v sha256sum >/dev/null || { echo "sha256sum is required" >&2; exit 1; }
if ! git -C "$REPO_DIR" diff --quiet -- apps/browser-extension ||
! git -C "$REPO_DIR" diff --cached --quiet -- apps/browser-extension; then
echo "Commit browser-extension changes before packaging" >&2
exit 1
fi
VERSION="$(node -p "require('$MANIFEST').version")"
ARCHIVE_NAME="ai-parallel-browser-extension-v${VERSION}.zip"
ARCHIVE_PATH="$OUTPUT_DIR/$ARCHIVE_NAME"
CHECKSUM_PATH="$ARCHIVE_PATH.sha256"
mkdir -p "$OUTPUT_DIR"
git -C "$REPO_DIR" archive \
--format=zip \
--prefix="ai-parallel-browser-extension/" \
--output="$ARCHIVE_PATH" \
HEAD:apps/browser-extension
(
cd "$OUTPUT_DIR"
sha256sum "$ARCHIVE_NAME" > "$ARCHIVE_NAME.sha256"
)
printf 'Created %s\n' "$ARCHIVE_PATH"
printf 'Created %s\n' "$CHECKSUM_PATH"