diff --git a/go.mod b/go.mod
index daf19453..adc4e720 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,8 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/klauspost/compress v1.18.3
- github.com/tidwall/gjson v1.14.2
+ github.com/router-for-me/CLIProxyAPI/v6 v6.7.53
+ github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
github.com/wailsapp/wails/v2 v2.11.0
golang.org/x/sync v0.19.0
@@ -21,60 +22,86 @@ require (
)
require (
+ cloud.google.com/go/compute/metadata v0.3.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bep/debounce v1.2.1 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
+ github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/gin-gonic/gin v1.10.1 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-ole/go-ole v1.3.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.20.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/go-stack/stack v1.8.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
- github.com/jackc/pgx/v5 v5.6.0 // indirect
+ github.com/jackc/pgx/v5 v5.7.6 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
- github.com/klauspost/cpuid/v2 v2.2.9 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/labstack/echo/v4 v4.13.3 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
github.com/leaanthony/gosod v1.0.4 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect
github.com/leaanthony/u v1.1.1 // indirect
+ github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
+ github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
+ github.com/refraction-networking/utls v1.8.2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
+ github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/samber/lo v1.49.1 // indirect
+ github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
+ github.com/tiktoken-go/tokenizer v0.7.0 // indirect
github.com/tkrajina/go-reflector v0.5.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.12 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.22 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
- golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
+ golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect
+ golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.33.0 // indirect
+ google.golang.org/protobuf v1.34.1 // indirect
+ gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.23.1 // indirect
)
+
+replace github.com/router-for-me/CLIProxyAPI/v6 => github.com/awsl-project/CLIProxyAPI/v6 v6.0.0-20260205175451-880f8f06a282
diff --git a/go.sum b/go.sum
index 09c87205..f03043b2 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,11 @@
+cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
+cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
+github.com/awsl-project/CLIProxyAPI/v6 v6.0.0-20260205175451-880f8f06a282 h1:uJjERHi5ZshMdhAVtHz3XsE7XdFkny75VZyH1GtbQmM=
+github.com/awsl-project/CLIProxyAPI/v6 v6.0.0-20260205175451-880f8f06a282/go.mod h1:xXy27kWr355/aWdBF/7MGIm0BTArnE9CHMwxo87bnQk=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
@@ -15,8 +19,12 @@ github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gE
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/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
+github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So=
@@ -31,20 +39,37 @@ github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSl
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/getlantern/systray v1.2.2 h1:dCEHtfmvkJG7HZ8lS/sLklTH4RKUcIsKrAD9sThoEBE=
github.com/getlantern/systray v1.2.2/go.mod h1:pXFOI1wwqwYXEhLPm9ZGjS2u/vVELeIgNMY5HvhHhcE=
+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.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
+github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+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.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
+github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+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/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
+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/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -55,8 +80,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
-github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
-github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
+github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
+github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
@@ -65,10 +90,16 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+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/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
-github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
-github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
@@ -83,6 +114,8 @@ github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
@@ -93,22 +126,36 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/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/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo=
+github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
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=
@@ -119,20 +166,26 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
+github.com/tiktoken-go/tokenizer v0.7.0 h1:VMu6MPT0bXFDHr7UPh9uii7CNItVt3X9K90omxL54vw=
+github.com/tiktoken-go/tokenizer v0.7.0/go.mod h1:6UCYI/DtOallbmL7sSy30p6YQv60qNyU/4aVigPOx6w=
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
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.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
@@ -145,19 +198,22 @@ github.com/wailsapp/wails/v2 v2.11.0 h1:seLacV8pqupq32IjS4Y7V8ucab0WZwtK6VvUVxSB
github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
+golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
+golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
+golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -168,8 +224,16 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
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=
diff --git a/internal/adapter/provider/antigravity/adapter.go b/internal/adapter/provider/antigravity/adapter.go
index 853602c9..0f610573 100644
--- a/internal/adapter/provider/antigravity/adapter.go
+++ b/internal/adapter/provider/antigravity/adapter.go
@@ -15,6 +15,7 @@ import (
"time"
"github.com/awsl-project/maxx/internal/adapter/provider"
+ cliproxyapi "github.com/awsl-project/maxx/internal/adapter/provider/cliproxyapi_antigravity"
ctxutil "github.com/awsl-project/maxx/internal/context"
"github.com/awsl-project/maxx/internal/domain"
"github.com/awsl-project/maxx/internal/usage"
@@ -41,6 +42,27 @@ func NewAdapter(p *domain.Provider) (provider.ProviderAdapter, error) {
if p.Config == nil || p.Config.Antigravity == nil {
return nil, fmt.Errorf("provider %s missing antigravity config", p.Name)
}
+
+ // If UseCLIProxyAPI is enabled, directly return CLIProxyAPI adapter
+ if p.Config.Antigravity.UseCLIProxyAPI {
+ cliproxyapiProvider := &domain.Provider{
+ ID: p.ID,
+ Name: p.Name,
+ Type: "cliproxyapi-antigravity",
+ SupportedClientTypes: p.SupportedClientTypes,
+ Config: &domain.ProviderConfig{
+ CLIProxyAPIAntigravity: &domain.ProviderConfigCLIProxyAPIAntigravity{
+ Email: p.Config.Antigravity.Email,
+ RefreshToken: p.Config.Antigravity.RefreshToken,
+ ProjectID: p.Config.Antigravity.ProjectID,
+ ModelMapping: p.Config.Antigravity.ModelMapping,
+ HaikuTarget: p.Config.Antigravity.HaikuTarget,
+ },
+ },
+ }
+ return cliproxyapi.NewAdapter(cliproxyapiProvider)
+ }
+
return &AntigravityAdapter{
provider: p,
tokenCache: &TokenCache{},
diff --git a/internal/adapter/provider/cliproxyapi_antigravity/adapter.go b/internal/adapter/provider/cliproxyapi_antigravity/adapter.go
new file mode 100644
index 00000000..9d80dcbd
--- /dev/null
+++ b/internal/adapter/provider/cliproxyapi_antigravity/adapter.go
@@ -0,0 +1,273 @@
+package cliproxyapi_antigravity
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/awsl-project/maxx/internal/adapter/provider"
+ ctxutil "github.com/awsl-project/maxx/internal/context"
+ "github.com/awsl-project/maxx/internal/domain"
+ "github.com/awsl-project/maxx/internal/usage"
+ "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
+ "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
+ "github.com/router-for-me/CLIProxyAPI/v6/sdk/exec"
+ "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
+)
+
+type CLIProxyAPIAntigravityAdapter struct {
+ provider *domain.Provider
+ authObj *auth.Auth
+ executor *exec.AntigravityExecutor
+}
+
+func NewAdapter(p *domain.Provider) (provider.ProviderAdapter, error) {
+ if p.Config == nil || p.Config.CLIProxyAPIAntigravity == nil {
+ return nil, fmt.Errorf("provider %s missing cliproxyapi-antigravity config", p.Name)
+ }
+
+ cfg := p.Config.CLIProxyAPIAntigravity
+
+ // 创建 Auth 对象,executor 内部会自动处理 token 刷新
+ authObj := &auth.Auth{
+ Provider: "antigravity",
+ Metadata: map[string]any{
+ "type": "antigravity",
+ "refresh_token": cfg.RefreshToken,
+ "project_id": cfg.ProjectID,
+ },
+ }
+
+ adapter := &CLIProxyAPIAntigravityAdapter{
+ provider: p,
+ authObj: authObj,
+ executor: exec.NewAntigravityExecutor(),
+ }
+
+ return adapter, nil
+}
+
+func (a *CLIProxyAPIAntigravityAdapter) SupportedClientTypes() []domain.ClientType {
+ return []domain.ClientType{domain.ClientTypeClaude, domain.ClientTypeGemini}
+}
+
+func (a *CLIProxyAPIAntigravityAdapter) Execute(ctx context.Context, w http.ResponseWriter, req *http.Request, p *domain.Provider) error {
+ clientType := ctxutil.GetClientType(ctx)
+ requestBody := ctxutil.GetRequestBody(ctx)
+ stream := ctxutil.GetIsStream(ctx)
+ requestModel := ctxutil.GetRequestModel(ctx)
+ model := ctxutil.GetMappedModel(ctx) // 全局映射后的模型名(已包含 ProviderType 条件)
+
+ log.Printf("[CLIProxyAPI-Antigravity] requestModel=%s, mappedModel=%s, clientType=%s", requestModel, model, clientType)
+
+ // 替换 body 中的 model 字段为映射后的模型名
+ requestBody, err := updateModelInBody(requestBody, model)
+ if err != nil {
+ return domain.NewProxyErrorWithMessage(err, false, fmt.Sprintf("failed to update model in body: %v", err))
+ }
+
+ // 发送事件
+ if eventChan := ctxutil.GetEventChan(ctx); eventChan != nil {
+ eventChan.SendRequestInfo(&domain.RequestInfo{
+ Method: "POST",
+ URL: fmt.Sprintf("cliproxyapi://antigravity/%s", model),
+ Body: string(requestBody),
+ })
+ }
+
+ // 确定 source format
+ var sourceFormat translator.Format
+ switch clientType {
+ case domain.ClientTypeClaude:
+ sourceFormat = translator.FormatClaude
+ case domain.ClientTypeGemini:
+ sourceFormat = translator.FormatGemini
+ default:
+ return domain.NewProxyErrorWithMessage(nil, false, fmt.Sprintf("unsupported client type: %s", clientType))
+ }
+
+ // 直接透传原始请求给 executor,executor 内部处理格式转换
+ execReq := executor.Request{
+ Model: model,
+ Payload: requestBody,
+ Format: sourceFormat,
+ }
+
+ execOpts := executor.Options{
+ Stream: stream,
+ OriginalRequest: requestBody,
+ SourceFormat: sourceFormat,
+ }
+
+ if stream {
+ return a.executeStream(ctx, w, execReq, execOpts)
+ }
+ return a.executeNonStream(ctx, w, execReq, execOpts)
+}
+
+// updateModelInBody 替换 body 中的 model 字段
+func updateModelInBody(body []byte, model string) ([]byte, error) {
+ var req map[string]any
+ if err := json.Unmarshal(body, &req); err != nil {
+ return nil, err
+ }
+ req["model"] = model
+ return json.Marshal(req)
+}
+
+func (a *CLIProxyAPIAntigravityAdapter) executeNonStream(ctx context.Context, w http.ResponseWriter, execReq executor.Request, execOpts executor.Options) error {
+ resp, err := a.executor.Execute(ctx, a.authObj, execReq, execOpts)
+ if err != nil {
+ log.Printf("[CLIProxyAPI-Antigravity] executeNonStream error: model=%s, err=%v", execReq.Model, err)
+ return domain.NewProxyErrorWithMessage(err, true, fmt.Sprintf("executor request failed: %v", err))
+ }
+
+ if eventChan := ctxutil.GetEventChan(ctx); eventChan != nil {
+ // Send response info
+ eventChan.SendResponseInfo(&domain.ResponseInfo{
+ Status: http.StatusOK,
+ Body: string(resp.Payload),
+ })
+
+ // Extract and send token usage metrics
+ if metrics := usage.ExtractFromResponse(string(resp.Payload)); metrics != nil {
+ eventChan.SendMetrics(&domain.AdapterMetrics{
+ InputTokens: metrics.InputTokens,
+ OutputTokens: metrics.OutputTokens,
+ CacheReadCount: metrics.CacheReadCount,
+ CacheCreationCount: metrics.CacheCreationCount,
+ Cache5mCreationCount: metrics.Cache5mCreationCount,
+ Cache1hCreationCount: metrics.Cache1hCreationCount,
+ })
+ }
+
+ // Extract and send response model
+ if model := extractModelFromResponse(resp.Payload); model != "" {
+ eventChan.SendResponseModel(model)
+ }
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write(resp.Payload)
+
+ return nil
+}
+
+func (a *CLIProxyAPIAntigravityAdapter) executeStream(ctx context.Context, w http.ResponseWriter, execReq executor.Request, execOpts executor.Options) error {
+ flusher, ok := w.(http.Flusher)
+ if !ok {
+ return a.executeNonStream(ctx, w, execReq, execOpts)
+ }
+
+ startTime := time.Now()
+
+ stream, err := a.executor.ExecuteStream(ctx, a.authObj, execReq, execOpts)
+ if err != nil {
+ log.Printf("[CLIProxyAPI-Antigravity] executeStream error: model=%s, err=%v", execReq.Model, err)
+ return domain.NewProxyErrorWithMessage(err, true, fmt.Sprintf("executor stream request failed: %v", err))
+ }
+
+ // 设置 SSE 响应头
+ w.Header().Set("Content-Type", "text/event-stream")
+ w.Header().Set("Cache-Control", "no-cache")
+ w.Header().Set("Connection", "keep-alive")
+ w.WriteHeader(http.StatusOK)
+
+ eventChan := ctxutil.GetEventChan(ctx)
+
+ // Collect SSE content for token extraction
+ var sseBuffer bytes.Buffer
+ var streamErr error
+ firstChunkSent := false
+
+ for chunk := range stream {
+ if chunk.Err != nil {
+ log.Printf("[CLIProxyAPI-Antigravity] stream chunk error: %v", chunk.Err)
+ streamErr = chunk.Err
+ break
+ }
+ if len(chunk.Payload) > 0 {
+ // Payload from executor already includes SSE delimiters (\n\n)
+ sseBuffer.Write(chunk.Payload)
+ _, _ = w.Write(chunk.Payload)
+ flusher.Flush()
+
+ // Report TTFT on first non-empty chunk
+ if !firstChunkSent && eventChan != nil {
+ eventChan.SendFirstToken(time.Since(startTime).Milliseconds())
+ firstChunkSent = true
+ }
+ }
+ }
+
+ // Send final events
+ if eventChan != nil && sseBuffer.Len() > 0 {
+ // Send response info
+ eventChan.SendResponseInfo(&domain.ResponseInfo{
+ Status: http.StatusOK,
+ Body: sseBuffer.String(),
+ })
+
+ // Extract and send token usage metrics
+ if metrics := usage.ExtractFromStreamContent(sseBuffer.String()); metrics != nil {
+ eventChan.SendMetrics(&domain.AdapterMetrics{
+ InputTokens: metrics.InputTokens,
+ OutputTokens: metrics.OutputTokens,
+ CacheReadCount: metrics.CacheReadCount,
+ CacheCreationCount: metrics.CacheCreationCount,
+ Cache5mCreationCount: metrics.Cache5mCreationCount,
+ Cache1hCreationCount: metrics.Cache1hCreationCount,
+ })
+ }
+
+ // Extract and send response model
+ if model := extractModelFromSSE(sseBuffer.String()); model != "" {
+ eventChan.SendResponseModel(model)
+ }
+ }
+
+ // If error occurred before any data was sent, return error to caller
+ if streamErr != nil && sseBuffer.Len() == 0 {
+ return domain.NewProxyErrorWithMessage(streamErr, true, fmt.Sprintf("stream chunk error: %v", streamErr))
+ }
+
+ return nil
+}
+
+// extractModelFromResponse extracts the model field from a JSON response body.
+func extractModelFromResponse(body []byte) string {
+ var resp struct {
+ Model string `json:"model"`
+ }
+ if err := json.Unmarshal(body, &resp); err == nil && resp.Model != "" {
+ return resp.Model
+ }
+ return ""
+}
+
+// extractModelFromSSE extracts the last model field from accumulated SSE content.
+func extractModelFromSSE(sseContent string) string {
+ var lastModel string
+ for line := range strings.SplitSeq(sseContent, "\n") {
+ if !strings.HasPrefix(line, "data: ") {
+ continue
+ }
+ data := strings.TrimPrefix(line, "data: ")
+ if data == "[DONE]" {
+ continue
+ }
+ var chunk struct {
+ Model string `json:"model"`
+ }
+ if err := json.Unmarshal([]byte(data), &chunk); err == nil && chunk.Model != "" {
+ lastModel = chunk.Model
+ }
+ }
+ return lastModel
+}
diff --git a/internal/adapter/provider/cliproxyapi_codex/adapter.go b/internal/adapter/provider/cliproxyapi_codex/adapter.go
new file mode 100644
index 00000000..bab635c3
--- /dev/null
+++ b/internal/adapter/provider/cliproxyapi_codex/adapter.go
@@ -0,0 +1,236 @@
+package cliproxyapi_codex
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/awsl-project/maxx/internal/adapter/provider"
+ ctxutil "github.com/awsl-project/maxx/internal/context"
+ "github.com/awsl-project/maxx/internal/domain"
+ "github.com/awsl-project/maxx/internal/usage"
+ "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
+ "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
+ "github.com/router-for-me/CLIProxyAPI/v6/sdk/exec"
+ "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
+)
+
+type CLIProxyAPICodexAdapter struct {
+ provider *domain.Provider
+ authObj *auth.Auth
+ executor *exec.CodexExecutor
+}
+
+func NewAdapter(p *domain.Provider) (provider.ProviderAdapter, error) {
+ if p.Config == nil || p.Config.CLIProxyAPICodex == nil {
+ return nil, fmt.Errorf("provider %s missing cliproxyapi-codex config", p.Name)
+ }
+
+ // 创建 Auth 对象,executor 内部会自动处理 token 刷新
+ authObj := &auth.Auth{
+ Provider: "codex",
+ Metadata: map[string]any{
+ "type": "codex",
+ "refresh_token": p.Config.CLIProxyAPICodex.RefreshToken,
+ },
+ }
+
+ adapter := &CLIProxyAPICodexAdapter{
+ provider: p,
+ authObj: authObj,
+ executor: exec.NewCodexExecutor(),
+ }
+
+ return adapter, nil
+}
+
+func (a *CLIProxyAPICodexAdapter) SupportedClientTypes() []domain.ClientType {
+ return []domain.ClientType{domain.ClientTypeCodex}
+}
+
+func (a *CLIProxyAPICodexAdapter) Execute(ctx context.Context, w http.ResponseWriter, req *http.Request, p *domain.Provider) error {
+ requestBody := ctxutil.GetRequestBody(ctx)
+ stream := ctxutil.GetIsStream(ctx)
+ model := ctxutil.GetMappedModel(ctx)
+
+ // Codex CLI 使用 OpenAI Responses API 格式
+ sourceFormat := translator.FormatCodex
+
+ // 发送事件
+ if eventChan := ctxutil.GetEventChan(ctx); eventChan != nil {
+ eventChan.SendRequestInfo(&domain.RequestInfo{
+ Method: "POST",
+ URL: fmt.Sprintf("cliproxyapi://codex/%s", model),
+ Body: string(requestBody),
+ })
+ }
+
+ // 构建 executor 请求
+ execReq := executor.Request{
+ Model: model,
+ Payload: requestBody,
+ Format: sourceFormat,
+ }
+
+ execOpts := executor.Options{
+ Stream: stream,
+ OriginalRequest: requestBody,
+ SourceFormat: sourceFormat,
+ }
+
+ if stream {
+ return a.executeStream(ctx, w, execReq, execOpts)
+ }
+ return a.executeNonStream(ctx, w, execReq, execOpts)
+}
+
+func (a *CLIProxyAPICodexAdapter) executeNonStream(ctx context.Context, w http.ResponseWriter, execReq executor.Request, execOpts executor.Options) error {
+ resp, err := a.executor.Execute(ctx, a.authObj, execReq, execOpts)
+ if err != nil {
+ return domain.NewProxyErrorWithMessage(err, true, fmt.Sprintf("executor request failed: %v", err))
+ }
+
+ if eventChan := ctxutil.GetEventChan(ctx); eventChan != nil {
+ // Send response info
+ eventChan.SendResponseInfo(&domain.ResponseInfo{
+ Status: http.StatusOK,
+ Body: string(resp.Payload),
+ })
+
+ // Extract and send token usage metrics
+ if metrics := usage.ExtractFromResponse(string(resp.Payload)); metrics != nil {
+ // Adjust for Codex: input_tokens includes cached_tokens
+ metrics = usage.AdjustForClientType(metrics, domain.ClientTypeCodex)
+ eventChan.SendMetrics(&domain.AdapterMetrics{
+ InputTokens: metrics.InputTokens,
+ OutputTokens: metrics.OutputTokens,
+ })
+ }
+
+ // Extract and send response model
+ if model := extractModelFromResponse(resp.Payload); model != "" {
+ eventChan.SendResponseModel(model)
+ }
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write(resp.Payload)
+
+ return nil
+}
+
+func (a *CLIProxyAPICodexAdapter) executeStream(ctx context.Context, w http.ResponseWriter, execReq executor.Request, execOpts executor.Options) error {
+ flusher, ok := w.(http.Flusher)
+ if !ok {
+ return a.executeNonStream(ctx, w, execReq, execOpts)
+ }
+
+ startTime := time.Now()
+
+ stream, err := a.executor.ExecuteStream(ctx, a.authObj, execReq, execOpts)
+ if err != nil {
+ return domain.NewProxyErrorWithMessage(err, true, fmt.Sprintf("executor stream request failed: %v", err))
+ }
+
+ // 设置 SSE 响应头
+ w.Header().Set("Content-Type", "text/event-stream")
+ w.Header().Set("Cache-Control", "no-cache")
+ w.Header().Set("Connection", "keep-alive")
+ w.WriteHeader(http.StatusOK)
+
+ eventChan := ctxutil.GetEventChan(ctx)
+
+ // Collect SSE content for token extraction
+ var sseBuffer bytes.Buffer
+ var streamErr error
+ firstChunkSent := false
+
+ for chunk := range stream {
+ if chunk.Err != nil {
+ log.Printf("[CLIProxyAPI-Codex] stream chunk error: %v", chunk.Err)
+ streamErr = chunk.Err
+ break
+ }
+ if len(chunk.Payload) > 0 {
+ // Payload from executor already includes SSE delimiters (\n\n)
+ sseBuffer.Write(chunk.Payload)
+ _, _ = w.Write(chunk.Payload)
+ flusher.Flush()
+
+ // Report TTFT on first non-empty chunk
+ if !firstChunkSent && eventChan != nil {
+ eventChan.SendFirstToken(time.Since(startTime).Milliseconds())
+ firstChunkSent = true
+ }
+ }
+ }
+
+ // Send final events
+ if eventChan != nil && sseBuffer.Len() > 0 {
+ // Send response info
+ eventChan.SendResponseInfo(&domain.ResponseInfo{
+ Status: http.StatusOK,
+ Body: sseBuffer.String(),
+ })
+
+ // Extract and send token usage metrics
+ if metrics := usage.ExtractFromStreamContent(sseBuffer.String()); metrics != nil {
+ // Adjust for Codex: input_tokens includes cached_tokens
+ metrics = usage.AdjustForClientType(metrics, domain.ClientTypeCodex)
+ eventChan.SendMetrics(&domain.AdapterMetrics{
+ InputTokens: metrics.InputTokens,
+ OutputTokens: metrics.OutputTokens,
+ })
+ }
+
+ // Extract and send response model
+ if model := extractModelFromSSE(sseBuffer.String()); model != "" {
+ eventChan.SendResponseModel(model)
+ }
+ }
+
+ // If error occurred before any data was sent, return error to caller
+ if streamErr != nil && sseBuffer.Len() == 0 {
+ return domain.NewProxyErrorWithMessage(streamErr, true, fmt.Sprintf("stream chunk error: %v", streamErr))
+ }
+
+ return nil
+}
+
+// extractModelFromResponse extracts the model field from a JSON response body.
+func extractModelFromResponse(body []byte) string {
+ var resp struct {
+ Model string `json:"model"`
+ }
+ if err := json.Unmarshal(body, &resp); err == nil && resp.Model != "" {
+ return resp.Model
+ }
+ return ""
+}
+
+// extractModelFromSSE extracts the last model field from accumulated SSE content.
+func extractModelFromSSE(sseContent string) string {
+ var lastModel string
+ for line := range strings.SplitSeq(sseContent, "\n") {
+ if !strings.HasPrefix(line, "data: ") {
+ continue
+ }
+ data := strings.TrimPrefix(line, "data: ")
+ if data == "[DONE]" {
+ continue
+ }
+ var chunk struct {
+ Model string `json:"model"`
+ }
+ if err := json.Unmarshal([]byte(data), &chunk); err == nil && chunk.Model != "" {
+ lastModel = chunk.Model
+ }
+ }
+ return lastModel
+}
diff --git a/internal/adapter/provider/codex/adapter.go b/internal/adapter/provider/codex/adapter.go
index bab4ec44..74d90672 100644
--- a/internal/adapter/provider/codex/adapter.go
+++ b/internal/adapter/provider/codex/adapter.go
@@ -13,6 +13,7 @@ import (
"time"
"github.com/awsl-project/maxx/internal/adapter/provider"
+ cliproxyapi "github.com/awsl-project/maxx/internal/adapter/provider/cliproxyapi_codex"
ctxutil "github.com/awsl-project/maxx/internal/context"
"github.com/awsl-project/maxx/internal/domain"
"github.com/awsl-project/maxx/internal/usage"
@@ -50,6 +51,26 @@ func NewAdapter(p *domain.Provider) (provider.ProviderAdapter, error) {
return nil, fmt.Errorf("provider %s missing codex config", p.Name)
}
+ config := p.Config.Codex
+
+ // If UseCLIProxyAPI is enabled, directly return CLIProxyAPI adapter
+ if config.UseCLIProxyAPI {
+ cliproxyapiProvider := &domain.Provider{
+ ID: p.ID,
+ Name: p.Name,
+ Type: "cliproxyapi-codex",
+ SupportedClientTypes: p.SupportedClientTypes,
+ Config: &domain.ProviderConfig{
+ CLIProxyAPICodex: &domain.ProviderConfigCLIProxyAPICodex{
+ Email: config.Email,
+ RefreshToken: config.RefreshToken,
+ ModelMapping: config.ModelMapping,
+ },
+ },
+ }
+ return cliproxyapi.NewAdapter(cliproxyapiProvider)
+ }
+
adapter := &CodexAdapter{
provider: p,
tokenCache: &TokenCache{},
@@ -57,7 +78,6 @@ func NewAdapter(p *domain.Provider) (provider.ProviderAdapter, error) {
}
// Initialize token cache from persisted config if available
- config := p.Config.Codex
if config.AccessToken != "" && config.ExpiresAt != "" {
expiresAt, err := time.Parse(time.RFC3339, config.ExpiresAt)
if err == nil && time.Now().Before(expiresAt) {
diff --git a/internal/domain/model.go b/internal/domain/model.go
index 059c534e..f8c8c409 100644
--- a/internal/domain/model.go
+++ b/internal/domain/model.go
@@ -62,6 +62,9 @@ type ProviderConfigAntigravity struct {
// Haiku 模型映射目标 (默认 "gemini-2.5-flash-lite" 省钱,可选 "claude-sonnet-4-5" 更强)
// 空值使用默认 gemini-2.5-flash-lite
HaikuTarget string `json:"haikuTarget,omitempty"`
+
+ // 使用 CLIProxyAPI 转发
+ UseCLIProxyAPI bool `json:"useCLIProxyAPI,omitempty"`
}
type ProviderConfigKiro struct {
@@ -119,13 +122,51 @@ type ProviderConfigCodex struct {
// Model 映射: RequestModel → MappedModel
ModelMapping map[string]string `json:"modelMapping,omitempty"`
+
+ // 使用 CLIProxyAPI 转发
+ UseCLIProxyAPI bool `json:"useCLIProxyAPI,omitempty"`
+}
+
+// ProviderConfigCLIProxyAPIAntigravity CLIProxyAPI Antigravity 内部配置
+// 用于 useCLIProxyAPI=true 时传递给 CLIProxyAPI adapter
+type ProviderConfigCLIProxyAPIAntigravity struct {
+ // 邮箱(用于标识帐号)
+ Email string `json:"email"`
+
+ // Google OAuth refresh_token
+ RefreshToken string `json:"refreshToken"`
+
+ // Google Cloud Project ID
+ ProjectID string `json:"projectID,omitempty"`
+
+ // Model 映射: RequestModel → MappedModel
+ ModelMapping map[string]string `json:"modelMapping,omitempty"`
+
+ // Haiku 模型映射目标 (默认 "gemini-2.5-flash-lite" 省钱)
+ HaikuTarget string `json:"haikuTarget,omitempty"`
+}
+
+// ProviderConfigCLIProxyAPICodex CLIProxyAPI Codex 内部配置
+// 用于 useCLIProxyAPI=true 时传递给 CLIProxyAPI adapter
+type ProviderConfigCLIProxyAPICodex struct {
+ // 邮箱(用于标识帐号)
+ Email string `json:"email"`
+
+ // OpenAI OAuth refresh_token
+ RefreshToken string `json:"refreshToken"`
+
+ // Model 映射: RequestModel → MappedModel
+ ModelMapping map[string]string `json:"modelMapping,omitempty"`
}
type ProviderConfig struct {
- Custom *ProviderConfigCustom `json:"custom,omitempty"`
- Antigravity *ProviderConfigAntigravity `json:"antigravity,omitempty"`
- Kiro *ProviderConfigKiro `json:"kiro,omitempty"`
- Codex *ProviderConfigCodex `json:"codex,omitempty"`
+ Custom *ProviderConfigCustom `json:"custom,omitempty"`
+ Antigravity *ProviderConfigAntigravity `json:"antigravity,omitempty"`
+ Kiro *ProviderConfigKiro `json:"kiro,omitempty"`
+ Codex *ProviderConfigCodex `json:"codex,omitempty"`
+ // 内部运行时字段,仅用于 NewAdapter 委托,不序列化
+ CLIProxyAPIAntigravity *ProviderConfigCLIProxyAPIAntigravity `json:"-"`
+ CLIProxyAPICodex *ProviderConfigCLIProxyAPICodex `json:"-"`
}
// Provider 供应商
diff --git a/web/src/lib/transport/types.ts b/web/src/lib/transport/types.ts
index b1a870be..7f6b2150 100644
--- a/web/src/lib/transport/types.ts
+++ b/web/src/lib/transport/types.ts
@@ -28,6 +28,7 @@ export interface ProviderConfigAntigravity {
projectID: string;
endpoint: string;
modelMapping?: Record