release: prepare v2.1.4 distribution
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 不是稳定 API,Provider DOM 或安全策略变化可能导致 Adapter 暂时失效。欢迎通过 GitHub Issues 提交复现步骤、页面截图和控制台错误。
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+52
@@ -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'
|
||||
Executable
+38
@@ -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"
|
||||
Reference in New Issue
Block a user