From 452e03ada7c66b9c96355bb131311be8960e5e75 Mon Sep 17 00:00:00 2001 From: superche <35420957+superche@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:22:02 +0800 Subject: [PATCH 1/2] docs: streamline readme and add support code --- README.md | 594 +++++++------------------------------ assets/buy-me-a-coffee.jpg | Bin 0 -> 110625 bytes 2 files changed, 109 insertions(+), 485 deletions(-) create mode 100644 assets/buy-me-a-coffee.jpg diff --git a/README.md b/README.md index e8e7864..3900adc 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,42 @@ # Pippit Bridge -> Release health note (2026-07-20): the repository candidate is `0.2.16`, while the canonical marketplace remains pinned to the only currently published npm artifact, `0.2.13`. Do not activate `0.2.16` until that exact artifact is published and re-downloaded successfully. See [Codex Plugin development and release engineering](docs/codex-plugin-dev-release-engineering.md). -

Pippit Bridge 飞鸟 Logo

-## 安装方法 +

+ 让 Codex、OpenCode、MCP 与 ChatGPT 安全调用小云雀的图片和视频生成能力。 +
+ 本地优先 · 多账号 BYOK · 参考素材 · 结果预览与落盘 +

-### OpenCode +

+ 快速安装 · + 选择接入方式 · + 首次使用 · + 文档导航 +

-从公开 npm registry 全局安装 OpenCode custom-tool plugin: +Pippit Bridge 是小云雀(Pippit)的开源 API gateway 与 adapter monorepo。它把图片和视频生成、参考素材上传、任务查询、结果下载与账号管理封装成 agent 可以安全调用的工具,同时提供 OpenRouter 风格的 facade 和 TypeScript SDK。 -```bash -opencode plugin @pippit-bridge/opencode-plugin --global -``` +## 功能亮点 -也可以在全局或项目 `opencode.json` 中配置: +- **图片与视频生成**:支持文生图、参考图生图、文生视频,以及图片、视频、音频参考素材。 +- **视频工作流**:支持首尾帧、异步任务查询、结果预览与参考视频片段重拍。 +- **安全的多账号 BYOK**:通过一次性 loopback 页面录入和切换 Pippit AK,原始密钥不进入聊天或项目配置。 +- **本地优先**:Codex、stdio MCP 与 OpenCode 默认在受信主机上运行,生成结果持久化到用户目录。 +- **多种接入面**:同一套能力可用于 Codex、OpenCode、通用 MCP、ChatGPT App 和 OpenRouter 风格 API。 -```json -{ - "$schema": "https://opencode.ai/config.json", - "plugin": ["@pippit-bridge/opencode-plugin"] -} -``` +> [!IMPORTANT] +> Pippit Bridge 当前聚焦图片与视频生成。音频可作为视频参考素材,但尚未提供通用文本、语音生成或转录工具。 + +## 快速安装 + +运行 Codex plugin 或完整 monorepo 需要 Node.js 22.22.2+、24.15.0+ 或 26+,并确保 `npm` / `npx` 可用。 -### Codex +### Codex(推荐) -从公开 GitHub marketplace 安装 `pippit-video` plugin,无需克隆本仓库或使用本地路径: +从公开 GitHub marketplace 安装,无需克隆本仓库: ```bash codex plugin marketplace add superche/pippit-bridge --ref main --json @@ -35,127 +44,17 @@ codex plugin add pippit-video@pippit-bridge --json codex plugin list --json ``` -Codex plugin 运行时需要 Node.js 22.22.2+、24.15.0+ 或 26+,并确保 `npm` / `npx` 可用。安装完成后重启 ChatGPT Desktop 或新建 Codex session。完整接入说明见 [MCP、ChatGPT App 与 Codex plugin](./docs/integrations.md)。 - -### Codex Plugin 开发与发布 - -开发环境必须使用独立 Codex profile,并通过稳定 gateway 热切换 backend worker;不要同时启用开发版与正式版: - -```bash -npm run codex:dev:profile:setup -npm run codex:dev -npm run codex:dev:app -npm run codex:dev:profile:status -npm run codex:dev:status -npm run codex:dev:full-gate -``` - -Run `codex:dev:profile:setup` once before starting the watcher in another terminal. It bootstraps -the stable gateway, registers only -`pippit-video@pippit-bridge-dev` in a persistent isolated profile, and preserves that profile's -login and appearance settings across restarts. On macOS, `codex:dev:app` launches a separate -ChatGPT/Codex process with `~/.codex-profiles/dev` and -`~/Library/Application Support/Codex Dev`; it never opens a workspace path as an Electron entry -point and refuses a profile containing the production Pippit plugin. Override these locations with -`PIPPIT_CODEX_DEV_PROFILE_HOME` and `PIPPIT_CODEX_DEV_BROWSER_DATA_DIR` when needed. Sign in and -choose the theme once inside the Dev app; credentials and browser data remain local and are never -copied into the repository. - -The Dev App, stable gateway/worker generations, and detached loopback Facade are three separate -lifecycle layers. `codex:dev:app` converges all three: it refreshes only the isolated plugin cache, -authenticates the Facade inside the Dev runtime root, reuses an exact entry/SHA-256 match, and safely -replaces any authenticated non-exact daemon before launching the App. -`codex:dev:profile:status` reports App PIDs, cache/gateway hashes, and Facade entry/hash/PID/health -independently. Cold refresh preserves login/browser data as well as BYOK credentials, runtime -secrets, jobs, artifacts, outputs, and idempotency state. - -正式发布使用受保护的两阶段 workflow:本地/CI gate、npm publish、官方 registry 重下载验证完成后,才允许创建 exact-version marketplace activation PR。Agent 操作约束、hot/cold contract 边界和 rollback 规则见 [AGENTS.md](./AGENTS.md),完整设计与 runbook 见 [Codex Plugin 开发热更新与正式发布工程](./docs/codex-plugin-dev-release-engineering.md)。当前 backend worker HMR 已接通;已挂载 Widget iframe 的原地 HMR 尚未接入,不得对外声明支持。 - -Pippit Bridge 是小云雀(Pippit)的 API gateway 与 adapter monorepo。当前同时提供: - -- OpenRouter 风格的图片/视频生成 facade 与服务器持久化 BYOK。 -- 可发布的 Pippit TypeScript SDK、共享模型目录与安全素材能力。 -- `@pippit-bridge/opencode-plugin`:不注册 LLM provider/auth slot 的 OpenCode 本地视频 custom-tool plugin。 -- `@pippit-bridge/mcp-server`:通用 stdio MCP server,面向支持 MCP 的本地 agent/client。 -- `@pippit-bridge/chatgpt-app`:使用 Streamable HTTP `/mcp` 与 Apps SDK widget 的 ChatGPT App。 -- `pippit-video`:从公开 GitHub marketplace 安装的 Codex plugin;公开快照携带 manifest、skill 与启动 shim,运行时使用 npm 上的同一个 stdio MCP server。 - -**架构定位:这是面向单个本地用户的 plugin bridge,不是多租户 SaaS。** 主要产品面是 Codex `pippit-video` plugin 与 `@pippit-bridge/opencode-plugin`;通用 MCP、loopback Facade 和本地 ChatGPT developer app 服务于同一用户、同一台受信主机上的接入与调试。项目不以多用户 OAuth、租户隔离、分布式锁、横向扩容或跨机器状态同步为当前目标。 - -**当前三种封装提供图片与视频生成。** 图片由 Seedream 5.0 / 5.0 Pro 同步返回,视频仍使用异步 job、轮询与本地结果 widget。图片、视频和音频也可作为视频参考素材;这不代表已提供通用文本、语音生成或转录工具。通用 MCP 与 Codex plugin 另外提供 facade 账号管理;ChatGPT App 的 `noauth` developer-mode surface 不暴露这组管理工具。 - -```text -pippit-bridge -├── apps -│ ├── openrouter-facade -│ └── chatgpt-app -├── packages -│ ├── core -│ ├── sdk -│ ├── mcp-server-pippit -│ └── opencode-plugin-pippit -├── .agents/plugins/marketplace.json -└── docs -``` - -`core` 是模型版本与安全素材真源;`sdk` 只封装小云雀官方 AK API;OpenCode plugin 复用 MCP package 的一次性 loopback enrollment,并直接使用 core + sdk 完成视频任务。MCP server 只调用 facade,ChatGPT App 再复用 MCP 的工具实现;Codex plugin 的 manifest、skill 和 `.mcp.json` 直接随 `packages/mcp-server-pippit` 分发。后续的 CLI、ComfyUI、n8n 和 OpenMontage adapter 仍可作为新 workspace 增加,不需要复制上游协议实现。 - -## MCP、ChatGPT App 与 Codex plugin - -| 形式 | 运行入口 | 暴露能力 | 适用场景 | -| --- | --- | --- | --- | -| 通用 MCP package | `packages/mcp-server-pippit/src/stdio.ts` | 安全新增/列出/切换/删除 AK;列模型、生成、参考视频重新生成、查询;completed 自动本地落盘与受限额外下载 | 支持 stdio MCP 的本地 client | -| ChatGPT App | `https:///mcp` | 投影 MCP 的图片生成,以及视频列模型、生成、查询和参考视频重新生成;视频结果提供 widget | ChatGPT developer mode;本地或 tunnel 调试 | -| Codex plugin | `pippit-video@pippit-bridge` | `.mcp.json` 直接启动同一个通用 MCP,skill 只负责安全编排 | Codex CLI 或 ChatGPT Desktop 的 Codex 插件面 | - -本地 stdio MCP、Codex plugin 和本地 ChatGPT App 默认不需要预设 facade 环境变量。安装与 `initialize` / `tools/list` 发现阶段不启动服务、也不创建密钥;第一次实际 MCP 工具调用(或本地 ChatGPT App 启动)才幂等创建/复用一个用户级、只监听 loopback 的共享 Facade。内部 runtime、management、BYOK encryption、job signing 与 ChatGPT media signing key 保存在 plugin cache 和项目目录之外,卸载 plugin 默认不会删除账号数据;Codex/stdio 预览通过宿主代理的 MCP 本地资源读取,不依赖临时端口。 - -部署方也可以显式使用外部 Facade;此时下面两个变量必须成对设置,半套配置会 fail closed,不会与本地自动配置混用: - -```bash -export PIPPIT_FACADE_BASE_URL=http://127.0.0.1:3000 -export PIPPIT_FACADE_API_KEY='' -``` - -`PIPPIT_FACADE_API_KEY` 是 wrapper 访问外部 facade 的凭证,不是 Pippit AK。自动本地模式会自行生成与保存内部 Facade/Management key;Pippit AK 仍只能从 `pippit_add_access_key` 返回的短时 loopback password 页面进入加密 store,不应写入环境变量、MCP client 配置、ChatGPT widget、工具结果或 Codex plugin。 - -外部模式下,stdio MCP / Codex 若要管理 AK,还需设置与 runtime key 严格分离的 `PIPPIT_FACADE_MANAGEMENT_API_KEY`。切换状态按 Facade API Key 指纹持久化在加密 BYOK store 中,因此同一 runtime identity 的 MCP、Codex 与 ChatGPT App 对“新任务使用哪个账号”保持一致。ChatGPT App 当前不投影 AK 管理工具,也不会继承 Management key。 - -最短启动命令: - -```bash -# 通用 stdio MCP / Codex plugin 内嵌 server -npm run dev:mcp - -# ChatGPT App,默认监听 http://127.0.0.1:8787/mcp -npm run dev:chatgpt-app -``` - -本地 ChatGPT App 未配置公开 origin 时不启用媒体预览。设置 tunnel/部署 origin `CHATGPT_APP_PUBLIC_BASE_URL` 后,本地自动模式会使用独立的用户级 media signing key;显式外部 Facade 模式仍须同时设置 `CHATGPT_APP_MEDIA_SIGNING_KEY_HEX`。 - -Codex plugin 的 manifest 是声明式配置,宿主没有可安全生成/注入 secret 的 install/postinstall hook;因此这里的“安装自动处理”落在首次实际能力调用,而不是安装期间执行任意代码。ChatGPT developer/production App 仍必须部署为可达 HTTPS endpoint、在 ChatGPT 注册真实 app ID;生产多用户形态还必须增加 OAuth 与远程 secret manager,不能由本地 plugin 安装代替。 +安装完成后重启 ChatGPT Desktop 或新建 Codex session。 -完整的 MCP client 配置、ChatGPT developer-mode 注册、Codex marketplace 安装命令、生产 OAuth 要求和 `.app.json` 真实 ID 边界见 [三种集成形式](./docs/integrations.md)。 - -`pippit_generate_video` 与 `pippit_edit_video_segment` 的 `idempotency_key` 是可选异常恢复键,不属于 Facade/OpenRouter 协议。图片接口按 OpenRouter dedicated Image API 同步返回,不暴露这一 MCP 恢复字段。完整边界见 [持久化幂等设计](./docs/idempotency.md)。 - -## OpenCode custom-tool plugin - -OpenCode 1.18.3 的 model provider contract 是 AI SDK `LanguageModelV3`。小云雀当前公开的是异步视频任务 API,不是聊天/流式语言模型;本项目不会把视频模型伪装成聊天模型。这个包只注册 custom tools,不返回 `auth`/`provider` hook,也不写入 `config.provider.pippit`,因此不会过滤或覆盖宿主默认 LLM: - -- `pippit_manage_access_keys`:返回一次性 localhost password form,并脱敏列出、切换和删除多个本地账号的 AK。 -- `pippit_generate_video`:上传参考素材并提交视频生成;每次付费提交前显式请求权限。 -- `pippit_generate_image`:使用 Seedream 5.0 / 5.0 Pro 生成图片,可带最多 9 张参考图;Codex/stdio 先将完成图片保存到本地 output root,再通过稳定 artifact 地址预览并提供原图下载。 -- `pippit_get_video`:查询任务;写入 worktree 前单独请求下载权限。 -- 共享模型目录:新增 Pippit 模型版本时只增加 catalog entry,两个 adapter 同步可见。 +### OpenCode -直接安装到 OpenCode 全局配置: +从公开 npm registry 全局安装: ```bash opencode plugin @pippit-bridge/opencode-plugin --global ``` -也可以在 OpenCode 配置中加入: +也可以写入全局或项目级 `opencode.json`: ```json { @@ -164,401 +63,126 @@ opencode plugin @pippit-bridge/opencode-plugin --global } ``` -让 OpenCode 配置一个账号时,调用 `pippit_manage_access_keys` 的 `configure` 操作并传入本地账号名。工具返回短时、单次使用的 `http://127.0.0.1:/enroll/`;用户在浏览器 password form 中粘贴 AK,表单直接写入插件私有 keyring 并设为 active。raw AK 不进入聊天、普通工具参数、OpenCode auth slot、URL query 或日志。 - -同一个 `pippit_manage_access_keys` 工具还提供: - -- `list`:只返回 `account_id`、本地账号名、脱敏 AK 与 active 状态。 -- `switch`:按 `account_id` 或账号名切换新任务使用的 AK。 -- `delete`:删除本地保存;删除 active 且仍有其他账号时必须先显式 switch。工具会展示受影响的历史 run 数量,并提醒“本地删除不等于官网撤销”。 - -多账号秘密保存在 OpenCode 全局 state 下的 `pippit/access-keys.json`,而不是项目目录或 OpenCode provider auth store。父目录使用 `0700`、文件使用 `0600`,原子替换写入;当前内容是同 UID 可读的明文,不是系统 keychain。plugin 不读取 Cookie、不监听剪贴板、不把 AK 放进普通 tool 参数、项目配置、日志或 telemetry。 - -`PIPPIT_ACCESS_KEY` 仍用于 CI/短期隔离环境,并覆盖新任务使用的本地 active 账号;管理工具会把这个 override 明确返回。已持久绑定的历史 `run_id + thread_id` 始终优先使用原账号,不会被后来设置的环境变量或 active 切换静默改写;若原账号已删除则 fail closed。完整 loopback 协议、威胁模型与验收标准见 [OpenCode AK 绑定设计](./docs/opencode-ak-binding.md)。 - -OpenCode 包的安装与 options 见 [packages/opencode-plugin-pippit/README.md](./packages/opencode-plugin-pippit/README.md)。 - -## OpenRouter facade - -facade 底层调用小云雀 Seedream 图片生成与沉浸式短片视频 API,并提供服务器持久化 BYOK(Bring Your Own Key)。 - -调用方不会把 Pippit AK 直接用作 facade 的 Bearer token。部署管理员先用 Management API Key 将小云雀官方签发的 Pippit AK 写入 `/api/v1/byok`;运行时调用方再使用独立的 Facade API Key 访问模型、生成、轮询和下载接口。 - -```text -Management API Key -> /api/v1/byok - -> /api/v1/byok/active - -> encrypted Pippit AK store + per-Facade-key active selection - -Facade API Key -> POST /api/v1/videos - -> POST /api/v1/images - -> POST /api/v1/videos/edits - -> GET /api/v1/videos/{jobId} - -> GET /api/v1/videos/{jobId}/content?index=0 -``` - -对图片、视频和音频参考素材,服务会先下载每个 URL,逐个调用小云雀上传接口取得 `data.pippit_asset_id`,全部成功后再根据目标提交图片或视频任务: +## 选择接入方式 -```text -image_url / video_url / audio_url - -> POST /api/biz/v1/skill/upload_file - -> data.pippit_asset_id - -> POST /api/biz/v1/skill/submit_run - -> thread_id + run_id - -> POST /api/biz/v1/agent/query_generate_video_result -``` - -## 认证边界 - -| 凭证 | 来源与保存方式 | 允许访问的接口 | +| 接入方式 | 适合场景 | 入口 | | --- | --- | --- | -| Management API Key | 部署方生成;服务配置中只保存 SHA-256 | 仅 `/api/v1/byok` CRUD | -| Facade API Key | 部署方生成并发给调用方;服务配置中只保存 SHA-256 allowlist | `/api/v1/models`、`/api/v1/images**`、`/api/v1/videos/**` | -| Pippit AK | 从小云雀官方页面签发;通过 BYOK API 写入并加密落盘 | 仅由服务调用 Pippit 上游,不作为 facade Bearer token | -| BYOK encryption key | 32 个随机字节;仅部署环境持有 | AES-256-GCM 加密 BYOK store | -| Job signing key | 另一把独立的 32 字节随机密钥 | 签名并校验异步 `jobId` | - -Management API Key 不能调用模型或视频接口,Facade API Key 不能管理 BYOK;若 Management digest 同时出现在 Facade allowlist,服务会拒绝启动。`BYOK_ENCRYPTION_KEY_HEX` 与 `JOB_SIGNING_KEY_HEX` 必须不同。 +| **Codex plugin** | 在 Codex CLI 或 ChatGPT Desktop 中直接生成和预览素材 | `pippit-video@pippit-bridge` | +| **OpenCode plugin** | 在 OpenCode 中使用图片、视频和账号工具 | `@pippit-bridge/opencode-plugin` | +| **通用 MCP** | 其他支持 stdio MCP 的本地 agent/client | `@pippit-bridge/mcp-server` | +| **ChatGPT App** | ChatGPT developer mode 或 HTTPS 部署 | `@pippit-bridge/chatgpt-app` | +| **OpenRouter facade** | HTTP API、服务器持久化 BYOK 或自定义 adapter | `apps/openrouter-facade` | -本项目参考 OpenRouter 的 BYOK 管理资源和 Management API Key 认证方式,但有两个明确扩展: +完整配置和部署边界见 [MCP、ChatGPT App 与 Codex plugin 接入指南](./docs/integrations.md)。 -- BYOK 请求中的 `provider: "pippit"` 是本 facade 扩展;OpenRouter 官方 provider 枚举不应被理解为已经包含 Pippit。 -- 在 BYOK create/update 中写入 `allowed_api_key_hashes` 是本 facade 扩展,用于把一条 Pippit AK 限定给指定 Facade API Key 的 SHA-256;传 `null` 表示不做该项限制。 +## 首次使用 -当前 facade 只解析静态 Facade API Key,没有 per-user identity。`allowed_user_ids` 只有为 `null` 时才能用于当前运行时路由;一旦写成非空列表,任何图片或视频生成请求都不会匹配该 credential。保留这个字段是为了契约兼容和未来扩展,不代表已经支持 user routing。 +1. 在[小云雀官网](https://xyq.jianying.com/)签发并复制 Pippit AK。 +2. Codex/stdio MCP 调用 `pippit_add_access_key`;OpenCode 调用 `pippit_manage_access_keys` 的 `configure` 操作。 +3. 在工具返回的短时本地页面中粘贴 AK。不要把 AK 发送到聊天、普通工具参数、URL query 或项目配置。 +4. 直接用自然语言请求 agent,例如“用这张参考图生成一张 16:9 海报”或“生成一段 10 秒的视频”。 -当前 file store 是单 workspace 实现,workspace 固定为 `00000000-0000-0000-0000-000000000000`。创建 credential 时建议省略 `workspace_id`;传入其他 workspace id 会被拒绝,不会被静默合并。 +主要能力: -原始 Pippit AK 不会由 list/get/update 响应回显,响应只返回掩码 `label` 和路由元数据。 - -### Pippit AK 的签发边界 - -Pippit AK 必须由用户在小云雀官方页面中签发。本 provider 不导入 Pippit Cookie,也不代替官方页面管理 AK;它只接收已经由官方签发的 AK,并通过自己的 Management-Key-protected BYOK API 加密保存。 - -## 快速开始 - -完整 monorepo 要求 Node.js 22.22.2+、24.15.0+ 或 26+;单独运行 facade/core/sdk 的最低版本仍为 Node.js 22。 - -先生成四个彼此独立的高熵值: - -```bash -export MANAGEMENT_API_KEY="$(openssl rand -hex 32)" -export FACADE_API_KEY="$(openssl rand -hex 32)" - -printf '%s' "$MANAGEMENT_API_KEY" | shasum -a 256 -printf '%s' "$FACADE_API_KEY" | shasum -a 256 - -openssl rand -hex 32 # BYOK_ENCRYPTION_KEY_HEX -openssl rand -hex 32 # JOB_SIGNING_KEY_HEX,必须与上一行不同 -``` - -复制配置,并把上面两个摘要和两把 64 位十六进制密钥填入 `.env`: - -```bash -npm install -cp .env.example .env -npm run dev -``` +| 能力 | 工具 | +| --- | --- | +| 模型发现 | `pippit_list_image_models`、`pippit_list_video_models` | +| 图片生成 | `pippit_generate_image` | +| 视频生成与查询 | `pippit_generate_video`、`pippit_get_video` | +| 视频片段重拍 | `pippit_edit_video_segment` | +| 本地视频副本 | `pippit_download_video` | +| Codex/MCP 账号管理 | `pippit_add_access_key`、`pippit_list_access_keys`、`pippit_switch_access_key`、`pippit_delete_access_key` | -默认监听 `http://127.0.0.1:3000`。所有必需密钥或摘要缺失时,服务会拒绝启动。 +Codex/MCP 完成的图片和视频默认保存到 macOS `~/Movies/Pippit` 或其他平台 `~/Videos/Pippit`,随后通过结果卡预览。更多工具、文件上传和 ChatGPT App 差异见[接入指南](./docs/integrations.md)。 -### 1. 写入 Pippit BYOK +## 项目架构 -先从小云雀官方页面创建并复制 AK,再用 Management API Key 写入。以下示例将该 AK 限定给一个 Facade API Key;`FACADE_API_KEY_SHA256` 是该 Facade API Key 的小写 SHA-256: +这是面向**单个本地用户、单台受信主机**的 plugin bridge,不是多租户 SaaS。多用户 OAuth、租户隔离、横向扩容和跨机器状态同步不属于当前目标。 -```bash -export PIPPIT_AK='ak-...' -export FACADE_API_KEY_SHA256='' - -curl -X POST http://localhost:3000/api/v1/byok \ - -H "Authorization: Bearer $MANAGEMENT_API_KEY" \ - -H 'Content-Type: application/json' \ - -d "{ - \"provider\": \"pippit\", - \"key\": \"${PIPPIT_AK}\", - \"name\": \"production-pippit\", - \"allowed_models\": [\"pippit/seedance-2.0\"], - \"allowed_api_key_hashes\": [\"${FACADE_API_KEY_SHA256}\"] - }" -``` - -成功返回 HTTP `201`,其中 `data.id` 是 BYOK credential id;`key` 不会返回: +```text +Codex / MCP / ChatGPT + -> authenticated OpenRouter-style Facade + -> encrypted BYOK store + -> Pippit SDK + -> Pippit upstream -```json -{ - "data": { - "id": "30a504af-e33b-46a3-a689-b40fae68bd25", - "provider": "pippit", - "label": "ak-****bc12", - "name": "production-pippit", - "disabled": false, - "is_fallback": false - } -} +OpenCode + -> Core + Pippit SDK + -> Pippit upstream ``` -管理接口均只接受 Management API Key,并返回 `Cache-Control: no-store`: - ```text -POST /api/v1/byok -GET /api/v1/byok -GET /api/v1/byok/{id} -PATCH /api/v1/byok/{id} -DELETE /api/v1/byok/{id} +pippit-bridge +├── apps +│ ├── openrouter-facade +│ └── chatgpt-app +├── packages +│ ├── contracts +│ ├── core +│ ├── sdk +│ ├── mcp-server-pippit +│ └── opencode-plugin-pippit +├── .agents/plugins/marketplace.json +└── docs ``` -用 `PATCH` 传入新的 `key` 会创建新的内部 key version。已经生成的 `jobId` 仍绑定旧 version,因此轮询不会因为正常轮换而漂移到新 AK。删除 credential 会同时删除其版本,依赖它的未完成任务将无法再查询;若需要立即吊销,应同时在小云雀官方侧撤销 AK。 - -### 2. 发现模型 +`contracts` 是公共 schema 真源,`core` 提供模型目录与安全文件原语,`sdk` 封装小云雀上游 API。完整依赖方向、Widget、运行时和发布边界见[架构文档](./docs/architecture.md)。 -模型与生成接口使用 Facade API Key: +## 本地开发 ```bash -curl http://localhost:3000/api/v1/videos/models \ - -H "Authorization: Bearer $FACADE_API_KEY" - -curl http://localhost:3000/api/v1/images/models \ - -H "Authorization: Bearer $FACADE_API_KEY" +npm ci +npm run dev # OpenRouter facade +npm run dev:mcp # stdio MCP +npm run dev:chatgpt-app # http://127.0.0.1:8787/mcp ``` -### 3. 生成图片 +Facade 默认监听 `http://127.0.0.1:3000`。配置项以 [.env.example](./.env.example) 为准,HTTP 合同以 [OpenAPI golden](./apps/openrouter-facade/contracts/openapi.golden.json) 为准。 -图片只暴露 `pippit/seedream-5.0` 与 `pippit/seedream-5.0-pro`,省略 `model` 时默认使用 Seedream 5.0。`pippit/seedream-5.0` 必须完全省略 `resolution`;`pippit/seedream-5.0-pro` 可省略或使用 `1K`、`2K`、`4K`。`input_references` 最多 9 张,图片完成后以 OpenRouter dedicated Image API 的 `data[].b64_json` 同步返回。 +连接显式外部 Facade 时,下面两个变量必须成对设置: ```bash -curl -X POST http://localhost:3000/api/v1/images \ - -H "Authorization: Bearer $FACADE_API_KEY" \ - -H 'Content-Type: application/json' \ - -d '{ - "model": "pippit/seedream-5.0-pro", - "prompt": "未来感运动鞋广告海报,黑色背景,蓝紫霓虹边缘光", - "resolution": "4K", - "n": 1 - }' +export PIPPIT_FACADE_BASE_URL=http://127.0.0.1:3000 +export PIPPIT_FACADE_API_KEY='' ``` -### 4. 提交视频 - -视频暴露 Seedance 2.5 与既有四个 Seedance 2.0 模型;普通生成省略 `model` 时仍默认使用 VIP 通道的 Seedance 2.0 Mini。原生片段重拍固定使用 Seedance 2.5。 +Codex plugin 开发使用独立 profile: ```bash -curl -X POST http://localhost:3000/api/v1/videos \ - -H "Authorization: Bearer $FACADE_API_KEY" \ - -H 'Content-Type: application/json' \ - -d '{ - "prompt": "以产品特写开场,镜头缓慢推进,最后出现品牌标语", - "duration": 10, - "resolution": "720p", - "aspect_ratio": "9:16", - "provider": { - "options": { - "pippit": { - "byok_id": "30a504af-e33b-46a3-a689-b40fae68bd25" - } - } - }, - "input_references": [ - { - "type": "image_url", - "image_url": { "url": "https://example.com/product.png" } - }, - { - "type": "video_url", - "video_url": { "url": "https://example.com/motion.mp4" } - }, - { - "type": "audio_url", - "audio_url": { "url": "https://example.com/music.mp3" } - } - ] - }' -``` - -`provider.options.pippit.byok_id` 也是 facade 扩展;省略时服务会按 credential 的限制条件和排序自动选择。继续已有 `thread_id` 且存在多个可用 credential 时,必须显式提供 `byok_id`,避免把 Pippit 会话切换到另一条 AK。 - -成功返回 HTTP `202`: - -```json -{ - "id": "pippit_job_v2....", - "polling_url": "/api/v1/videos/pippit_job_v2....", - "status": "pending", - "generation_id": "marketing_...", - "model": "pippit/seedance-2.0-mini", - "usage": { "is_byok": true } -} +npm run codex:dev:profile:setup +npm run codex:dev +npm run codex:dev:app +npm run codex:dev:full-gate ``` -用提交任务时的同一个 Facade API Key 查询和下载: +运行完整检查: ```bash -curl http://localhost:3000/api/v1/videos/$JOB_ID \ - -H "Authorization: Bearer $FACADE_API_KEY" - -curl "http://localhost:3000/api/v1/videos/$JOB_ID/content?index=0" \ - -H "Authorization: Bearer $FACADE_API_KEY" \ - --output result.mp4 -``` - -`jobId` 是带 HMAC 的无状态句柄,绑定 Facade API Key、workspace、BYOK credential/key version、`thread_id`、`run_id` 和 facade model id。只要相同的 job signing key 和所需 credential version 仍存在,服务重启后即可继续查询。 - -## BYOK 选择与 fallback - -运行时只考虑满足以下条件的 credential:未禁用、`provider`/workspace 匹配、允许当前 model,并且 `allowed_api_key_hashes` 允许当前 Facade API Key。主 credential 按 `sort_order` 优先,`is_fallback: true` 的 credential 排在其后。 - -服务只在 Pippit 明确返回 HTTP `401`、`403` 或 `429` 时尝试下一条 credential。网络错误、超时或 `submit_run` 结果不确定时不会 fallback,避免同一请求在上游产生重复任务。每次切换 credential,参考图片、视频和音频都会使用该 credential 重新上传并保留返回的 `data.pippit_asset_id` 与可用的 `data.asset_id`,随后才调用 `submit_run`。 - -## 首尾帧 - -OpenRouter 的 `frame_images` 会映射为小云雀 `generate_type: 1`。上传顺序固定为首帧、尾帧,不依赖调用方数组顺序。 - -```json -{ - "model": "pippit/seedance-2.0-mini", - "prompt": "从白天平滑过渡到夜景", - "frame_images": [ - { - "type": "image_url", - "image_url": { "url": "https://example.com/first.png" }, - "frame_type": "first_frame" - }, - { - "type": "image_url", - "image_url": { "url": "https://example.com/last.png" }, - "frame_type": "last_frame" - } - ] -} +npm run check ``` -与 OpenRouter 当前语义一致,同时传 `frame_images` 和 `input_references` 时,`frame_images` 优先,后者不会上传或提交。 - -## 模型 - -| Facade model | Pippit model | 通道 | 分辨率 | -| --- | --- | --- | --- | -| `pippit/seedance-2.0-mini`(默认) | `Seedance_2.0_mini` | VIP | `480p`, `720p` | -| `pippit/seedance-2.0` | `seedance2.0_direct` | 非 VIP | `480p`, `720p` | -| `pippit/seedance-2.0-mini-lite` | `Seedance_2.0_mini_lite` | 非 VIP | `480p`, `720p` | -| `pippit/seedance-2.0-vision` | `seedance2.0_vision` | VIP | `480p`, `720p`, `1080p` | - -公开生成接口只接受表格中的稳定 facade id;原始 Pippit model 字符串仅用于内部上游映射,不作为可调用模型暴露。 +正式发布、hot/cold contract 和回滚流程见 [Codex Plugin 开发与发布工程](./docs/codex-plugin-dev-release-engineering.md)。 -接口同时提供: +## 文档导航 -- `GET /api/v1/videos/models`:OpenRouter 视频模型能力结构,需要 Facade API Key。 -- `GET /api/v1/models`:带 `architecture.input_modalities/output_modalities` 的通用模型列表,需要 Facade API Key。 -- `GET /openapi.json`:OpenAPI 3.1 描述。 -- `GET /health`:不鉴权的健康检查;加密 BYOK store 不可用时不会返回健康状态。 - -## 参数映射 - -| OpenRouter 字段 | Pippit 字段/行为 | +| 文档 | 内容 | | --- | --- | -| `prompt` | 同时写入 `message` 与 `video_part_tool_param.prompt` | -| `duration` | `duration_sec`;省略时默认 5 秒 | -| `aspect_ratio` | `ratio` | -| `resolution` | `resolution` | -| `size` | 显式拒绝;Pippit 只承诺 `resolution + ratio`,不能保证精确像素尺寸 | -| `seed` | `video_part_tool_param.seed` | -| `frame_images` | 先上传并取得 `data.pippit_asset_id`,写入 `images`,设置 `generate_type: 1` | -| `input_references` | 图片/视频/音频先上传并取得 `data.pippit_asset_id`,再写入 `images` / `videos` / `audios` | -| `provider.options.pippit.byok_id` | facade 扩展;固定使用指定 BYOK credential | -| `provider.options.pippit.thread_id` | facade 扩展;复用已有 Pippit 会话 | - -小云雀文档没有暴露 `callback_url` 和可控的 `generate_audio`,因此显式传入这两个字段会返回 `unsupported_parameter`,不会静默忽略。 - -## 参考素材安全 - -- 只接受 `http:` / `https:` URL;不接受 `data:`、`file:` 或携带 URL credentials 的地址。 -- 默认拒绝 localhost、私网、链路本地和其他非公网目标;每次重定向都会重新校验,生产传输会把已校验 DNS 地址固定到实际 socket,避免 DNS rebinding。 -- 根据文件特征和 MIME/扩展名校验格式:图片支持 JPEG/PNG/GIF/BMP/WebP,视频支持 MP4/MOV,音频支持 MP3/WAV。 -- 默认单文件上限为图片 30 MiB、视频 200 MiB、音频 15 MiB;单请求总计 300 MiB,音频合计 15 MiB。 -- 默认单请求上传并发和全局素材工作并发均为 1;同一请求内相同类型、相同 URL 只上传一次。 -- 任一下载或上传失败会中止当前 credential 的提交;没有成功上传全部参考素材时不会调用该 credential 的 `submit_run`。 -- 生成结果也通过本服务代理;结果 URL 使用同一套公网目标与重定向校验,并支持 `Range` 下载。 - -如确实需要访问内网素材,可设置 `ALLOW_PRIVATE_REFERENCE_URLS=true`;只应在受信网络和受控调用方场景启用。 +| [集成指南](./docs/integrations.md) | stdio MCP、ChatGPT App、Codex plugin、工具和配置 | +| [架构设计](./docs/architecture.md) | 模块边界、依赖方向、Widget 与运行时 | +| [OpenCode plugin](./packages/opencode-plugin-pippit/README.md) | 安装、账号录入和生成流程 | +| [MCP server](./packages/mcp-server-pippit/README.md) | 本地与外部 Facade 模式 | +| [ChatGPT App](./apps/chatgpt-app/README.md) | Endpoint、配置和安全边界 | +| [持久化幂等](./docs/idempotency.md) | 视频任务异常恢复合同 | +| [AK 安全录入](./docs/opencode-ak-binding.md) | OpenCode 账号存储与安全约束 | +| [开发与发布工程](./docs/codex-plugin-dev-release-engineering.md) | Dev profile、contract gate、release 与 rollback | -## File store 部署边界 +## 支持项目 -默认 `BYOK_STORE_PATH=./data/byok-credentials.json` 使用本地 file store。它适用于单进程、单实例的本地 POSIX 部署,不支持多副本或 NFS/共享文件系统,也不是分布式凭证库。 +如果 Pippit Bridge 对你的创作或开发有所帮助,欢迎请我喝杯咖啡。你的支持会用于持续维护适配器、改进生成体验和完善文档。 -- 父目录必须由服务用户拥有且权限为 `0700` 或更严格;store 和 `${BYOK_STORE_PATH}.lock` 使用 `0600`。 -- 进程以排他方式创建 `.lock`。若启动提示锁不可用,应先确认没有其他 provider 进程使用该 store;只有确认是崩溃遗留的 stale lock 后才能人工删除。 -- 每次变更会以临时文件、`fsync`、原子 rename、目录 `fsync` 的方式写入完整 AES-256-GCM envelope。 -- AES-GCM 能校验机密性和完整性,但不能判断一个旧的、仍然有效的完整 store snapshot 是否被回滚。备份/快照的访问控制与版本新鲜度必须由部署系统负责。 -- update/delete 只保证当前逻辑 store 不再使用旧 AK。APFS/文件系统快照、备份、SSD wear leveling 可能仍保留旧 ciphertext;本服务不声称实现物理擦除。需要立即吊销时,以小云雀官方侧撤销 AK 为准。 - -容器部署必须把 `/app/data` 挂载到持久卷;同一卷同一时间只运行一个 provider 实例。`BYOK_ENCRYPTION_KEY_HEX`、`JOB_SIGNING_KEY_HEX`、Management/Facade 原始 Key 应由 secret manager 注入,不要写入镜像或提交到仓库。 - -## 配置 - -见 [.env.example](./.env.example)。常用项: - -| 变量 | 默认值 | 说明 | -| --- | --- | --- | -| `BYOK_ENCRYPTION_KEY_HEX` | 无 | 必填;32 个随机字节的 64 位小写 hex,用于加密 Pippit AK | -| `JOB_SIGNING_KEY_HEX` | 无 | 必填;另一把 32 字节随机密钥,用于 job token HMAC | -| `BYOK_MANAGEMENT_KEY_SHA256` | 无 | 必填;Management API Key 的小写 SHA-256 | -| `FACADE_API_KEY_SHA256_ALLOWLIST` | 无 | 必填;逗号分隔的 Facade API Key 小写 SHA-256 | -| `BYOK_STORE_PATH` | `./data/byok-credentials.json` | 加密 file store 路径;相对路径按服务工作目录解析 | -| `HOST` | `127.0.0.1` | 监听地址;容器端口映射时设置为 `0.0.0.0` | -| `PIPPIT_BASE_URL` | `https://xyq.jianying.com` | 文档中的小云雀 API origin,可按部署环境覆盖 | -| `PIPPIT_REQUEST_TIMEOUT_MS` | `43200000` | 上传、提交、查询超时(12 小时) | -| `IMAGE_GENERATION_POLL_INTERVAL_MS` | `3000` | 同步图片接口查询生成状态的轮询间隔(3 秒) | -| `IMAGE_GENERATION_TIMEOUT_MS` | `43200000` | 同步图片接口从提交到完成的总等待上限(12 小时) | -| `CONTENT_STREAM_IDLE_TIMEOUT_MS` | `43200000` | 生成结果流连续无数据的最大等待时间(12 小时) | -| `REFERENCE_FETCH_TIMEOUT_MS` | `43200000` | 单个参考素材下载超时(12 小时) | -| `REFERENCE_MAX_IMAGE_BYTES` | `31457280` | 单张图片最大 30 MiB | -| `REFERENCE_MAX_VIDEO_BYTES` | `209715200` | 单个视频最大 200 MiB | -| `REFERENCE_MAX_AUDIO_BYTES` | `15728640` | 单个音频与单请求音频合计最大 15 MiB | -| `REFERENCE_MAX_TOTAL_BYTES` | `314572800` | 单请求参考素材合计最大 300 MiB | -| `REFERENCE_MAX_REDIRECTS` | `3` | 参考 URL 最大重定向次数 | -| `REFERENCE_UPLOAD_CONCURRENCY` | `1` | 单请求素材上传并发数 | -| `REFERENCE_GLOBAL_CONCURRENCY` | `1` | 整个进程同时执行的素材下载+上传工作数 | -| `ALLOW_PRIVATE_REFERENCE_URLS` | `false` | 是否允许私网参考 URL | -| `PUBLIC_BASE_URL` | 空 | 设置后返回绝对 `polling_url` / `unsigned_urls` | -| `PIPPIT_BRIDGE_HOME` | 平台用户数据目录 | 本地自动 runtime 的高级/测试覆盖;不要指向 plugin cache 或项目目录 | -| `PIPPIT_LOCAL_RUNTIME_AUTO_START` | `true` | 设为 `false` 时禁止本地自动 runtime,要求显式外部 Facade | -| `PIPPIT_FACADE_BASE_URL` | 本地自动解析 | 外部模式的 facade origin;必须与 API key 成对设置 | -| `PIPPIT_FACADE_API_KEY` | 本地自动生成 | 外部模式的原始 Facade API Key,不是 digest 或 Pippit AK | -| `PIPPIT_FACADE_MANAGEMENT_API_KEY` | 本地自动生成 / 外部空 | stdio MCP / Codex 的 AK 管理 key;ChatGPT App 明确丢弃 | -| `PIPPIT_FACADE_TIMEOUT_MS` | `43200000` | wrapper 调用 facade 的超时(12 小时) | -| `PIPPIT_MCP_OUTPUT_ROOT` | macOS `~/Movies/Pippit`;其他平台 `~/Videos/Pippit` | stdio MCP / Codex 的 completed 图片、MP4 与额外视频副本 root;可覆盖 | -| `PIPPIT_MCP_ENROLLMENT_PORT` | `0` | stdio MCP / Codex 的 loopback AK 设置页端口;`0` 表示随机空闲端口 | -| `PIPPIT_MCP_ENROLLMENT_TTL_MS` | `300000` | stdio MCP / Codex 的单次 AK 设置链接有效期,最大 15 分钟 | -| `CHATGPT_APP_HOST` / `CHATGPT_APP_PORT` | `127.0.0.1` / `8787` | ChatGPT App Streamable HTTP 监听地址;当前 `noauth` 封装只允许 loopback | -| `CHATGPT_APP_PUBLIC_BASE_URL` | 空 | 用于签名媒体预览的公开 HTTPS origin;必须与签名 key 同时设置 | -| `CHATGPT_APP_MEDIA_SIGNING_KEY_HEX` | 空 | 独立 32-byte 签名 key;必须与 public base URL 同时设置 | -| `CHATGPT_APP_MEDIA_TTL_SECONDS` | `300` | 签名预览 URL 有效期,允许 `30`–`900` 秒 | - -Codex/MCP 的 completed 图片和视频会先原子保存为 `PIPPIT_MCP_OUTPUT_ROOT` 下的普通本地文件,再返回 widget。默认目录在 macOS 是 `~/Movies/Pippit`,其他平台是 `~/Videos/Pippit`;不会写入 `/tmp`、仓库或 plugin cache。图片 widget 读取完整本地图片,视频 widget 分块读取本地 MP4,并分别创建沙箱内 `blob:` 预览地址;不会把远程签名地址、`file://` 路径或普通 HTTP loopback 地址交给预览器。stdio 重启后仍可按稳定 artifact identity 重新读取,本地文件继续保留。`pippit_download_video` 只用于创建另一个用户指定文件名或路径的视频副本。 - -生成、参考素材准备、重新生成、结果查询与落盘链路的内部默认超时统一为 12 小时。Codex plugin 还在 `.mcp.json` 中把 MCP tool timeout 设置为 12 小时;其他 MCP/ChatGPT 宿主若另有更短的外层调用期限,仍需在宿主侧同步配置。生成任务本身保持异步:工具尽快返回 pending job,widget 自动轮询。点击 `Regenerate video` 后,widget 会立即显示 loading,并请求从全屏编辑视图回到 inline 对话视图;宿主不支持该显示模式时会留在当前视图继续展示 loading。 - -## 验证 - -```bash -npm run check -``` - -默认测试使用内存 BYOK store 和注入的 Pippit fake,覆盖管理/运行时认证隔离、加密持久化、credential 选择与轮换、素材上传先于生成、图片/视频/音频映射、状态映射、job token 隔离、内容代理、超时、大小限制与私网 URL 拒绝。真实 Pippit AK 的上游验收需要单独执行,可能产生生成费用,不会混入默认测试。 - -## 协议依据 - -- [OpenRouter BYOK overview](https://openrouter.ai/docs/guides/overview/auth/byok) -- [OpenRouter Management API Keys](https://openrouter.ai/docs/guides/overview/auth/management-api-keys) -- OpenRouter BYOK CRUD:[create](https://openrouter.ai/docs/api/api-reference/byok/create-byok-key)、[list](https://openrouter.ai/docs/api/api-reference/byok/list-byok-keys)、[get](https://openrouter.ai/docs/api/api-reference/byok/get-byok-key)、[update](https://openrouter.ai/docs/api/api-reference/byok/update-byok-key)、[delete](https://openrouter.ai/docs/api/api-reference/byok/delete-byok-key) -- [OpenRouter Video Generation](https://openrouter.ai/docs/guides/overview/multimodal/video-generation) -- [OpenRouter Image Generation](https://openrouter.ai/docs/guides/overview/multimodal/image-generation) -- [OpenRouter Video API Reference](https://openrouter.ai/docs/api/api-reference/video-generation/create-videos) -- [OpenAI Apps SDK: Build your MCP server](https://developers.openai.com/apps-sdk/build/mcp-server) -- [OpenAI Apps SDK: Connect from ChatGPT](https://developers.openai.com/apps-sdk/deploy/connect-chatgpt) -- [OpenAI Apps SDK: Authentication](https://developers.openai.com/apps-sdk/build/auth) -- [OpenAI: Build plugins](https://developers.openai.com/codex/build-plugins) -- [小云雀(Pippit)](https://xyq.jianying.com/) - -更细的边界和状态映射见 [docs/architecture.md](./docs/architecture.md)。 +

+ Pippit Bridge 赞赏码:Buy Me a Coffee +

## License diff --git a/assets/buy-me-a-coffee.jpg b/assets/buy-me-a-coffee.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ace7d09e3c3986857bea9fce5774a015d391c680 GIT binary patch literal 110625 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 Date: Tue, 28 Jul 2026 20:28:36 +0800 Subject: [PATCH 2/2] docs: add English and Chinese readmes --- README.md | 136 +++++++++++++++++----------------- README.zh-CN.md | 193 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 263 insertions(+), 66 deletions(-) create mode 100644 README.zh-CN.md diff --git a/README.md b/README.md index 3900adc..ba1567d 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,46 @@ # Pippit Bridge

- Pippit Bridge 飞鸟 Logo + English · 简体中文

- 让 Codex、OpenCode、MCP 与 ChatGPT 安全调用小云雀的图片和视频生成能力。 + Pippit Bridge bird logo +

+ +

+ Bring Pippit image and video generation safely to Codex, OpenCode, MCP, and ChatGPT.
- 本地优先 · 多账号 BYOK · 参考素材 · 结果预览与落盘 + Local-first · Multi-account BYOK · Reference media · Preview and persistent outputs

- 快速安装 · - 选择接入方式 · - 首次使用 · - 文档导航 + Quick install · + Choose an integration · + First use · + Documentation

-Pippit Bridge 是小云雀(Pippit)的开源 API gateway 与 adapter monorepo。它把图片和视频生成、参考素材上传、任务查询、结果下载与账号管理封装成 agent 可以安全调用的工具,同时提供 OpenRouter 风格的 facade 和 TypeScript SDK。 +Pippit Bridge is an open-source API gateway and adapter monorepo for Pippit (XiaoYunque). It turns image and video generation, reference uploads, job polling, downloads, and account management into agent-safe tools, while also providing an OpenRouter-style facade and a TypeScript SDK. -## 功能亮点 +## Highlights -- **图片与视频生成**:支持文生图、参考图生图、文生视频,以及图片、视频、音频参考素材。 -- **视频工作流**:支持首尾帧、异步任务查询、结果预览与参考视频片段重拍。 -- **安全的多账号 BYOK**:通过一次性 loopback 页面录入和切换 Pippit AK,原始密钥不进入聊天或项目配置。 -- **本地优先**:Codex、stdio MCP 与 OpenCode 默认在受信主机上运行,生成结果持久化到用户目录。 -- **多种接入面**:同一套能力可用于 Codex、OpenCode、通用 MCP、ChatGPT App 和 OpenRouter 风格 API。 +- **Image and video generation:** text-to-image, reference-image generation, text-to-video, and image, video, or audio references. +- **Video workflows:** first and last frames, asynchronous job polling, result previews, and native video segment regeneration. +- **Secure multi-account BYOK:** add and switch Pippit access keys through a one-time loopback page without exposing raw keys to chat or project configuration. +- **Local-first:** Codex, stdio MCP, and OpenCode run on a trusted host by default and persist generated results in a user directory. +- **Multiple integrations:** use the same capabilities through Codex, OpenCode, generic MCP, a ChatGPT App, or an OpenRouter-style API. > [!IMPORTANT] -> Pippit Bridge 当前聚焦图片与视频生成。音频可作为视频参考素材,但尚未提供通用文本、语音生成或转录工具。 +> Pippit Bridge currently focuses on image and video generation. Audio can be used as video reference material, but general text generation, speech generation, and transcription are not provided. -## 快速安装 +## Quick install -运行 Codex plugin 或完整 monorepo 需要 Node.js 22.22.2+、24.15.0+ 或 26+,并确保 `npm` / `npx` 可用。 +The Codex plugin and full monorepo require Node.js 22.22.2+, 24.15.0+, or 26+, with `npm` and `npx` available. -### Codex(推荐) +### Codex (recommended) -从公开 GitHub marketplace 安装,无需克隆本仓库: +Install from the public GitHub marketplace without cloning this repository: ```bash codex plugin marketplace add superche/pippit-bridge --ref main --json @@ -44,17 +48,17 @@ codex plugin add pippit-video@pippit-bridge --json codex plugin list --json ``` -安装完成后重启 ChatGPT Desktop 或新建 Codex session。 +Restart ChatGPT Desktop or start a new Codex session after installation. ### OpenCode -从公开 npm registry 全局安装: +Install the plugin globally from the public npm registry: ```bash opencode plugin @pippit-bridge/opencode-plugin --global ``` -也可以写入全局或项目级 `opencode.json`: +Alternatively, add it to a global or project-level `opencode.json`: ```json { @@ -63,41 +67,41 @@ opencode plugin @pippit-bridge/opencode-plugin --global } ``` -## 选择接入方式 +## Choose an integration -| 接入方式 | 适合场景 | 入口 | +| Integration | Best for | Entry point | | --- | --- | --- | -| **Codex plugin** | 在 Codex CLI 或 ChatGPT Desktop 中直接生成和预览素材 | `pippit-video@pippit-bridge` | -| **OpenCode plugin** | 在 OpenCode 中使用图片、视频和账号工具 | `@pippit-bridge/opencode-plugin` | -| **通用 MCP** | 其他支持 stdio MCP 的本地 agent/client | `@pippit-bridge/mcp-server` | -| **ChatGPT App** | ChatGPT developer mode 或 HTTPS 部署 | `@pippit-bridge/chatgpt-app` | -| **OpenRouter facade** | HTTP API、服务器持久化 BYOK 或自定义 adapter | `apps/openrouter-facade` | +| **Codex plugin** | Generating and previewing media in Codex CLI or ChatGPT Desktop | `pippit-video@pippit-bridge` | +| **OpenCode plugin** | Image, video, and account tools in OpenCode | `@pippit-bridge/opencode-plugin` | +| **Generic MCP** | Other local agents and clients that support stdio MCP | `@pippit-bridge/mcp-server` | +| **ChatGPT App** | ChatGPT developer mode or HTTPS deployments | `@pippit-bridge/chatgpt-app` | +| **OpenRouter facade** | HTTP APIs, server-persisted BYOK, or custom adapters | `apps/openrouter-facade` | -完整配置和部署边界见 [MCP、ChatGPT App 与 Codex plugin 接入指南](./docs/integrations.md)。 +See the [MCP, ChatGPT App, and Codex plugin integration guide](./docs/integrations.md) for full configuration and deployment boundaries. -## 首次使用 +## First use -1. 在[小云雀官网](https://xyq.jianying.com/)签发并复制 Pippit AK。 -2. Codex/stdio MCP 调用 `pippit_add_access_key`;OpenCode 调用 `pippit_manage_access_keys` 的 `configure` 操作。 -3. 在工具返回的短时本地页面中粘贴 AK。不要把 AK 发送到聊天、普通工具参数、URL query 或项目配置。 -4. 直接用自然语言请求 agent,例如“用这张参考图生成一张 16:9 海报”或“生成一段 10 秒的视频”。 +1. Create and copy a Pippit access key from the [official Pippit site](https://xyq.jianying.com/). +2. In Codex or stdio MCP, call `pippit_add_access_key`. In OpenCode, use the `configure` action of `pippit_manage_access_keys`. +3. Paste the key into the short-lived local page returned by the tool. Never send it through chat, ordinary tool arguments, URL queries, or project configuration. +4. Ask the agent naturally, for example: “Create a 16:9 poster from this reference image” or “Generate a 10-second video.” -主要能力: +Core capabilities: -| 能力 | 工具 | +| Capability | Tools | | --- | --- | -| 模型发现 | `pippit_list_image_models`、`pippit_list_video_models` | -| 图片生成 | `pippit_generate_image` | -| 视频生成与查询 | `pippit_generate_video`、`pippit_get_video` | -| 视频片段重拍 | `pippit_edit_video_segment` | -| 本地视频副本 | `pippit_download_video` | -| Codex/MCP 账号管理 | `pippit_add_access_key`、`pippit_list_access_keys`、`pippit_switch_access_key`、`pippit_delete_access_key` | +| Model discovery | `pippit_list_image_models`, `pippit_list_video_models` | +| Image generation | `pippit_generate_image` | +| Video generation and polling | `pippit_generate_video`, `pippit_get_video` | +| Video segment regeneration | `pippit_edit_video_segment` | +| Additional local video copy | `pippit_download_video` | +| Codex/MCP account management | `pippit_add_access_key`, `pippit_list_access_keys`, `pippit_switch_access_key`, `pippit_delete_access_key` | -Codex/MCP 完成的图片和视频默认保存到 macOS `~/Movies/Pippit` 或其他平台 `~/Videos/Pippit`,随后通过结果卡预览。更多工具、文件上传和 ChatGPT App 差异见[接入指南](./docs/integrations.md)。 +Completed Codex/MCP images and videos are saved to `~/Movies/Pippit` on macOS or `~/Videos/Pippit` on other platforms, then displayed in a result card. See the [integration guide](./docs/integrations.md) for more tools, file uploads, and ChatGPT App differences. -## 项目架构 +## Architecture -这是面向**单个本地用户、单台受信主机**的 plugin bridge,不是多租户 SaaS。多用户 OAuth、租户隔离、横向扩容和跨机器状态同步不属于当前目标。 +This is a plugin bridge for **one local user on one trusted host**, not a multi-tenant SaaS. Multi-user OAuth, tenant isolation, horizontal scaling, and cross-machine state synchronization are outside the current scope. ```text Codex / MCP / ChatGPT @@ -126,9 +130,9 @@ pippit-bridge └── docs ``` -`contracts` 是公共 schema 真源,`core` 提供模型目录与安全文件原语,`sdk` 封装小云雀上游 API。完整依赖方向、Widget、运行时和发布边界见[架构文档](./docs/architecture.md)。 +`contracts` is the source of truth for public schemas, `core` provides the model catalog and secure file primitives, and `sdk` wraps the upstream Pippit API. See the [architecture guide](./docs/architecture.md) for dependency direction, widgets, runtime behavior, and release boundaries. -## 本地开发 +## Local development ```bash npm ci @@ -137,16 +141,16 @@ npm run dev:mcp # stdio MCP npm run dev:chatgpt-app # http://127.0.0.1:8787/mcp ``` -Facade 默认监听 `http://127.0.0.1:3000`。配置项以 [.env.example](./.env.example) 为准,HTTP 合同以 [OpenAPI golden](./apps/openrouter-facade/contracts/openapi.golden.json) 为准。 +The facade listens on `http://127.0.0.1:3000` by default. [.env.example](./.env.example) is the configuration source of truth, and the [OpenAPI golden](./apps/openrouter-facade/contracts/openapi.golden.json) defines the HTTP contract. -连接显式外部 Facade 时,下面两个变量必须成对设置: +When connecting to an explicit external facade, both variables must be set together: ```bash export PIPPIT_FACADE_BASE_URL=http://127.0.0.1:3000 export PIPPIT_FACADE_API_KEY='' ``` -Codex plugin 开发使用独立 profile: +Use an isolated profile for Codex plugin development: ```bash npm run codex:dev:profile:setup @@ -155,33 +159,33 @@ npm run codex:dev:app npm run codex:dev:full-gate ``` -运行完整检查: +Run the complete local check: ```bash npm run check ``` -正式发布、hot/cold contract 和回滚流程见 [Codex Plugin 开发与发布工程](./docs/codex-plugin-dev-release-engineering.md)。 +See [Codex Plugin development and release engineering](./docs/codex-plugin-dev-release-engineering.md) for release gates, hot/cold contracts, and rollback procedures. -## 文档导航 +## Documentation -| 文档 | 内容 | +| Document | Contents | | --- | --- | -| [集成指南](./docs/integrations.md) | stdio MCP、ChatGPT App、Codex plugin、工具和配置 | -| [架构设计](./docs/architecture.md) | 模块边界、依赖方向、Widget 与运行时 | -| [OpenCode plugin](./packages/opencode-plugin-pippit/README.md) | 安装、账号录入和生成流程 | -| [MCP server](./packages/mcp-server-pippit/README.md) | 本地与外部 Facade 模式 | -| [ChatGPT App](./apps/chatgpt-app/README.md) | Endpoint、配置和安全边界 | -| [持久化幂等](./docs/idempotency.md) | 视频任务异常恢复合同 | -| [AK 安全录入](./docs/opencode-ak-binding.md) | OpenCode 账号存储与安全约束 | -| [开发与发布工程](./docs/codex-plugin-dev-release-engineering.md) | Dev profile、contract gate、release 与 rollback | +| [Integration guide](./docs/integrations.md) | stdio MCP, ChatGPT App, Codex plugin, tools, and configuration | +| [Architecture](./docs/architecture.md) | Module boundaries, dependency direction, widgets, and runtime | +| [OpenCode plugin](./packages/opencode-plugin-pippit/README.md) | Installation, account enrollment, and generation | +| [MCP server](./packages/mcp-server-pippit/README.md) | Local and external facade modes | +| [ChatGPT App](./apps/chatgpt-app/README.md) | Endpoints, configuration, and security boundaries | +| [Durable idempotency](./docs/idempotency.md) | Recovery contract for video jobs | +| [Secure key enrollment](./docs/opencode-ak-binding.md) | OpenCode account storage and security constraints | +| [Development and release engineering](./docs/codex-plugin-dev-release-engineering.md) | Dev profiles, contract gates, releases, and rollback | -## 支持项目 +## Support the project -如果 Pippit Bridge 对你的创作或开发有所帮助,欢迎请我喝杯咖啡。你的支持会用于持续维护适配器、改进生成体验和完善文档。 +If Pippit Bridge helps your creative or development workflow, consider buying me a coffee. Your support helps maintain the adapters, improve the generation experience, and keep the documentation current.

- Pippit Bridge 赞赏码:Buy Me a Coffee + Buy Me a Coffee QR code for Pippit Bridge

## License diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..1099197 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,193 @@ +# Pippit Bridge + +

+ 简体中文 · English +

+ +

+ Pippit Bridge 飞鸟 Logo +

+ +

+ 让 Codex、OpenCode、MCP 与 ChatGPT 安全调用小云雀的图片和视频生成能力。 +
+ 本地优先 · 多账号 BYOK · 参考素材 · 结果预览与落盘 +

+ +

+ 快速安装 · + 选择接入方式 · + 首次使用 · + 文档导航 +

+ +Pippit Bridge 是小云雀(Pippit)的开源 API gateway 与 adapter monorepo。它把图片和视频生成、参考素材上传、任务查询、结果下载与账号管理封装成 agent 可以安全调用的工具,同时提供 OpenRouter 风格的 facade 和 TypeScript SDK。 + +## 功能亮点 + +- **图片与视频生成**:支持文生图、参考图生图、文生视频,以及图片、视频、音频参考素材。 +- **视频工作流**:支持首尾帧、异步任务查询、结果预览与参考视频片段重拍。 +- **安全的多账号 BYOK**:通过一次性 loopback 页面录入和切换 Pippit AK,原始密钥不进入聊天或项目配置。 +- **本地优先**:Codex、stdio MCP 与 OpenCode 默认在受信主机上运行,生成结果持久化到用户目录。 +- **多种接入面**:同一套能力可用于 Codex、OpenCode、通用 MCP、ChatGPT App 和 OpenRouter 风格 API。 + +> [!IMPORTANT] +> Pippit Bridge 当前聚焦图片与视频生成。音频可作为视频参考素材,但尚未提供通用文本、语音生成或转录工具。 + +## 快速安装 + +运行 Codex plugin 或完整 monorepo 需要 Node.js 22.22.2+、24.15.0+ 或 26+,并确保 `npm` / `npx` 可用。 + +### Codex(推荐) + +从公开 GitHub marketplace 安装,无需克隆本仓库: + +```bash +codex plugin marketplace add superche/pippit-bridge --ref main --json +codex plugin add pippit-video@pippit-bridge --json +codex plugin list --json +``` + +安装完成后重启 ChatGPT Desktop 或新建 Codex session。 + +### OpenCode + +从公开 npm registry 全局安装: + +```bash +opencode plugin @pippit-bridge/opencode-plugin --global +``` + +也可以写入全局或项目级 `opencode.json`: + +```json +{ + "$schema": "https://opencode.ai/config.json", + "plugin": ["@pippit-bridge/opencode-plugin"] +} +``` + +## 选择接入方式 + +| 接入方式 | 适合场景 | 入口 | +| --- | --- | --- | +| **Codex plugin** | 在 Codex CLI 或 ChatGPT Desktop 中直接生成和预览素材 | `pippit-video@pippit-bridge` | +| **OpenCode plugin** | 在 OpenCode 中使用图片、视频和账号工具 | `@pippit-bridge/opencode-plugin` | +| **通用 MCP** | 其他支持 stdio MCP 的本地 agent/client | `@pippit-bridge/mcp-server` | +| **ChatGPT App** | ChatGPT developer mode 或 HTTPS 部署 | `@pippit-bridge/chatgpt-app` | +| **OpenRouter facade** | HTTP API、服务器持久化 BYOK 或自定义 adapter | `apps/openrouter-facade` | + +完整配置和部署边界见 [MCP、ChatGPT App 与 Codex plugin 接入指南](./docs/integrations.md)。 + +## 首次使用 + +1. 在[小云雀官网](https://xyq.jianying.com/)签发并复制 Pippit AK。 +2. Codex/stdio MCP 调用 `pippit_add_access_key`;OpenCode 调用 `pippit_manage_access_keys` 的 `configure` 操作。 +3. 在工具返回的短时本地页面中粘贴 AK。不要把 AK 发送到聊天、普通工具参数、URL query 或项目配置。 +4. 直接用自然语言请求 agent,例如“用这张参考图生成一张 16:9 海报”或“生成一段 10 秒的视频”。 + +主要能力: + +| 能力 | 工具 | +| --- | --- | +| 模型发现 | `pippit_list_image_models`、`pippit_list_video_models` | +| 图片生成 | `pippit_generate_image` | +| 视频生成与查询 | `pippit_generate_video`、`pippit_get_video` | +| 视频片段重拍 | `pippit_edit_video_segment` | +| 本地视频副本 | `pippit_download_video` | +| Codex/MCP 账号管理 | `pippit_add_access_key`、`pippit_list_access_keys`、`pippit_switch_access_key`、`pippit_delete_access_key` | + +Codex/MCP 完成的图片和视频默认保存到 macOS `~/Movies/Pippit` 或其他平台 `~/Videos/Pippit`,随后通过结果卡预览。更多工具、文件上传和 ChatGPT App 差异见[接入指南](./docs/integrations.md)。 + +## 项目架构 + +这是面向**单个本地用户、单台受信主机**的 plugin bridge,不是多租户 SaaS。多用户 OAuth、租户隔离、横向扩容和跨机器状态同步不属于当前目标。 + +```text +Codex / MCP / ChatGPT + -> authenticated OpenRouter-style Facade + -> encrypted BYOK store + -> Pippit SDK + -> Pippit upstream + +OpenCode + -> Core + Pippit SDK + -> Pippit upstream +``` + +```text +pippit-bridge +├── apps +│ ├── openrouter-facade +│ └── chatgpt-app +├── packages +│ ├── contracts +│ ├── core +│ ├── sdk +│ ├── mcp-server-pippit +│ └── opencode-plugin-pippit +├── .agents/plugins/marketplace.json +└── docs +``` + +`contracts` 是公共 schema 真源,`core` 提供模型目录与安全文件原语,`sdk` 封装小云雀上游 API。完整依赖方向、Widget、运行时和发布边界见[架构文档](./docs/architecture.md)。 + +## 本地开发 + +```bash +npm ci +npm run dev # OpenRouter facade +npm run dev:mcp # stdio MCP +npm run dev:chatgpt-app # http://127.0.0.1:8787/mcp +``` + +Facade 默认监听 `http://127.0.0.1:3000`。配置项以 [.env.example](./.env.example) 为准,HTTP 合同以 [OpenAPI golden](./apps/openrouter-facade/contracts/openapi.golden.json) 为准。 + +连接显式外部 Facade 时,下面两个变量必须成对设置: + +```bash +export PIPPIT_FACADE_BASE_URL=http://127.0.0.1:3000 +export PIPPIT_FACADE_API_KEY='' +``` + +Codex plugin 开发使用独立 profile: + +```bash +npm run codex:dev:profile:setup +npm run codex:dev +npm run codex:dev:app +npm run codex:dev:full-gate +``` + +运行完整检查: + +```bash +npm run check +``` + +正式发布、hot/cold contract 和回滚流程见 [Codex Plugin 开发与发布工程](./docs/codex-plugin-dev-release-engineering.md)。 + +## 文档导航 + +| 文档 | 内容 | +| --- | --- | +| [集成指南](./docs/integrations.md) | stdio MCP、ChatGPT App、Codex plugin、工具和配置 | +| [架构设计](./docs/architecture.md) | 模块边界、依赖方向、Widget 与运行时 | +| [OpenCode plugin](./packages/opencode-plugin-pippit/README.md) | 安装、账号录入和生成流程 | +| [MCP server](./packages/mcp-server-pippit/README.md) | 本地与外部 Facade 模式 | +| [ChatGPT App](./apps/chatgpt-app/README.md) | Endpoint、配置和安全边界 | +| [持久化幂等](./docs/idempotency.md) | 视频任务异常恢复合同 | +| [AK 安全录入](./docs/opencode-ak-binding.md) | OpenCode 账号存储与安全约束 | +| [开发与发布工程](./docs/codex-plugin-dev-release-engineering.md) | Dev profile、contract gate、release 与 rollback | + +## 支持项目 + +如果 Pippit Bridge 对你的创作或开发有所帮助,欢迎请我喝杯咖啡。你的支持会用于持续维护适配器、改进生成体验和完善文档。 + +

+ Pippit Bridge 赞赏码:Buy Me a Coffee +

+ +## License + +[MIT](./LICENSE) © 2026 superche