Raycast 里自带了 AI,可以用到不少模型。网上有两个很好用的项目,能把它转成 OpenAI 兼容的 API,这样其他的客户端也可以直接用。一个是 raycast-relay,另一个是 missuo 的 raycast2api,我之前一直在用它们。但是 Raycast 更新之后,这两个项目都用不了了。原因是新版的请求里多了一个签名,没有它,请求就过不了。其中 raycast-relay 已经归档,不再维护了,所以我干脆自己来做,把新的签名逆向出来,重新写了一个,名字就沿用 raycast2api,仓库在 xxxbrian/raycast2api。它做了什么整个项目其实只做一件事,就是把 OpenAI 格式的请求翻译成 Raycast 的请求,再把 Raycast 的响应翻译回来。对外只有几个接口。接口作用GET /v1/models模型列表POST /v1/chat/completions对话,支持流式和非流式GET /health存活检查GET /ready就绪检查模型列表是每次都向 Raycast 去拉的,所以 Raycast 那边新增了模型,这里不用改任何东西。拉回来之后,我会按环境变量过滤一遍,ADVANCED 控制要不要包含高级模型,INCLUDE_DEPRECATED 控制要不要包含已经弃用的模型。对话的转换也不复杂。OpenAI 消息列表开头的 system 消息,会变成 Raycast 的 system_instruction,user 和 assistant 的消息则换成 Raycast 的 author 加 content.text 的格式。请求的模型 ID 会去模型列表里查出对应的 provider 和内部模型名,每次请求还会生成一个新的 thread_id。Raycast 返回的是 SSE,每一行 data: 里带着一小段文本和结束标记。流式的时候,我把每一段重新包装成 OpenAI 的 chat.completion.chunk 发出去,最后补上 [DONE]。非流式的时候,则是把所有的文本拼起来,一次性返回。因为 Raycast 不会给 token 的用量,所以非流式响应中的 usage 暂时填 0,流式响应则不提供用量统计。V2 签名新版 Raycast 的请求里,除了原来的 Authorization 和 X-Raycast-DeviceId,还多了 X-Raycast-Timestamp 和 X-Raycast-Signature-v2。前面几个都好办,难的只有最后这个签名要自己算出来。签名的过程其实就是几步。首先对请求体做一次 SHA-256,然后把时间戳、设备 ID 和这个哈希值,各自做一遍 ROT13 加 ROT5 的编码,也就是字母按 ROT13 转换,数字按 ROT5 转换,其他字符保持不变。接着把三段用 . 连起来,最后用一个密钥做 HMAC-SHA256,得到的十六进制结果就是签名。因为时间戳和设备 ID 都参与了签名,所以每次请求的签名都不一样,设备 ID 也和请求绑定在了一起。密钥在项目里有一个默认值,如果以后换了,也可以通过 RAYCAST_SIGNATURE_SECRET 这个环境变量覆盖。这里全部用的是 Web Crypto 的接口,所以同一份代码在 Workers 和 Bun 里都能跑,不需要额外的依赖。一份代码,三种部署我想让这个项目尽量容易部署。有的人习惯用 Cloudflare Workers,有的人更喜欢 Docker,还有的人只想在自己的电脑上跑一下,所以我希望这三种方式都能支持。幸好 Hono 本身就是跨运行时的框架,同一个 app 既可以直接作为 Worker 的入口,也可以交给 Bun.serve。环境变量也是通过 Hono 的 env() 去读,不管在哪个运行时里,写法都是一样的。Cloudflare Workers入口就是 src/index.ts,wrangler.jsonc 里只写了名字和入口文件。README 里放了一个 Deploy to Cloudflare Workers 的按钮,点一下就能部署,再去后台把环境变量填上。Dockerserver.ts 只有几行,它把 Hono 的 app.fetch 交给 Bun.serve,端口默认是 3000。Dockerfile 分成两个阶段,第一个阶段用 bun build --compile 把它编译成一个独立的可执行文件,第二个阶段把这个文件拷进 debian:bookworm-slim,所以最后的镜像里只有一个二进制。BASH复制docker run -d \ --name raycast2api \ -p 3000:3000 \ -e RAYCAST_BEARER_TOKEN=your-raycast-bearer-token \ -e RAYCAST_DEVICE_ID=your-raycast-device-id \ -e API_KEY=your-api-key \ ghcr.io/xxxbrian/raycast2api:latest镜像是在发布 Release 的时候,由 GitHub Actions 自动构建并推到 ghcr.io 的,同时支持 linux/amd64 和 linux/arm64。如果放在 Kubernetes 里,就可以把 /health 当作存活探针,把 /ready 当作就绪探针。前者只检查配置是不是齐全,后者除了检查配置,还会看最近对 Raycast 的请求是否正常;不正常的时候,会去试着拉一次模型列表,确认 Raycast 那边是不是通的。这个检查最多每 30 秒做一次。Note这一版健康探针会直接检查 RAYCAST_SIGNATURE_SECRET 是否已配置。即使签名逻辑本身有默认值,使用探针时也要显式设置这一项。二进制Docker 里用的那条编译命令,换一个 --target 就能编译出不同平台的版本。所以发布 Release 的时候,Actions 会把 Linux、macOS、Windows 乘以 x64、arm64 都编译一遍,其中 Windows 的 arm64 没有做,然后把结果上传到 Release 里。下载下来直接运行就行,不需要装任何运行时。凭证运行之前需要两个值,也就是 RAYCAST_BEARER_TOKEN 和 RAYCAST_DEVICE_ID。它们都可以从 Raycast 自己发出的请求里拿到:用 Proxyman 或者 Charles 这样的抓包工具,给 backend.raycast.com 开启 SSL 代理,然后在 Raycast 里随便发一条 AI 的对话,就能在请求头里看到 Authorization: Bearer ... 和 X-Raycast-DeviceId。Warning这两个值等于是你自己的 Raycast 账号,千万别泄露。如果没有设置 API_KEY,部署出来的接口就是对所有人开放的,别人也能拿你的账号去用。建议一定要设置;客户端那边用它当作 OpenAI 的 API Key 就行。使用部署好之后,在任何支持自定义 OpenAI 地址的客户端里,把 Base URL 填成部署的地址,API Key 填成自己设置的 API_KEY,就可以直接选模型对话了。
Raycast 里自带了 AI,可以用到不少模型。网上有两个很好用的项目,能把它转成 OpenAI 兼容的 API,这样其他的客户端也可以直接用。一个是 raycast-relay,另一个是 missuo 的 raycast2api,我之前一直在用它们。
但是 Raycast 更新之后,这两个项目都用不了了。原因是新版的请求里多了一个签名,没有它,请求就过不了。其中 raycast-relay 已经归档,不再维护了,所以我干脆自己来做,把新的签名逆向出来,重新写了一个,名字就沿用 raycast2api,仓库在 xxxbrian/raycast2api。
它做了什么
整个项目其实只做一件事,就是把 OpenAI 格式的请求翻译成 Raycast 的请求,再把 Raycast 的响应翻译回来。对外只有几个接口。
接口
作用
GET /v1/models
模型列表
POST /v1/chat/completions
对话,支持流式和非流式
GET /health
存活检查
GET /ready
就绪检查
模型列表是每次都向 Raycast 去拉的,所以 Raycast 那边新增了模型,这里不用改任何东西。拉回来之后,我会按环境变量过滤一遍,ADVANCED 控制要不要包含高级模型,INCLUDE_DEPRECATED 控制要不要包含已经弃用的模型。
对话的转换也不复杂。OpenAI 消息列表开头的 system 消息,会变成 Raycast 的 system_instruction,user 和 assistant 的消息则换成 Raycast 的 author 加 content.text 的格式。请求的模型 ID 会去模型列表里查出对应的 provider 和内部模型名,每次请求还会生成一个新的 thread_id。
Raycast 返回的是 SSE,每一行 data: 里带着一小段文本和结束标记。流式的时候,我把每一段重新包装成 OpenAI 的 chat.completion.chunk 发出去,最后补上 [DONE]。非流式的时候,则是把所有的文本拼起来,一次性返回。因为 Raycast 不会给 token 的用量,所以非流式响应中的 usage 暂时填 0,流式响应则不提供用量统计。
V2 签名
新版 Raycast 的请求里,除了原来的 Authorization 和 X-Raycast-DeviceId,还多了 X-Raycast-Timestamp 和 X-Raycast-Signature-v2。前面几个都好办,难的只有最后这个签名要自己算出来。
签名的过程其实就是几步。首先对请求体做一次 SHA-256,然后把时间戳、设备 ID 和这个哈希值,各自做一遍 ROT13 加 ROT5 的编码,也就是字母按 ROT13 转换,数字按 ROT5 转换,其他字符保持不变。接着把三段用 . 连起来,最后用一个密钥做 HMAC-SHA256,得到的十六进制结果就是签名。
因为时间戳和设备 ID 都参与了签名,所以每次请求的签名都不一样,设备 ID 也和请求绑定在了一起。密钥在项目里有一个默认值,如果以后换了,也可以通过 RAYCAST_SIGNATURE_SECRET 这个环境变量覆盖。
这里全部用的是 Web Crypto 的接口,所以同一份代码在 Workers 和 Bun 里都能跑,不需要额外的依赖。
一份代码,三种部署
我想让这个项目尽量容易部署。有的人习惯用 Cloudflare Workers,有的人更喜欢 Docker,还有的人只想在自己的电脑上跑一下,所以我希望这三种方式都能支持。
幸好 Hono 本身就是跨运行时的框架,同一个 app 既可以直接作为 Worker 的入口,也可以交给 Bun.serve。环境变量也是通过 Hono 的 env() 去读,不管在哪个运行时里,写法都是一样的。
Cloudflare Workers
入口就是 src/index.ts,wrangler.jsonc 里只写了名字和入口文件。README 里放了一个 Deploy to Cloudflare Workers 的按钮,点一下就能部署,再去后台把环境变量填上。
Docker
server.ts 只有几行,它把 Hono 的 app.fetch 交给 Bun.serve,端口默认是 3000。Dockerfile 分成两个阶段,第一个阶段用 bun build --compile 把它编译成一个独立的可执行文件,第二个阶段把这个文件拷进 debian:bookworm-slim,所以最后的镜像里只有一个二进制。
镜像是在发布 Release 的时候,由 GitHub Actions 自动构建并推到 ghcr.io 的,同时支持 linux/amd64 和 linux/arm64。如果放在 Kubernetes 里,就可以把 /health 当作存活探针,把 /ready 当作就绪探针。前者只检查配置是不是齐全,后者除了检查配置,还会看最近对 Raycast 的请求是否正常;不正常的时候,会去试着拉一次模型列表,确认 Raycast 那边是不是通的。这个检查最多每 30 秒做一次。
这一版健康探针会直接检查 RAYCAST_SIGNATURE_SECRET 是否已配置。即使签名逻辑本身有默认值,使用探针时也要显式设置这一项。
二进制
Docker 里用的那条编译命令,换一个 --target 就能编译出不同平台的版本。所以发布 Release 的时候,Actions 会把 Linux、macOS、Windows 乘以 x64、arm64 都编译一遍,其中 Windows 的 arm64 没有做,然后把结果上传到 Release 里。下载下来直接运行就行,不需要装任何运行时。
凭证
运行之前需要两个值,也就是 RAYCAST_BEARER_TOKEN 和 RAYCAST_DEVICE_ID。它们都可以从 Raycast 自己发出的请求里拿到:用 Proxyman 或者 Charles 这样的抓包工具,给 backend.raycast.com 开启 SSL 代理,然后在 Raycast 里随便发一条 AI 的对话,就能在请求头里看到 Authorization: Bearer ... 和 X-Raycast-DeviceId。
这两个值等于是你自己的 Raycast 账号,千万别泄露。如果没有设置 API_KEY,部署出来的接口就是对所有人开放的,别人也能拿你的账号去用。建议一定要设置;客户端那边用它当作 OpenAI 的 API Key 就行。
使用
部署好之后,在任何支持自定义 OpenAI 地址的客户端里,把 Base URL 填成部署的地址,API Key 填成自己设置的 API_KEY,就可以直接选模型对话了。