Double简介
Double 是一款对细节有着极致追求,专为高效工作而打造的人工智能编码助手,目前已经可以在 VScode 中免费体验。
主要功能
①聊天
侧边栏支持 GPT-4 聊天,支持在提问时引用代码。
②自动补全
编辑代码时支持 Tab 一键自动补全。
③快捷键操作
解放鼠标,支持快捷键,方便键盘操作。
对标Github的Copilot,Double目前只支持 VScode,点击了解更多内容。
部署教程
本帖将介绍如何利用Worker逆向Double的请求并将其模拟为OpenAI格式,实现统一化调用。限制:目前免费计划 50条/月,订阅用户不受限制
第一步,安装VScode 和 插件
VScode 点击下载 Double插件 点击下载 ### 第二步,注册账号,获取api①安装好插件后重启,登录插件,登录成功后先发送一条聊天消息试试。

复制 Auth Token
第三步,创建worker
/*源代码来自 [Linx.do] 的帖子 :https://linux.do/t/topic/28184*/addEventListener("fetch", event => {event.respondWith(handleRequest(event.request));});async function generateResponseStream(apiResponse, model, timestamp) {const fixedStart = {id: "chatcmpl-smnet-2311676378-double",object: "chat.completion.chunk",created: timestamp,model: model,choices: [{delta: {role: "assistant",content: ""},index: 0,finish_reason: null},],};const fixedEnd = {id: "chatcmpl-smnet-2311676378-double",object: "chat.completion.chunk",created: timestamp,model: "gpt-4",choices: [{delta: {},index: 0,finish_reason: "stop"},],};const reader = apiResponse.body.getReader();return new Response(new ReadableStream({async start(controller) {controller.enqueue(new TextEncoder().encode('data: ' + JSON.stringify(fixedStart) + '\n\n'));while (true) {const { done, value } = await reader.read();if (done) break;const chunk = value;const formattedChunk = {id: "chatcmpl-smnet-2311676378-double",object: "chat.completion.chunk",created: timestamp,model: "gpt-4",choices: [{delta: {content: new TextDecoder().decode(chunk),role: "assistant"},index: 0,finish_reason: null},],};controller.enqueue(new TextEncoder().encode('data: ' + JSON.stringify(formattedChunk) + '\n\n'));}controller.enqueue(new TextEncoder().encode('data: ' + JSON.stringify(fixedEnd) + '\n\n'));controller.enqueue(new TextEncoder().encode('data: [DONE]\n\n'));controller.close();}}), {headers: {"Access-Control-Allow-Origin": "*",'Content-Type': 'text/event-stream'}});}async function generateNormalResponse(apiResponse, model, timestamp) {const responseText = await apiResponse.text();const jsonResponse = JSON.stringify({id: "chatcmpl-smnet-2311676378-double",object: "chat.completion",created: timestamp,model: model,choices: [{index: 0,message: {role: "assistant",content: responseText},finish_reason: "stop"}]});return new Response(jsonResponse, {headers: {"Access-Control-Allow-Origin": "*",'Content-Type': 'application/json'}});};async function handleRequest(request) {try {const url = new URL(request.url);if (request.method === 'OPTIONS') {return new Response(null, {status: 204,headers: {"Access-Control-Allow-Origin": "*","Access-Control-Allow-Methods": "POST, OPTIONS","Access-Control-Allow-Headers": "Content-Type, Authorization"}});}if (request.method !== "POST" || url.pathname !== "/v1/chat/completions") {return new Response("Invalid request", {status: 400});}const bearerToken = request.headers.get("Authorization")?.split("Bearer ")[1];if (!bearerToken) {throw new Error("Missing API Key");}let {messages, model, stream=true} = await request.json();if (!messages) {throw new Error("Messages object is missing");}const systemMessage = messages.find(message => message.role === 'system');if (systemMessage) {let userMessage = messages.find(message => message.role === 'user');if (userMessage) {userMessage.content = systemMessage.content + "\n\n" + userMessage.content;}messages = messages.filter(message => message.role !== 'system');}messages = messages.map(({role, content}) => ({role: role === 'system' ? 'assistant' : role,message: content,codeContexts: []}));let chat_model;switch (model?.toLowerCase()) {case "claude-3-opus":chat_model = "Claude 3 (Opus)";break;default:chat_model = "GPT4 Turbo";}const apiUrl = "https://api.double.bot/api/v1/chat";const headers = {"Content-Type": "application/json","Authorization": `Bearer ${bearerToken}`};const body = JSON.stringify({"api_key": bearerToken,"messages": messages,"chat_model": chat_model,});const apiResponse = await fetch(apiUrl, {method: "POST", headers, body});if (!apiResponse.ok) {const errorText = await apiResponse.text();throw new Error("API request failed: " + errorText);}const timestamp = Math.floor(Date.now() / 1000);if (stream) {return await generateResponseStream(apiResponse, model?.toLowerCase(), timestamp);} else {return await generateNormalResponse(apiResponse, model?.toLowerCase(), timestamp);}} catch (err) {return new Response(err.message, {status: 500});}}
第四步,绑定域名
在触发器中找到添加自定义域,绑定自己的域名,等待证书变成有效

第五步,配置客户端
OneAPI
在oneapi自定义渠道,填入你worker的domain以及auth token 模型填写gpt4和claude-3-opus
#### NextChat
在chatnext或者其他客户端,填入你的oneapi密钥和oneapi域名

