From 536e65750624eca3ed368e5e13d7a46a80a5332d Mon Sep 17 00:00:00 2001 From: CoderLambert Date: Mon, 14 Sep 2026 18:35:46 +0800 Subject: [PATCH] release: prepare v2.1.4 distribution --- .github/workflows/ci.yml | 8 ++ README.md | 204 ++++++++++++++++++--------- apps/browser-extension/README.md | 9 +- apps/browser-extension/manifest.json | 1 + apps/browser-extension/package.json | 4 +- docs/releases/v2.1.4.md | 36 +++++ package.json | 5 +- scripts/ai-parallel-sync | 52 +++++++ scripts/package-extension.sh | 38 +++++ 9 files changed, 283 insertions(+), 74 deletions(-) create mode 100644 docs/releases/v2.1.4.md create mode 100755 scripts/ai-parallel-sync create mode 100755 scripts/package-extension.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e10e59c..2854aab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/README.md b/README.md index d091a47..0b09d10 100644 --- a/README.md +++ b/README.md @@ -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 提交复现步骤、页面截图和控制台错误。 diff --git a/apps/browser-extension/README.md b/apps/browser-extension/README.md index 1a709f6..e8034a1 100644 --- a/apps/browser-extension/README.md +++ b/apps/browser-extension/README.md @@ -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 diff --git a/apps/browser-extension/manifest.json b/apps/browser-extension/manifest.json index 0cb8f17..0c0f53a 100644 --- a/apps/browser-extension/manifest.json +++ b/apps/browser-extension/manifest.json @@ -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", diff --git a/apps/browser-extension/package.json b/apps/browser-extension/package.json index 68ef915..2a402bf 100644 --- a/apps/browser-extension/package.json +++ b/apps/browser-extension/package.json @@ -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" } diff --git a/docs/releases/v2.1.4.md b/docs/releases/v2.1.4.md new file mode 100644 index 0000000..70dd30a --- /dev/null +++ b/docs/releases/v2.1.4.md @@ -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/` 后用同名命令同步后续版本。 diff --git a/package.json b/package.json index 4d3b860..91512e6 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/scripts/ai-parallel-sync b/scripts/ai-parallel-sync new file mode 100755 index 0000000..a4443d8 --- /dev/null +++ b/scripts/ai-parallel-sync @@ -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' diff --git a/scripts/package-extension.sh b/scripts/package-extension.sh new file mode 100755 index 0000000..b5dfae1 --- /dev/null +++ b/scripts/package-extension.sh @@ -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"