mirror of
https://github.com/zeke-chin/cursor-api.git
synced 2025-10-07 08:20:54 +08:00
go
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
.env
|
||||
tests/
|
||||
|
||||
node_modules/
|
49
api.go
Normal file
49
api.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func formatMessages(messages []Message) string {
|
||||
var formatted []string
|
||||
for _, msg := range messages {
|
||||
formatted = append(formatted, fmt.Sprintf("%s:%s", msg.Role, msg.Content))
|
||||
}
|
||||
return strings.Join(formatted, "\n")
|
||||
}
|
||||
|
||||
func sendToCursorAPI(c *gin.Context, hexData []byte) (*http.Response, error) {
|
||||
req, err := http.NewRequest("POST", "https://api2.cursor.sh/aiserver.v1.AiService/StreamChat", bytes.NewReader(hexData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取认证token
|
||||
authToken := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
|
||||
if strings.Contains(authToken, "%3A%3A") {
|
||||
authToken = strings.Split(authToken, "%3A%3A")[1]
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/connect+proto")
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", authToken))
|
||||
req.Header.Set("Connect-Accept-Encoding", "gzip,br")
|
||||
req.Header.Set("Connect-Protocol-Version", "1")
|
||||
req.Header.Set("User-Agent", "connect-es/1.4.0")
|
||||
req.Header.Set("X-Amzn-Trace-Id", fmt.Sprintf("Root=%s", uuid.New().String()))
|
||||
req.Header.Set("X-Cursor-Checksum", "zo6Qjequ9b9734d1f13c3438ba25ea31ac93d9287248b9d30434934e9fcbfa6b3b22029e/7e4af391f67188693b722eff0090e8e6608bca8fa320ef20a0ccb5d7d62dfdef")
|
||||
req.Header.Set("X-Cursor-Client-Version", "0.42.3")
|
||||
req.Header.Set("X-Cursor-Timezone", "Asia/Shanghai")
|
||||
req.Header.Set("X-Ghost-Mode", "false")
|
||||
req.Header.Set("X-Request-Id", uuid.New().String())
|
||||
req.Header.Set("Host", "api2.cursor.sh")
|
||||
|
||||
client := &http.Client{}
|
||||
return client.Do(req)
|
||||
}
|
36
go.mod
Normal file
36
go.mod
Normal file
@@ -0,0 +1,36 @@
|
||||
module cursor-api-proxy
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.9.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
90
go.sum
Normal file
90
go.sum
Normal file
@@ -0,0 +1,90 @@
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
138
handlers.go
Normal file
138
handlers.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func handleStreamResponse(c *gin.Context, req ChatRequest) {
|
||||
c.Header("Content-Type", "text/event-stream")
|
||||
c.Header("Cache-Control", "no-cache")
|
||||
c.Header("Connection", "keep-alive")
|
||||
|
||||
responseId := "chatcmpl-" + uuid.New().String()
|
||||
|
||||
// 准备请求数据
|
||||
messages := formatMessages(req.Messages)
|
||||
hexData := stringToHex(messages, req.Model)
|
||||
|
||||
// 发送请求到 Cursor API
|
||||
resp, err := sendToCursorAPI(c, hexData)
|
||||
if err != nil {
|
||||
c.SSEvent("error", gin.H{"error": "Internal server error"})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
reader := bufio.NewReader(resp.Body)
|
||||
for {
|
||||
chunk, err := reader.ReadBytes('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
text := processChunk(chunk)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
streamResp := StreamResponse{
|
||||
ID: responseId,
|
||||
Object: "chat.completion.chunk",
|
||||
Created: time.Now().Unix(),
|
||||
Model: req.Model,
|
||||
Choices: []struct {
|
||||
Index int `json:"index"`
|
||||
Delta struct {
|
||||
Content string `json:"content"`
|
||||
} `json:"delta"`
|
||||
}{{
|
||||
Index: 0,
|
||||
Delta: struct {
|
||||
Content string `json:"content"`
|
||||
}{
|
||||
Content: text,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(streamResp)
|
||||
c.SSEvent("message", string(data))
|
||||
c.Writer.Flush()
|
||||
}
|
||||
|
||||
c.SSEvent("message", "[DONE]")
|
||||
}
|
||||
|
||||
func handleNormalResponse(c *gin.Context, req ChatRequest) {
|
||||
messages := formatMessages(req.Messages)
|
||||
hexData := stringToHex(messages, req.Model)
|
||||
|
||||
resp, err := sendToCursorAPI(c, hexData)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var fullText strings.Builder
|
||||
reader := bufio.NewReader(resp.Body)
|
||||
for {
|
||||
chunk, err := reader.ReadBytes('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
text := processChunk(chunk)
|
||||
fullText.WriteString(text)
|
||||
}
|
||||
|
||||
// 处理响应文本
|
||||
text := fullText.String()
|
||||
text = strings.TrimSpace(strings.TrimPrefix(text, "<|END_USER|>"))
|
||||
|
||||
response := ChatResponse{
|
||||
ID: "chatcmpl-" + uuid.New().String(),
|
||||
Object: "chat.completion",
|
||||
Created: time.Now().Unix(),
|
||||
Model: req.Model,
|
||||
Choices: []struct {
|
||||
Index int `json:"index"`
|
||||
Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
} `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
}{{
|
||||
Index: 0,
|
||||
Message: struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}{
|
||||
Role: "assistant",
|
||||
Content: text,
|
||||
},
|
||||
FinishReason: "stop",
|
||||
}},
|
||||
Usage: struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
}{},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
70
main.go
Normal file
70
main.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Println("Warning: Error loading .env file")
|
||||
}
|
||||
|
||||
r := gin.Default()
|
||||
r.POST("/v1/chat/completions", handleChat)
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "3000"
|
||||
}
|
||||
|
||||
log.Printf("服务器运行在端口 %s\n", port)
|
||||
r.Run(":" + port)
|
||||
}
|
||||
|
||||
func handleChat(c *gin.Context) {
|
||||
var req ChatRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否为 o1 开头的模型且请求流式输出
|
||||
if strings.HasPrefix(req.Model, "o1-") && req.Stream {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Model not supported stream"})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取并处理认证token
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
authToken := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
|
||||
if authToken == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Authorization is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 处理消息
|
||||
if len(req.Messages) == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Messages should be a non-empty array",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 处理流式请求
|
||||
if req.Stream {
|
||||
handleStreamResponse(c, req)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理非流式请求
|
||||
handleNormalResponse(c, req)
|
||||
}
|
27
package-lock.json
generated
27
package-lock.json
generated
@@ -12,9 +12,9 @@
|
||||
"axios": "1.7.7",
|
||||
"body-parser": "1.20.3",
|
||||
"dotenv": "16.4.5",
|
||||
"eventsource-parser": "3.0.0",
|
||||
"express": "4.21.1",
|
||||
"uuid": "11.0.3"
|
||||
"uuid": "11.0.3",
|
||||
"yarn": "^1.22.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.0.1",
|
||||
@@ -1419,15 +1419,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
@@ -3722,6 +3713,20 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yarn": {
|
||||
"version": "1.22.22",
|
||||
"resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.22.tgz",
|
||||
"integrity": "sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"bin": {
|
||||
"yarn": "bin/yarn.js",
|
||||
"yarnpkg": "bin/yarn.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
@@ -8,7 +8,8 @@
|
||||
"body-parser": "1.20.3",
|
||||
"dotenv": "16.4.5",
|
||||
"express": "4.21.1",
|
||||
"uuid": "11.0.3"
|
||||
"uuid": "11.0.3",
|
||||
"yarn": "^1.22.22"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node src/index.js",
|
||||
|
190
process.go
Normal file
190
process.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
// "encoding/hex"
|
||||
// "log"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
||||
// // 封装函数,用于将 chunk 转换为 UTF-8 字符串
|
||||
// function chunkToUtf8String (chunk) {
|
||||
// if (chunk[0] === 0x01 || chunk[0] === 0x02 || (chunk[0] === 0x60 && chunk[1] === 0x0C)) {
|
||||
// return ''
|
||||
// }
|
||||
|
||||
// console.log('chunk:', Buffer.from(chunk).toString('hex'))
|
||||
// console.log('chunk string:', Buffer.from(chunk).toString('utf-8'))
|
||||
|
||||
// // 去掉 chunk 中 0x0A 以及之前的字符
|
||||
// chunk = chunk.slice(chunk.indexOf(0x0A) + 1)
|
||||
|
||||
// let filteredChunk = []
|
||||
// let i = 0
|
||||
// while (i < chunk.length) {
|
||||
// // 新的条件过滤:如果遇到连续4个0x00,则移除其之后所有的以 0 开头的字节(0x00 到 0x0F)
|
||||
// if (chunk.slice(i, i + 4).every(byte => byte === 0x00)) {
|
||||
// i += 4 // 跳过这4个0x00
|
||||
// while (i < chunk.length && chunk[i] >= 0x00 && chunk[i] <= 0x0F) {
|
||||
// i++ // 跳过所有以 0 开头的字节
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
|
||||
// if (chunk[i] === 0x0C) {
|
||||
// // 遇到 0x0C 时,跳过 0x0C 以及后续的所有连续的 0x0A
|
||||
// i++ // 跳过 0x0C
|
||||
// while (i < chunk.length && chunk[i] === 0x0A) {
|
||||
// i++ // 跳过所有连续的 0x0A
|
||||
// }
|
||||
// } else if (
|
||||
// i > 0 &&
|
||||
// chunk[i] === 0x0A &&
|
||||
// chunk[i - 1] >= 0x00 &&
|
||||
// chunk[i - 1] <= 0x09
|
||||
// ) {
|
||||
// // 如果当前字节是 0x0A,且前一个字节在 0x00 至 0x09 之间,跳过前一个字节和当前字节
|
||||
// filteredChunk.pop() // 移除已添加的前一个字节
|
||||
// i++ // 跳过当前的 0x0A
|
||||
// } else {
|
||||
// filteredChunk.push(chunk[i])
|
||||
// i++
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 第二步:去除所有的 0x00 和 0x0C
|
||||
// filteredChunk = filteredChunk.filter((byte) => byte !== 0x00 && byte !== 0x0C)
|
||||
|
||||
// // 去除小于 0x0A 的字节
|
||||
// filteredChunk = filteredChunk.filter((byte) => byte >= 0x0A)
|
||||
|
||||
// const hexString = Buffer.from(filteredChunk).toString('hex')
|
||||
// console.log('hexString:', hexString)
|
||||
// const utf8String = Buffer.from(filteredChunk).toString('utf-8')
|
||||
// console.log('utf8String:', utf8String)
|
||||
// return utf8String
|
||||
// }
|
||||
// func processChunk(chunk []byte) string {
|
||||
// // 检查特殊字节开头的情况
|
||||
// if len(chunk) > 0 && (chunk[0] == 0x01 || chunk[0] == 0x02 || (len(chunk) > 1 && chunk[0] == 0x60 && chunk[1] == 0x0C)) {
|
||||
// return ""
|
||||
// }
|
||||
|
||||
// // 打印调试信息
|
||||
// fmt.Printf("chunk: %x\n", chunk)
|
||||
// fmt.Printf("chunk string: %s\n", string(chunk))
|
||||
|
||||
// // 找到第一个 0x0A 并截取之后的内容
|
||||
// index := bytes.IndexByte(chunk, 0x0A)
|
||||
// if index != -1 {
|
||||
// chunk = chunk[index+1:]
|
||||
// }
|
||||
|
||||
// // 创建过滤后的切片
|
||||
// filteredChunk := make([]byte, 0, len(chunk))
|
||||
// for i := 0; i < len(chunk); {
|
||||
// // 检查连续4个0x00的情况
|
||||
// if i+4 <= len(chunk) {
|
||||
// if chunk[i] == 0x00 && chunk[i+1] == 0x00 && chunk[i+2] == 0x00 && chunk[i+3] == 0x00 {
|
||||
// i += 4
|
||||
// // 跳过所有以0开头的字节
|
||||
// for i < len(chunk) && chunk[i] <= 0x0F {
|
||||
// i++
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
|
||||
// if chunk[i] == 0x0C {
|
||||
// i++
|
||||
// // 跳过所有连续的0x0A
|
||||
// for i < len(chunk) && chunk[i] == 0x0A {
|
||||
// i++
|
||||
// }
|
||||
// } else if i > 0 && chunk[i] == 0x0A && chunk[i-1] >= 0x00 && chunk[i-1] <= 0x09 {
|
||||
// // 移除前一个字节并跳过当前的0x0A
|
||||
// filteredChunk = filteredChunk[:len(filteredChunk)-1]
|
||||
// i++
|
||||
// } else {
|
||||
// filteredChunk = append(filteredChunk, chunk[i])
|
||||
// i++
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 过滤掉0x00和0x0C
|
||||
// tempChunk := make([]byte, 0, len(filteredChunk))
|
||||
// for _, b := range filteredChunk {
|
||||
// if b != 0x00 && b != 0x0C {
|
||||
// tempChunk = append(tempChunk, b)
|
||||
// }
|
||||
// }
|
||||
// filteredChunk = tempChunk
|
||||
|
||||
// // 过滤掉小于0x0A的字节
|
||||
// tempChunk = make([]byte, 0, len(filteredChunk))
|
||||
// for _, b := range filteredChunk {
|
||||
// if b >= 0x0A {
|
||||
// tempChunk = append(tempChunk, b)
|
||||
// }
|
||||
// }
|
||||
// filteredChunk = tempChunk
|
||||
|
||||
// // 打印调试信息并返回结果
|
||||
// fmt.Printf("hexString: %x\n", filteredChunk)
|
||||
// result := string(filteredChunk)
|
||||
// fmt.Printf("utf8String: %s\n", result)
|
||||
// return result
|
||||
// }
|
||||
|
||||
func processChunk(chunk []byte) string {
|
||||
// 检查特殊字节开头的情况
|
||||
if len(chunk) > 0 && (chunk[0] == 0x01 || chunk[0] == 0x02 || (len(chunk) > 1 && chunk[0] == 0x60 && chunk[1] == 0x0C)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 打印调试信息
|
||||
fmt.Printf("chunk: %x\n", chunk)
|
||||
fmt.Printf("chunk string: %s\n", string(chunk))
|
||||
|
||||
// 找到第一个 0x0A 并截取之后的内容
|
||||
index := bytes.IndexByte(chunk, 0x0A)
|
||||
if index != -1 {
|
||||
chunk = chunk[index+1:]
|
||||
}
|
||||
|
||||
// 创建过滤后的切片
|
||||
filteredChunk := make([]byte, 0, len(chunk))
|
||||
for i := 0; i < len(chunk); {
|
||||
// 检查连续4个0x00的情况
|
||||
if i+4 <= len(chunk) {
|
||||
allZeros := true
|
||||
for j := 0; j < 4; j++ {
|
||||
if chunk[i+j] != 0x00 {
|
||||
allZeros = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allZeros {
|
||||
i += 4
|
||||
// 跳过所有以0开头的字节
|
||||
for i < len(chunk) && chunk[i] <= 0x0F {
|
||||
i++
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 保留UTF-8字符
|
||||
if chunk[i] >= 0xE0 || (chunk[i] >= 0x20 && chunk[i] <= 0x7F) {
|
||||
filteredChunk = append(filteredChunk, chunk[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// 打印调试信息并返回结果
|
||||
fmt.Printf("hexString: %x\n", filteredChunk)
|
||||
result := string(filteredChunk)
|
||||
fmt.Printf("utf8String: %s\n", result)
|
||||
return string(chunk)
|
||||
}
|
45
types.go
Normal file
45
types.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
type Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type ChatRequest struct {
|
||||
Model string `json:"model"`
|
||||
Messages []Message `json:"messages"`
|
||||
Stream bool `json:"stream"`
|
||||
}
|
||||
|
||||
type ChatResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int64 `json:"created"`
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Index int `json:"index"`
|
||||
Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
} `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
} `json:"choices"`
|
||||
Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
} `json:"usage"`
|
||||
}
|
||||
|
||||
type StreamResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int64 `json:"created"`
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Index int `json:"index"`
|
||||
Delta struct {
|
||||
Content string `json:"content"`
|
||||
} `json:"delta"`
|
||||
} `json:"choices"`
|
||||
}
|
83
utils.go
Normal file
83
utils.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func stringToHex(str, modelName string) []byte {
|
||||
inputBytes := []byte(str)
|
||||
byteLength := len(inputBytes)
|
||||
|
||||
const (
|
||||
FIXED_HEADER = 2
|
||||
SEPARATOR = 1
|
||||
)
|
||||
|
||||
FIXED_SUFFIX_LENGTH := 0xA3 + len(modelName)
|
||||
|
||||
// 计算文本长度字段
|
||||
var textLengthField1, textLengthFieldSize1 int
|
||||
if byteLength < 128 {
|
||||
textLengthField1 = byteLength
|
||||
textLengthFieldSize1 = 1
|
||||
} else {
|
||||
lowByte1 := (byteLength & 0x7F) | 0x80
|
||||
highByte1 := (byteLength >> 7) & 0xFF
|
||||
textLengthField1 = (highByte1 << 8) | lowByte1
|
||||
textLengthFieldSize1 = 2
|
||||
}
|
||||
|
||||
// 计算基础长度
|
||||
baseLength := byteLength + 0x2A
|
||||
var textLengthField, textLengthFieldSize int
|
||||
if baseLength < 128 {
|
||||
textLengthField = baseLength
|
||||
textLengthFieldSize = 1
|
||||
} else {
|
||||
lowByte := (baseLength & 0x7F) | 0x80
|
||||
highByte := (baseLength >> 7) & 0xFF
|
||||
textLengthField = (highByte << 8) | lowByte
|
||||
textLengthFieldSize = 2
|
||||
}
|
||||
|
||||
// 计算总消息长度
|
||||
messageTotalLength := FIXED_HEADER + textLengthFieldSize + SEPARATOR +
|
||||
textLengthFieldSize1 + byteLength + FIXED_SUFFIX_LENGTH
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
// 写入消息长度
|
||||
fmt.Fprintf(&buf, "%010x", messageTotalLength)
|
||||
|
||||
// 写入固定头部
|
||||
buf.WriteString("12")
|
||||
|
||||
// 写入长度字段
|
||||
fmt.Fprintf(&buf, "%02x", textLengthField)
|
||||
|
||||
buf.WriteString("0A")
|
||||
fmt.Fprintf(&buf, "%02x", textLengthField1)
|
||||
|
||||
// 写入消息内容
|
||||
buf.WriteString(hex.EncodeToString(inputBytes))
|
||||
|
||||
// 写入固定后缀
|
||||
buf.WriteString("10016A2432343163636435662D393162612D343131382D393239612D3936626330313631626432612")
|
||||
buf.WriteString("2002A132F643A2F6964656150726F2F656475626F73733A1E0A")
|
||||
|
||||
// 写入模型名称长度和内容
|
||||
fmt.Fprintf(&buf, "%02X", len(modelName))
|
||||
buf.WriteString(strings.ToUpper(hex.EncodeToString([]byte(modelName))))
|
||||
|
||||
// 写入剩余固定内容
|
||||
buf.WriteString("22004A")
|
||||
buf.WriteString("2461383761396133342D323164642D343863372D623434662D616636633365636536663765")
|
||||
buf.WriteString("680070007A2436393337376535612D386332642D343835342D623564392D653062623232336163303061")
|
||||
buf.WriteString("800101B00100C00100E00100E80100")
|
||||
|
||||
hexBytes, _ := hex.DecodeString(strings.ToUpper(buf.String()))
|
||||
return hexBytes
|
||||
}
|
44
yarn.lock
44
yarn.lock
@@ -102,7 +102,7 @@ acorn-jsx@^5.3.2:
|
||||
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn@^8.9.0:
|
||||
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.9.0:
|
||||
version "8.14.0"
|
||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz"
|
||||
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
|
||||
@@ -380,13 +380,6 @@ data-view-byte-offset@^1.0.0:
|
||||
es-errors "^1.3.0"
|
||||
is-data-view "^1.0.1"
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.2.7:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
||||
@@ -394,13 +387,27 @@ debug@^3.2.7:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.3.1, debug@^4.3.2:
|
||||
debug@^4.3.1:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz"
|
||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
debug@^4.3.2:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz"
|
||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
deep-is@^0.1.3:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
|
||||
@@ -676,7 +683,7 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
|
||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||
|
||||
eslint@^8.0.1:
|
||||
"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0 || ^9.0.0", eslint@^8.0.1, eslint@>=6.0.0, eslint@>=7.0.0, eslint@>=8:
|
||||
version "8.57.1"
|
||||
resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz"
|
||||
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
|
||||
@@ -1374,16 +1381,16 @@ minimist@^1.2.0, minimist@^1.2.6:
|
||||
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
ms@^2.1.1, ms@^2.1.3, ms@2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
|
||||
|
||||
ms@2.1.3, ms@^2.1.1, ms@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||
@@ -1909,7 +1916,7 @@ unbox-primitive@^1.0.2:
|
||||
has-symbols "^1.0.3"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
unpipe@~1.0.0, unpipe@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
|
||||
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
||||
@@ -2003,6 +2010,11 @@ wrappy@1:
|
||||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
yarn@^1.22.22:
|
||||
version "1.22.22"
|
||||
resolved "https://registry.npmjs.org/yarn/-/yarn-1.22.22.tgz"
|
||||
integrity sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
|
||||
|
Reference in New Issue
Block a user