on main help sign in
feat: add swagger api docs generation
6dcaab1Ayman Bagabas
Taskfile.yaml+13
@@ -185,6 +185,19 @@ tasks:
185 - go get charm.land/catwalk
185 - go get charm.land/catwalk
186 - go mod tidy
186 - go mod tidy
187
187
188+ swag:
189+ desc: Generate OpenAPI spec from swag annotations
190+ cmds:
191+ - go run github.com/swaggo/swag/cmd/swag@v1.16.6 init --generalInfo main.go --dir . --output docs --parseDependency --parseInternal --parseDepth 5
192+ sources:
193+ - internal/server/*.go
194+ - internal/proto/*.go
195+ - main.go
196+ generates:
197+ - docs/docs.go
198+ - docs/swagger.json
199+ - docs/swagger.yaml
200+
188 sqlc:
201 sqlc:
189 desc: Generate code using SQLC
202 desc: Generate code using SQLC
190 cmds:
203 cmds:
go.mod+14−2
@@ -14,6 +14,7 @@ require (
14 charm.land/x/vcr v0.1.1
14 charm.land/x/vcr v0.1.1
15 github.com/JohannesKaufmann/html-to-markdown v1.6.0
15 github.com/JohannesKaufmann/html-to-markdown v1.6.0
16 github.com/MakeNowJust/heredoc v1.0.0
16 github.com/MakeNowJust/heredoc v1.0.0
17+ github.com/Microsoft/go-winio v0.6.2
17 github.com/PuerkitoBio/goquery v1.12.0
18 github.com/PuerkitoBio/goquery v1.12.0
18 github.com/alecthomas/chroma/v2 v2.23.1
19 github.com/alecthomas/chroma/v2 v2.23.1
19 github.com/atotto/clipboard v0.1.4
20 github.com/atotto/clipboard v0.1.4
@@ -59,12 +60,15 @@ require (
59 github.com/sourcegraph/jsonrpc2 v0.2.1
60 github.com/sourcegraph/jsonrpc2 v0.2.1
60 github.com/spf13/cobra v1.10.2
61 github.com/spf13/cobra v1.10.2
61 github.com/stretchr/testify v1.11.1
62 github.com/stretchr/testify v1.11.1
63+ github.com/swaggo/http-swagger/v2 v2.0.2
64+ github.com/swaggo/swag v1.16.6
62 github.com/tidwall/gjson v1.18.0
65 github.com/tidwall/gjson v1.18.0
63 github.com/tidwall/sjson v1.2.5
66 github.com/tidwall/sjson v1.2.5
64 github.com/zeebo/xxh3 v1.1.0
67 github.com/zeebo/xxh3 v1.1.0
65 go.uber.org/goleak v1.3.0
68 go.uber.org/goleak v1.3.0
66 golang.org/x/net v0.52.0
69 golang.org/x/net v0.52.0
67 golang.org/x/sync v0.20.0
70 golang.org/x/sync v0.20.0
71+ golang.org/x/sys v0.42.0
68 golang.org/x/text v0.35.0
72 golang.org/x/text v0.35.0
69 gopkg.in/natefinch/lumberjack.v2 v2.2.1
73 gopkg.in/natefinch/lumberjack.v2 v2.2.1
70 gopkg.in/yaml.v3 v3.0.1
74 gopkg.in/yaml.v3 v3.0.1
@@ -81,7 +85,7 @@ require (
81 git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
85 git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
82 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
86 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
83 github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
87 github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
84− github.com/Microsoft/go-winio v0.6.2 // indirect
88+ github.com/KyleBanks/depth v1.2.1 // indirect
85 github.com/andybalholm/cascadia v1.3.3 // indirect
89 github.com/andybalholm/cascadia v1.3.3 // indirect
86 github.com/aws/aws-sdk-go-v2 v1.41.3 // indirect
90 github.com/aws/aws-sdk-go-v2 v1.41.3 // indirect
87 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect
91 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect
@@ -119,6 +123,10 @@ require (
119 github.com/go-logr/logr v1.4.3 // indirect
123 github.com/go-logr/logr v1.4.3 // indirect
120 github.com/go-logr/stdr v1.2.2 // indirect
124 github.com/go-logr/stdr v1.2.2 // indirect
121 github.com/go-ole/go-ole v1.3.0 // indirect
125 github.com/go-ole/go-ole v1.3.0 // indirect
126+ github.com/go-openapi/jsonpointer v0.19.5 // indirect
127+ github.com/go-openapi/jsonreference v0.20.0 // indirect
128+ github.com/go-openapi/spec v0.20.6 // indirect
129+ github.com/go-openapi/swag v0.19.15 // indirect
122 github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
130 github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
123 github.com/goccy/go-json v0.10.5 // indirect
131 github.com/goccy/go-json v0.10.5 // indirect
124 github.com/goccy/go-yaml v1.19.2 // indirect
132 github.com/goccy/go-yaml v1.19.2 // indirect
@@ -135,6 +143,7 @@ require (
135 github.com/inconshreveable/mousetrap v1.1.0 // indirect
143 github.com/inconshreveable/mousetrap v1.1.0 // indirect
136 github.com/jackmordaunt/icns/v3 v3.0.1 // indirect
144 github.com/jackmordaunt/icns/v3 v3.0.1 // indirect
137 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
145 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
146+ github.com/josharian/intern v1.0.0 // indirect
138 github.com/kaptinlin/go-i18n v0.2.12 // indirect
147 github.com/kaptinlin/go-i18n v0.2.12 // indirect
139 github.com/kaptinlin/jsonpointer v0.4.17 // indirect
148 github.com/kaptinlin/jsonpointer v0.4.17 // indirect
140 github.com/kaptinlin/jsonschema v0.7.5 // indirect
149 github.com/kaptinlin/jsonschema v0.7.5 // indirect
@@ -164,6 +173,7 @@ require (
164 github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
173 github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
165 github.com/sethvargo/go-retry v0.3.0 // indirect
174 github.com/sethvargo/go-retry v0.3.0 // indirect
166 github.com/spf13/pflag v1.0.9 // indirect
175 github.com/spf13/pflag v1.0.9 // indirect
176+ github.com/swaggo/files/v2 v2.0.0 // indirect
167 github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
177 github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
168 github.com/tetratelabs/wazero v1.11.0 // indirect
178 github.com/tetratelabs/wazero v1.11.0 // indirect
169 github.com/tidwall/match v1.1.1 // indirect
179 github.com/tidwall/match v1.1.1 // indirect
@@ -186,10 +196,11 @@ require (
186 golang.org/x/crypto v0.49.0 // indirect
196 golang.org/x/crypto v0.49.0 // indirect
187 golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
197 golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
188 golang.org/x/image v0.36.0 // indirect
198 golang.org/x/image v0.36.0 // indirect
199+ golang.org/x/mod v0.33.0 // indirect
189 golang.org/x/oauth2 v0.36.0 // indirect
200 golang.org/x/oauth2 v0.36.0 // indirect
190− golang.org/x/sys v0.42.0 // indirect
191 golang.org/x/term v0.41.0 // indirect
201 golang.org/x/term v0.41.0 // indirect
192 golang.org/x/time v0.14.0 // indirect
202 golang.org/x/time v0.14.0 // indirect
203+ golang.org/x/tools v0.42.0 // indirect
193 google.golang.org/api v0.269.0 // indirect
204 google.golang.org/api v0.269.0 // indirect
194 google.golang.org/genai v1.49.0 // indirect
205 google.golang.org/genai v1.49.0 // indirect
195 google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
206 google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
@@ -198,6 +209,7 @@ require (
198 gopkg.in/dnaeon/go-vcr.v4 v4.0.6-0.20251110073552-01de4eb40290 // indirect
209 gopkg.in/dnaeon/go-vcr.v4 v4.0.6-0.20251110073552-01de4eb40290 // indirect
199 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
210 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
200 gopkg.in/warnings.v0 v0.1.2 // indirect
211 gopkg.in/warnings.v0 v0.1.2 // indirect
212+ gopkg.in/yaml.v2 v2.4.0 // indirect
201 modernc.org/libc v1.68.0 // indirect
213 modernc.org/libc v1.68.0 // indirect
202 modernc.org/mathutil v1.7.1 // indirect
214 modernc.org/mathutil v1.7.1 // indirect
203 modernc.org/memory v1.11.0 // indirect
215 modernc.org/memory v1.11.0 // indirect
go.sum+28
@@ -36,6 +36,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mx
36github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
36github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
37github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k=
37github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k=
38github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
38github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
39+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
40+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
39github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
41github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
40github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
42github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
41github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
43github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
@@ -139,6 +141,7 @@ github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJ
139github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
141github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
140github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
142github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
141github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
143github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
144+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
142github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
145github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
143github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
146github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
144github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
147github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -188,6 +191,16 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
188github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
191github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
189github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
192github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
190github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
193github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
194+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
195+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
196+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
197+github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
198+github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
199+github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ=
200+github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
201+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
202+github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
203+github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
191github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
204github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
192github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
205github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
193github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
206github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
@@ -241,6 +254,7 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
241github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
254github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
242github.com/jordanella/go-ansi-paintbrush v0.0.0-20240728195301-b7ad996ecf3d h1:on25kP+Sx7sxUMRQiA8gdcToAGet4DK/EIA30mXre+4=
255github.com/jordanella/go-ansi-paintbrush v0.0.0-20240728195301-b7ad996ecf3d h1:on25kP+Sx7sxUMRQiA8gdcToAGet4DK/EIA30mXre+4=
243github.com/jordanella/go-ansi-paintbrush v0.0.0-20240728195301-b7ad996ecf3d/go.mod h1:SV0W0APWP9MZ1/gfDQ/NzzTlWdIgYZ/ZbpN4d/UXRYw=
256github.com/jordanella/go-ansi-paintbrush v0.0.0-20240728195301-b7ad996ecf3d/go.mod h1:SV0W0APWP9MZ1/gfDQ/NzzTlWdIgYZ/ZbpN4d/UXRYw=
257+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
244github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
258github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
245github.com/kaptinlin/go-i18n v0.2.12 h1:ywDsvb4KDFddMC2dpI/rrIzGU2mWUSvHmWUm9BMsdl4=
259github.com/kaptinlin/go-i18n v0.2.12 h1:ywDsvb4KDFddMC2dpI/rrIzGU2mWUSvHmWUm9BMsdl4=
246github.com/kaptinlin/go-i18n v0.2.12/go.mod h1:pVcu9qsW5pOIOoZFJXesRYmLos1vMQrby70JPAoWmJU=
260github.com/kaptinlin/go-i18n v0.2.12/go.mod h1:pVcu9qsW5pOIOoZFJXesRYmLos1vMQrby70JPAoWmJU=
@@ -267,6 +281,9 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
267github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
281github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
268github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
282github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
269github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
283github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
284+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
285+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
286+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
270github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
287github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
271github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
288github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
272github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
289github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -299,6 +316,7 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt
299github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
316github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
300github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
317github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
301github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
318github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
319+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
302github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
320github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
303github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
321github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
304github.com/openai/openai-go/v3 v3.28.0 h1:2+FfrCVMdGXSQrBv1tLWtokm+BU7+3hJ/8rAHPQ63KM=
322github.com/openai/openai-go/v3 v3.28.0 h1:2+FfrCVMdGXSQrBv1tLWtokm+BU7+3hJ/8rAHPQ63KM=
@@ -359,11 +377,18 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
359github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
377github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
360github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
378github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
361github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
379github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
380+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
362github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
381github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
363github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
382github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
364github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
383github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
365github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
384github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
366github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
385github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
386+github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
387+github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
388+github.com/swaggo/http-swagger/v2 v2.0.2 h1:FKCdLsl+sFCx60KFsyM0rDarwiUSZ8DqbfSyIKC9OBg=
389+github.com/swaggo/http-swagger/v2 v2.0.2/go.mod h1:r7/GBkAWIfK6E/OLnE8fXnviHiDeAHmgIyooa4xm3AQ=
390+github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
391+github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
367github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
392github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
368github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
393github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
369github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
394github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
@@ -532,7 +557,9 @@ google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhH
532google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
557google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
533google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
558google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
534gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
559gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
560+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
535gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
561gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
562+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
536gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
563gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
537gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
564gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
538gopkg.in/dnaeon/go-vcr.v4 v4.0.6-0.20251110073552-01de4eb40290 h1:g3ah7zaWmw41EtOgBNXpx8zk4HYuH3OMwB+qh1Dt834=
565gopkg.in/dnaeon/go-vcr.v4 v4.0.6-0.20251110073552-01de4eb40290 h1:g3ah7zaWmw41EtOgBNXpx8zk4HYuH3OMwB+qh1Dt834=
@@ -547,6 +574,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
547gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
574gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
548gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
575gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
549gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
576gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
577+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
550gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
578gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
551gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
579gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
552modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
580modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
internal/proto/requests.gonew file+92
@@ -0,0 +1,92 @@
1+package proto
2+
3+import "github.com/charmbracelet/crush/internal/config"
4+
5+// ConfigSetRequest represents a request to set a config field.
6+type ConfigSetRequest struct {
7+ Scope config.Scope `json:"scope"`
8+ Key string `json:"key"`
9+ Value any `json:"value"`
10+}
11+
12+// ConfigRemoveRequest represents a request to remove a config field.
13+type ConfigRemoveRequest struct {
14+ Scope config.Scope `json:"scope"`
15+ Key string `json:"key"`
16+}
17+
18+// ConfigModelRequest represents a request to update the preferred model.
19+type ConfigModelRequest struct {
20+ Scope config.Scope `json:"scope"`
21+ ModelType config.SelectedModelType `json:"model_type"`
22+ Model config.SelectedModel `json:"model"`
23+}
24+
25+// ConfigCompactRequest represents a request to set compact mode.
26+type ConfigCompactRequest struct {
27+ Scope config.Scope `json:"scope"`
28+ Enabled bool `json:"enabled"`
29+}
30+
31+// ConfigProviderKeyRequest represents a request to set a provider API key.
32+type ConfigProviderKeyRequest struct {
33+ Scope config.Scope `json:"scope"`
34+ ProviderID string `json:"provider_id"`
35+ APIKey any `json:"api_key"`
36+}
37+
38+// ConfigRefreshOAuthRequest represents a request to refresh an OAuth token.
39+type ConfigRefreshOAuthRequest struct {
40+ Scope config.Scope `json:"scope"`
41+ ProviderID string `json:"provider_id"`
42+}
43+
44+// ImportCopilotResponse represents the response from importing Copilot credentials.
45+type ImportCopilotResponse struct {
46+ Token any `json:"token"`
47+ Success bool `json:"success"`
48+}
49+
50+// ProjectNeedsInitResponse represents whether a project needs initialization.
51+type ProjectNeedsInitResponse struct {
52+ NeedsInit bool `json:"needs_init"`
53+}
54+
55+// ProjectInitPromptResponse represents the project initialization prompt.
56+type ProjectInitPromptResponse struct {
57+ Prompt string `json:"prompt"`
58+}
59+
60+// LSPStartRequest represents a request to start an LSP for a path.
61+type LSPStartRequest struct {
62+ Path string `json:"path"`
63+}
64+
65+// FileTrackerReadRequest represents a request to record a file read.
66+type FileTrackerReadRequest struct {
67+ SessionID string `json:"session_id"`
68+ Path string `json:"path"`
69+}
70+
71+// MCPNameRequest represents a request targeting a named MCP server.
72+type MCPNameRequest struct {
73+ Name string `json:"name"`
74+}
75+
76+// MCPReadResourceRequest represents a request to read an MCP resource.
77+type MCPReadResourceRequest struct {
78+ Name string `json:"name"`
79+ URI string `json:"uri"`
80+}
81+
82+// MCPGetPromptRequest represents a request to get an MCP prompt.
83+type MCPGetPromptRequest struct {
84+ ClientID string `json:"client_id"`
85+ PromptID string `json:"prompt_id"`
86+ Args map[string]string `json:"args"`
87+}
88+
89+// MCPGetPromptResponse represents the response from getting an MCP prompt.
90+type MCPGetPromptResponse struct {
91+ Prompt string `json:"prompt"`
92+}
internal/server/config.go+198−59
@@ -4,18 +4,25 @@ import (
4 "encoding/json"
4 "encoding/json"
5 "net/http"
5 "net/http"
6
6
7− "github.com/charmbracelet/crush/internal/config"
8 "github.com/charmbracelet/crush/internal/proto"
7 "github.com/charmbracelet/crush/internal/proto"
9)
8)
10
9
10+// handlePostWorkspaceConfigSet sets a configuration field.
11+//
12+// @Summary Set a config field
13+// @Tags config
14+// @Accept json
15+// @Param id path string true "Workspace ID"
16+// @Param request body proto.ConfigSetRequest true "Config set request"
17+// @Success 200
18+// @Failure 400 {object} proto.Error
19+// @Failure 404 {object} proto.Error
20+// @Failure 500 {object} proto.Error
21+// @Router /workspaces/{id}/config/set [post]
11func (c *controllerV1) handlePostWorkspaceConfigSet(w http.ResponseWriter, r *http.Request) {
22func (c *controllerV1) handlePostWorkspaceConfigSet(w http.ResponseWriter, r *http.Request) {
12 id := r.PathValue("id")
23 id := r.PathValue("id")
13
24
14− var req struct {
25+ var req proto.ConfigSetRequest
15− Scope config.Scope `json:"scope"`
16− Key string `json:"key"`
17− Value any `json:"value"`
18− }
19 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
26 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
20 c.server.logError(r, "Failed to decode request", "error", err)
27 c.server.logError(r, "Failed to decode request", "error", err)
21 jsonError(w, http.StatusBadRequest, "failed to decode request")
28 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -29,13 +36,22 @@ func (c *controllerV1) handlePostWorkspaceConfigSet(w http.ResponseWriter, r *ht
29 w.WriteHeader(http.StatusOK)
36 w.WriteHeader(http.StatusOK)
30}
37}
31
38
39+// handlePostWorkspaceConfigRemove removes a configuration field.
40+//
41+// @Summary Remove a config field
42+// @Tags config
43+// @Accept json
44+// @Param id path string true "Workspace ID"
45+// @Param request body proto.ConfigRemoveRequest true "Config remove request"
46+// @Success 200
47+// @Failure 400 {object} proto.Error
48+// @Failure 404 {object} proto.Error
49+// @Failure 500 {object} proto.Error
50+// @Router /workspaces/{id}/config/remove [post]
32func (c *controllerV1) handlePostWorkspaceConfigRemove(w http.ResponseWriter, r *http.Request) {
51func (c *controllerV1) handlePostWorkspaceConfigRemove(w http.ResponseWriter, r *http.Request) {
33 id := r.PathValue("id")
52 id := r.PathValue("id")
34
53
35− var req struct {
54+ var req proto.ConfigRemoveRequest
36− Scope config.Scope `json:"scope"`
37− Key string `json:"key"`
38− }
39 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
55 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
40 c.server.logError(r, "Failed to decode request", "error", err)
56 c.server.logError(r, "Failed to decode request", "error", err)
41 jsonError(w, http.StatusBadRequest, "failed to decode request")
57 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -49,14 +65,22 @@ func (c *controllerV1) handlePostWorkspaceConfigRemove(w http.ResponseWriter, r
49 w.WriteHeader(http.StatusOK)
65 w.WriteHeader(http.StatusOK)
50}
66}
51
67
68+// handlePostWorkspaceConfigModel updates the preferred model.
69+//
70+// @Summary Set the preferred model
71+// @Tags config
72+// @Accept json
73+// @Param id path string true "Workspace ID"
74+// @Param request body proto.ConfigModelRequest true "Config model request"
75+// @Success 200
76+// @Failure 400 {object} proto.Error
77+// @Failure 404 {object} proto.Error
78+// @Failure 500 {object} proto.Error
79+// @Router /workspaces/{id}/config/model [post]
52func (c *controllerV1) handlePostWorkspaceConfigModel(w http.ResponseWriter, r *http.Request) {
80func (c *controllerV1) handlePostWorkspaceConfigModel(w http.ResponseWriter, r *http.Request) {
53 id := r.PathValue("id")
81 id := r.PathValue("id")
54
82
55− var req struct {
83+ var req proto.ConfigModelRequest
56− Scope config.Scope `json:"scope"`
57− ModelType config.SelectedModelType `json:"model_type"`
58− Model config.SelectedModel `json:"model"`
59− }
60 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
84 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
61 c.server.logError(r, "Failed to decode request", "error", err)
85 c.server.logError(r, "Failed to decode request", "error", err)
62 jsonError(w, http.StatusBadRequest, "failed to decode request")
86 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -70,13 +94,22 @@ func (c *controllerV1) handlePostWorkspaceConfigModel(w http.ResponseWriter, r *
70 w.WriteHeader(http.StatusOK)
94 w.WriteHeader(http.StatusOK)
71}
95}
72
96
97+// handlePostWorkspaceConfigCompact sets compact mode.
98+//
99+// @Summary Set compact mode
100+// @Tags config
101+// @Accept json
102+// @Param id path string true "Workspace ID"
103+// @Param request body proto.ConfigCompactRequest true "Config compact request"
104+// @Success 200
105+// @Failure 400 {object} proto.Error
106+// @Failure 404 {object} proto.Error
107+// @Failure 500 {object} proto.Error
108+// @Router /workspaces/{id}/config/compact [post]
73func (c *controllerV1) handlePostWorkspaceConfigCompact(w http.ResponseWriter, r *http.Request) {
109func (c *controllerV1) handlePostWorkspaceConfigCompact(w http.ResponseWriter, r *http.Request) {
74 id := r.PathValue("id")
110 id := r.PathValue("id")
75
111
76− var req struct {
112+ var req proto.ConfigCompactRequest
77− Scope config.Scope `json:"scope"`
78− Enabled bool `json:"enabled"`
79− }
80 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
113 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
81 c.server.logError(r, "Failed to decode request", "error", err)
114 c.server.logError(r, "Failed to decode request", "error", err)
82 jsonError(w, http.StatusBadRequest, "failed to decode request")
115 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -90,14 +123,22 @@ func (c *controllerV1) handlePostWorkspaceConfigCompact(w http.ResponseWriter, r
90 w.WriteHeader(http.StatusOK)
123 w.WriteHeader(http.StatusOK)
91}
124}
92
125
126+// handlePostWorkspaceConfigProviderKey sets a provider API key.
127+//
128+// @Summary Set provider API key
129+// @Tags config
130+// @Accept json
131+// @Param id path string true "Workspace ID"
132+// @Param request body proto.ConfigProviderKeyRequest true "Config provider key request"
133+// @Success 200
134+// @Failure 400 {object} proto.Error
135+// @Failure 404 {object} proto.Error
136+// @Failure 500 {object} proto.Error
137+// @Router /workspaces/{id}/config/provider-key [post]
93func (c *controllerV1) handlePostWorkspaceConfigProviderKey(w http.ResponseWriter, r *http.Request) {
138func (c *controllerV1) handlePostWorkspaceConfigProviderKey(w http.ResponseWriter, r *http.Request) {
94 id := r.PathValue("id")
139 id := r.PathValue("id")
95
140
96− var req struct {
141+ var req proto.ConfigProviderKeyRequest
97− Scope config.Scope `json:"scope"`
98− ProviderID string `json:"provider_id"`
99− APIKey any `json:"api_key"`
100− }
101 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
142 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
102 c.server.logError(r, "Failed to decode request", "error", err)
143 c.server.logError(r, "Failed to decode request", "error", err)
103 jsonError(w, http.StatusBadRequest, "failed to decode request")
144 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -111,6 +152,16 @@ func (c *controllerV1) handlePostWorkspaceConfigProviderKey(w http.ResponseWrite
111 w.WriteHeader(http.StatusOK)
152 w.WriteHeader(http.StatusOK)
112}
153}
113
154
155+// handlePostWorkspaceConfigImportCopilot imports Copilot credentials.
156+//
157+// @Summary Import Copilot credentials
158+// @Tags config
159+// @Produce json
160+// @Param id path string true "Workspace ID"
161+// @Success 200 {object} proto.ImportCopilotResponse
162+// @Failure 404 {object} proto.Error
163+// @Failure 500 {object} proto.Error
164+// @Router /workspaces/{id}/config/import-copilot [post]
114func (c *controllerV1) handlePostWorkspaceConfigImportCopilot(w http.ResponseWriter, r *http.Request) {
165func (c *controllerV1) handlePostWorkspaceConfigImportCopilot(w http.ResponseWriter, r *http.Request) {
115 id := r.PathValue("id")
166 id := r.PathValue("id")
116 token, ok, err := c.backend.ImportCopilot(id)
167 token, ok, err := c.backend.ImportCopilot(id)
@@ -118,19 +169,25 @@ func (c *controllerV1) handlePostWorkspaceConfigImportCopilot(w http.ResponseWri
118 c.handleError(w, r, err)
169 c.handleError(w, r, err)
119 return
170 return
120 }
171 }
121− jsonEncode(w, struct {
122− Token any `json:"token"`
123− Success bool `json:"success"`
124− }{Token: token, Success: ok})
172+ jsonEncode(w, proto.ImportCopilotResponse{Token: token, Success: ok})
125}
173}
126
174
175+// handlePostWorkspaceConfigRefreshOAuth refreshes an OAuth token for a provider.
176+//
177+// @Summary Refresh OAuth token
178+// @Tags config
179+// @Accept json
180+// @Param id path string true "Workspace ID"
181+// @Param request body proto.ConfigRefreshOAuthRequest true "Refresh OAuth request"
182+// @Success 200
183+// @Failure 400 {object} proto.Error
184+// @Failure 404 {object} proto.Error
185+// @Failure 500 {object} proto.Error
186+// @Router /workspaces/{id}/config/refresh-oauth [post]
127func (c *controllerV1) handlePostWorkspaceConfigRefreshOAuth(w http.ResponseWriter, r *http.Request) {
187func (c *controllerV1) handlePostWorkspaceConfigRefreshOAuth(w http.ResponseWriter, r *http.Request) {
128 id := r.PathValue("id")
188 id := r.PathValue("id")
129
189
130− var req struct {
190+ var req proto.ConfigRefreshOAuthRequest
131− Scope config.Scope `json:"scope"`
132− ProviderID string `json:"provider_id"`
133− }
134 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
191 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
135 c.server.logError(r, "Failed to decode request", "error", err)
192 c.server.logError(r, "Failed to decode request", "error", err)
136 jsonError(w, http.StatusBadRequest, "failed to decode request")
193 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -144,6 +201,16 @@ func (c *controllerV1) handlePostWorkspaceConfigRefreshOAuth(w http.ResponseWrit
144 w.WriteHeader(http.StatusOK)
201 w.WriteHeader(http.StatusOK)
145}
202}
146
203
204+// handleGetWorkspaceProjectNeedsInit reports whether a project needs initialization.
205+//
206+// @Summary Check if project needs initialization
207+// @Tags project
208+// @Produce json
209+// @Param id path string true "Workspace ID"
210+// @Success 200 {object} proto.ProjectNeedsInitResponse
211+// @Failure 404 {object} proto.Error
212+// @Failure 500 {object} proto.Error
213+// @Router /workspaces/{id}/project/needs-init [get]
147func (c *controllerV1) handleGetWorkspaceProjectNeedsInit(w http.ResponseWriter, r *http.Request) {
214func (c *controllerV1) handleGetWorkspaceProjectNeedsInit(w http.ResponseWriter, r *http.Request) {
148 id := r.PathValue("id")
215 id := r.PathValue("id")
149 needs, err := c.backend.ProjectNeedsInitialization(id)
216 needs, err := c.backend.ProjectNeedsInitialization(id)
@@ -151,11 +218,18 @@ func (c *controllerV1) handleGetWorkspaceProjectNeedsInit(w http.ResponseWriter,
151 c.handleError(w, r, err)
218 c.handleError(w, r, err)
152 return
219 return
153 }
220 }
154− jsonEncode(w, struct {
155− NeedsInit bool `json:"needs_init"`
156− }{NeedsInit: needs})
221+ jsonEncode(w, proto.ProjectNeedsInitResponse{NeedsInit: needs})
157}
222}
158
223
224+// handlePostWorkspaceProjectInit marks the project as initialized.
225+//
226+// @Summary Mark project as initialized
227+// @Tags project
228+// @Param id path string true "Workspace ID"
229+// @Success 200
230+// @Failure 404 {object} proto.Error
231+// @Failure 500 {object} proto.Error
232+// @Router /workspaces/{id}/project/init [post]
159func (c *controllerV1) handlePostWorkspaceProjectInit(w http.ResponseWriter, r *http.Request) {
233func (c *controllerV1) handlePostWorkspaceProjectInit(w http.ResponseWriter, r *http.Request) {
160 id := r.PathValue("id")
234 id := r.PathValue("id")
161 if err := c.backend.MarkProjectInitialized(id); err != nil {
235 if err := c.backend.MarkProjectInitialized(id); err != nil {
@@ -165,6 +239,16 @@ func (c *controllerV1) handlePostWorkspaceProjectInit(w http.ResponseWriter, r *
165 w.WriteHeader(http.StatusOK)
239 w.WriteHeader(http.StatusOK)
166}
240}
167
241
242+// handleGetWorkspaceProjectInitPrompt returns the project initialization prompt.
243+//
244+// @Summary Get project initialization prompt
245+// @Tags project
246+// @Produce json
247+// @Param id path string true "Workspace ID"
248+// @Success 200 {object} proto.ProjectInitPromptResponse
249+// @Failure 404 {object} proto.Error
250+// @Failure 500 {object} proto.Error
251+// @Router /workspaces/{id}/project/init-prompt [get]
168func (c *controllerV1) handleGetWorkspaceProjectInitPrompt(w http.ResponseWriter, r *http.Request) {
252func (c *controllerV1) handleGetWorkspaceProjectInitPrompt(w http.ResponseWriter, r *http.Request) {
169 id := r.PathValue("id")
253 id := r.PathValue("id")
170 prompt, err := c.backend.InitializePrompt(id)
254 prompt, err := c.backend.InitializePrompt(id)
@@ -172,17 +256,25 @@ func (c *controllerV1) handleGetWorkspaceProjectInitPrompt(w http.ResponseWriter
172 c.handleError(w, r, err)
256 c.handleError(w, r, err)
173 return
257 return
174 }
258 }
175− jsonEncode(w, struct {
259+ jsonEncode(w, proto.ProjectInitPromptResponse{Prompt: prompt})
176− Prompt string `json:"prompt"`
177− }{Prompt: prompt})
178}
260}
179
261
262+// handlePostWorkspaceMCPRefreshTools refreshes tools for a named MCP server.
263+//
264+// @Summary Refresh MCP tools
265+// @Tags mcp
266+// @Accept json
267+// @Param id path string true "Workspace ID"
268+// @Param request body proto.MCPNameRequest true "MCP name request"
269+// @Success 200
270+// @Failure 400 {object} proto.Error
271+// @Failure 404 {object} proto.Error
272+// @Failure 500 {object} proto.Error
273+// @Router /workspaces/{id}/mcp/refresh-tools [post]
180func (c *controllerV1) handlePostWorkspaceMCPRefreshTools(w http.ResponseWriter, r *http.Request) {
274func (c *controllerV1) handlePostWorkspaceMCPRefreshTools(w http.ResponseWriter, r *http.Request) {
181 id := r.PathValue("id")
275 id := r.PathValue("id")
182
276
183− var req struct {
277+ var req proto.MCPNameRequest
184− Name string `json:"name"`
185− }
186 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
278 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
187 c.server.logError(r, "Failed to decode request", "error", err)
279 c.server.logError(r, "Failed to decode request", "error", err)
188 jsonError(w, http.StatusBadRequest, "failed to decode request")
280 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -196,13 +288,23 @@ func (c *controllerV1) handlePostWorkspaceMCPRefreshTools(w http.ResponseWriter,
196 w.WriteHeader(http.StatusOK)
288 w.WriteHeader(http.StatusOK)
197}
289}
198
290
291+// handlePostWorkspaceMCPReadResource reads a resource from an MCP server.
292+//
293+// @Summary Read MCP resource
294+// @Tags mcp
295+// @Accept json
296+// @Produce json
297+// @Param id path string true "Workspace ID"
298+// @Param request body proto.MCPReadResourceRequest true "MCP read resource request"
299+// @Success 200 {object} object
300+// @Failure 400 {object} proto.Error
301+// @Failure 404 {object} proto.Error
302+// @Failure 500 {object} proto.Error
303+// @Router /workspaces/{id}/mcp/read-resource [post]
199func (c *controllerV1) handlePostWorkspaceMCPReadResource(w http.ResponseWriter, r *http.Request) {
304func (c *controllerV1) handlePostWorkspaceMCPReadResource(w http.ResponseWriter, r *http.Request) {
200 id := r.PathValue("id")
305 id := r.PathValue("id")
201
306
202− var req struct {
307+ var req proto.MCPReadResourceRequest
203− Name string `json:"name"`
204− URI string `json:"uri"`
205− }
206 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
308 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
207 c.server.logError(r, "Failed to decode request", "error", err)
309 c.server.logError(r, "Failed to decode request", "error", err)
208 jsonError(w, http.StatusBadRequest, "failed to decode request")
310 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -217,14 +319,23 @@ func (c *controllerV1) handlePostWorkspaceMCPReadResource(w http.ResponseWriter,
217 jsonEncode(w, contents)
319 jsonEncode(w, contents)
218}
320}
219
321
322+// handlePostWorkspaceMCPGetPrompt retrieves a prompt from an MCP server.
323+//
324+// @Summary Get MCP prompt
325+// @Tags mcp
326+// @Accept json
327+// @Produce json
328+// @Param id path string true "Workspace ID"
329+// @Param request body proto.MCPGetPromptRequest true "MCP get prompt request"
330+// @Success 200 {object} proto.MCPGetPromptResponse
331+// @Failure 400 {object} proto.Error
332+// @Failure 404 {object} proto.Error
333+// @Failure 500 {object} proto.Error
334+// @Router /workspaces/{id}/mcp/get-prompt [post]
220func (c *controllerV1) handlePostWorkspaceMCPGetPrompt(w http.ResponseWriter, r *http.Request) {
335func (c *controllerV1) handlePostWorkspaceMCPGetPrompt(w http.ResponseWriter, r *http.Request) {
221 id := r.PathValue("id")
336 id := r.PathValue("id")
222
337
223− var req struct {
338+ var req proto.MCPGetPromptRequest
224− ClientID string `json:"client_id"`
225− PromptID string `json:"prompt_id"`
226− Args map[string]string `json:"args"`
227− }
228 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
339 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
229 c.server.logError(r, "Failed to decode request", "error", err)
340 c.server.logError(r, "Failed to decode request", "error", err)
230 jsonError(w, http.StatusBadRequest, "failed to decode request")
341 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -236,11 +347,19 @@ func (c *controllerV1) handlePostWorkspaceMCPGetPrompt(w http.ResponseWriter, r
236 c.handleError(w, r, err)
347 c.handleError(w, r, err)
237 return
348 return
238 }
349 }
239− jsonEncode(w, struct {
240− Prompt string `json:"prompt"`
241− }{Prompt: prompt})
350+ jsonEncode(w, proto.MCPGetPromptResponse{Prompt: prompt})
242}
351}
243
352
353+// handleGetWorkspaceMCPStates returns the state of all MCP clients.
354+//
355+// @Summary Get MCP client states
356+// @Tags mcp
357+// @Produce json
358+// @Param id path string true "Workspace ID"
359+// @Success 200 {object} map[string]proto.MCPClientInfo
360+// @Failure 404 {object} proto.Error
361+// @Failure 500 {object} proto.Error
362+// @Router /workspaces/{id}/mcp/states [get]
244func (c *controllerV1) handleGetWorkspaceMCPStates(w http.ResponseWriter, r *http.Request) {
363func (c *controllerV1) handleGetWorkspaceMCPStates(w http.ResponseWriter, r *http.Request) {
245 id := r.PathValue("id")
364 id := r.PathValue("id")
246 states := c.backend.MCPGetStates(id)
365 states := c.backend.MCPGetStates(id)
@@ -259,12 +378,22 @@ func (c *controllerV1) handleGetWorkspaceMCPStates(w http.ResponseWriter, r *htt
259 jsonEncode(w, result)
378 jsonEncode(w, result)
260}
379}
261
380
381+// handlePostWorkspaceMCPRefreshPrompts refreshes prompts for a named MCP server.
382+//
383+// @Summary Refresh MCP prompts
384+// @Tags mcp
385+// @Accept json
386+// @Param id path string true "Workspace ID"
387+// @Param request body proto.MCPNameRequest true "MCP name request"
388+// @Success 200
389+// @Failure 400 {object} proto.Error
390+// @Failure 404 {object} proto.Error
391+// @Failure 500 {object} proto.Error
392+// @Router /workspaces/{id}/mcp/refresh-prompts [post]
262func (c *controllerV1) handlePostWorkspaceMCPRefreshPrompts(w http.ResponseWriter, r *http.Request) {
393func (c *controllerV1) handlePostWorkspaceMCPRefreshPrompts(w http.ResponseWriter, r *http.Request) {
263 id := r.PathValue("id")
394 id := r.PathValue("id")
264
395
265− var req struct {
396+ var req proto.MCPNameRequest
266− Name string `json:"name"`
267− }
268 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
397 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
269 c.server.logError(r, "Failed to decode request", "error", err)
398 c.server.logError(r, "Failed to decode request", "error", err)
270 jsonError(w, http.StatusBadRequest, "failed to decode request")
399 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -275,12 +404,22 @@ func (c *controllerV1) handlePostWorkspaceMCPRefreshPrompts(w http.ResponseWrite
275 w.WriteHeader(http.StatusOK)
404 w.WriteHeader(http.StatusOK)
276}
405}
277
406
407+// handlePostWorkspaceMCPRefreshResources refreshes resources for a named MCP server.
408+//
409+// @Summary Refresh MCP resources
410+// @Tags mcp
411+// @Accept json
412+// @Param id path string true "Workspace ID"
413+// @Param request body proto.MCPNameRequest true "MCP name request"
414+// @Success 200
415+// @Failure 400 {object} proto.Error
416+// @Failure 404 {object} proto.Error
417+// @Failure 500 {object} proto.Error
418+// @Router /workspaces/{id}/mcp/refresh-resources [post]
278func (c *controllerV1) handlePostWorkspaceMCPRefreshResources(w http.ResponseWriter, r *http.Request) {
419func (c *controllerV1) handlePostWorkspaceMCPRefreshResources(w http.ResponseWriter, r *http.Request) {
279 id := r.PathValue("id")
420 id := r.PathValue("id")
280
421
281− var req struct {
422+ var req proto.MCPNameRequest
282− Name string `json:"name"`
283− }
284 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
423 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
285 c.server.logError(r, "Failed to decode request", "error", err)
424 c.server.logError(r, "Failed to decode request", "error", err)
286 jsonError(w, http.StatusBadRequest, "failed to decode request")
425 jsonError(w, http.StatusBadRequest, "failed to decode request")
internal/server/proto.go+423−7
@@ -16,14 +16,36 @@ type controllerV1 struct {
16 server *Server
16 server *Server
17}
17}
18
18
19+// handleGetHealth checks server health.
20+//
21+// @Summary Health check
22+// @Tags system
23+// @Success 200
24+// @Router /health [get]
19func (c *controllerV1) handleGetHealth(w http.ResponseWriter, _ *http.Request) {
25func (c *controllerV1) handleGetHealth(w http.ResponseWriter, _ *http.Request) {
20 w.WriteHeader(http.StatusOK)
26 w.WriteHeader(http.StatusOK)
21}
27}
22
28
29+// handleGetVersion returns server version information.
30+//
31+// @Summary Get server version
32+// @Tags system
33+// @Produce json
34+// @Success 200 {object} proto.VersionInfo
35+// @Router /version [get]
23func (c *controllerV1) handleGetVersion(w http.ResponseWriter, _ *http.Request) {
36func (c *controllerV1) handleGetVersion(w http.ResponseWriter, _ *http.Request) {
24 jsonEncode(w, c.backend.VersionInfo())
37 jsonEncode(w, c.backend.VersionInfo())
25}
38}
26
39
40+// handlePostControl sends a control command to the server.
41+//
42+// @Summary Send server control command
43+// @Tags system
44+// @Accept json
45+// @Param request body proto.ServerControl true "Control command (e.g. shutdown)"
46+// @Success 200
47+// @Failure 400 {object} proto.Error
48+// @Router /control [post]
27func (c *controllerV1) handlePostControl(w http.ResponseWriter, r *http.Request) {
49func (c *controllerV1) handlePostControl(w http.ResponseWriter, r *http.Request) {
28 var req proto.ServerControl
50 var req proto.ServerControl
29 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
51 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -42,14 +64,38 @@ func (c *controllerV1) handlePostControl(w http.ResponseWriter, r *http.Request)
42 }
64 }
43}
65}
44
66
67+// handleGetConfig returns global server configuration.
68+//
69+// @Summary Get server config
70+// @Tags system
71+// @Produce json
72+// @Success 200 {object} object
73+// @Router /config [get]
45func (c *controllerV1) handleGetConfig(w http.ResponseWriter, _ *http.Request) {
74func (c *controllerV1) handleGetConfig(w http.ResponseWriter, _ *http.Request) {
46 jsonEncode(w, c.backend.Config())
75 jsonEncode(w, c.backend.Config())
47}
76}
48
77
78+// handleGetWorkspaces lists all workspaces.
79+//
80+// @Summary List workspaces
81+// @Tags workspaces
82+// @Produce json
83+// @Success 200 {array} proto.Workspace
84+// @Router /workspaces [get]
49func (c *controllerV1) handleGetWorkspaces(w http.ResponseWriter, _ *http.Request) {
85func (c *controllerV1) handleGetWorkspaces(w http.ResponseWriter, _ *http.Request) {
50 jsonEncode(w, c.backend.ListWorkspaces())
86 jsonEncode(w, c.backend.ListWorkspaces())
51}
87}
52
88
89+// handleGetWorkspace returns a single workspace by ID.
90+//
91+// @Summary Get workspace
92+// @Tags workspaces
93+// @Produce json
94+// @Param id path string true "Workspace ID"
95+// @Success 200 {object} proto.Workspace
96+// @Failure 404 {object} proto.Error
97+// @Failure 500 {object} proto.Error
98+// @Router /workspaces/{id} [get]
53func (c *controllerV1) handleGetWorkspace(w http.ResponseWriter, r *http.Request) {
99func (c *controllerV1) handleGetWorkspace(w http.ResponseWriter, r *http.Request) {
54 id := r.PathValue("id")
100 id := r.PathValue("id")
55 ws, err := c.backend.GetWorkspaceProto(id)
101 ws, err := c.backend.GetWorkspaceProto(id)
@@ -60,6 +106,17 @@ func (c *controllerV1) handleGetWorkspace(w http.ResponseWriter, r *http.Request
60 jsonEncode(w, ws)
106 jsonEncode(w, ws)
61}
107}
62
108
109+// handlePostWorkspaces creates a new workspace.
110+//
111+// @Summary Create workspace
112+// @Tags workspaces
113+// @Accept json
114+// @Produce json
115+// @Param request body proto.Workspace true "Workspace creation params"
116+// @Success 200 {object} proto.Workspace
117+// @Failure 400 {object} proto.Error
118+// @Failure 500 {object} proto.Error
119+// @Router /workspaces [post]
63func (c *controllerV1) handlePostWorkspaces(w http.ResponseWriter, r *http.Request) {
120func (c *controllerV1) handlePostWorkspaces(w http.ResponseWriter, r *http.Request) {
64 var args proto.Workspace
121 var args proto.Workspace
65 if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
122 if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
@@ -76,11 +133,29 @@ func (c *controllerV1) handlePostWorkspaces(w http.ResponseWriter, r *http.Reque
76 jsonEncode(w, result)
133 jsonEncode(w, result)
77}
134}
78
135
136+// handleDeleteWorkspaces deletes a workspace.
137+//
138+// @Summary Delete workspace
139+// @Tags workspaces
140+// @Param id path string true "Workspace ID"
141+// @Success 200
142+// @Failure 404 {object} proto.Error
143+// @Router /workspaces/{id} [delete]
79func (c *controllerV1) handleDeleteWorkspaces(w http.ResponseWriter, r *http.Request) {
144func (c *controllerV1) handleDeleteWorkspaces(w http.ResponseWriter, r *http.Request) {
80 id := r.PathValue("id")
145 id := r.PathValue("id")
81 c.backend.DeleteWorkspace(id)
146 c.backend.DeleteWorkspace(id)
82}
147}
83
148
149+// handleGetWorkspaceConfig returns workspace configuration.
150+//
151+// @Summary Get workspace config
152+// @Tags workspaces
153+// @Produce json
154+// @Param id path string true "Workspace ID"
155+// @Success 200 {object} object
156+// @Failure 404 {object} proto.Error
157+// @Failure 500 {object} proto.Error
158+// @Router /workspaces/{id}/config [get]
84func (c *controllerV1) handleGetWorkspaceConfig(w http.ResponseWriter, r *http.Request) {
159func (c *controllerV1) handleGetWorkspaceConfig(w http.ResponseWriter, r *http.Request) {
85 id := r.PathValue("id")
160 id := r.PathValue("id")
86 cfg, err := c.backend.GetWorkspaceConfig(id)
161 cfg, err := c.backend.GetWorkspaceConfig(id)
@@ -91,6 +166,16 @@ func (c *controllerV1) handleGetWorkspaceConfig(w http.ResponseWriter, r *http.R
91 jsonEncode(w, cfg)
166 jsonEncode(w, cfg)
92}
167}
93
168
169+// handleGetWorkspaceProviders lists available providers for a workspace.
170+//
171+// @Summary Get workspace providers
172+// @Tags workspaces
173+// @Produce json
174+// @Param id path string true "Workspace ID"
175+// @Success 200 {object} object
176+// @Failure 404 {object} proto.Error
177+// @Failure 500 {object} proto.Error
178+// @Router /workspaces/{id}/providers [get]
94func (c *controllerV1) handleGetWorkspaceProviders(w http.ResponseWriter, r *http.Request) {
179func (c *controllerV1) handleGetWorkspaceProviders(w http.ResponseWriter, r *http.Request) {
95 id := r.PathValue("id")
180 id := r.PathValue("id")
96 providers, err := c.backend.GetWorkspaceProviders(id)
181 providers, err := c.backend.GetWorkspaceProviders(id)
@@ -101,6 +186,16 @@ func (c *controllerV1) handleGetWorkspaceProviders(w http.ResponseWriter, r *htt
101 jsonEncode(w, providers)
186 jsonEncode(w, providers)
102}
187}
103
188
189+// handleGetWorkspaceEvents streams workspace events as Server-Sent Events.
190+//
191+// @Summary Stream workspace events (SSE)
192+// @Tags workspaces
193+// @Produce text/event-stream
194+// @Param id path string true "Workspace ID"
195+// @Success 200
196+// @Failure 404 {object} proto.Error
197+// @Failure 500 {object} proto.Error
198+// @Router /workspaces/{id}/events [get]
104func (c *controllerV1) handleGetWorkspaceEvents(w http.ResponseWriter, r *http.Request) {
199func (c *controllerV1) handleGetWorkspaceEvents(w http.ResponseWriter, r *http.Request) {
105 flusher := http.NewResponseController(w)
200 flusher := http.NewResponseController(w)
106 id := r.PathValue("id")
201 id := r.PathValue("id")
@@ -140,6 +235,16 @@ func (c *controllerV1) handleGetWorkspaceEvents(w http.ResponseWriter, r *http.R
140 }
235 }
141}
236}
142
237
238+// handleGetWorkspaceLSPs lists LSP clients for a workspace.
239+//
240+// @Summary List LSP clients
241+// @Tags lsp
242+// @Produce json
243+// @Param id path string true "Workspace ID"
244+// @Success 200 {object} map[string]proto.LSPClientInfo
245+// @Failure 404 {object} proto.Error
246+// @Failure 500 {object} proto.Error
247+// @Router /workspaces/{id}/lsps [get]
143func (c *controllerV1) handleGetWorkspaceLSPs(w http.ResponseWriter, r *http.Request) {
248func (c *controllerV1) handleGetWorkspaceLSPs(w http.ResponseWriter, r *http.Request) {
144 id := r.PathValue("id")
249 id := r.PathValue("id")
145 states, err := c.backend.GetLSPStates(id)
250 states, err := c.backend.GetLSPStates(id)
@@ -160,6 +265,17 @@ func (c *controllerV1) handleGetWorkspaceLSPs(w http.ResponseWriter, r *http.Req
160 jsonEncode(w, result)
265 jsonEncode(w, result)
161}
266}
162
267
268+// handleGetWorkspaceLSPDiagnostics returns diagnostics for an LSP client.
269+//
270+// @Summary Get LSP diagnostics
271+// @Tags lsp
272+// @Produce json
273+// @Param id path string true "Workspace ID"
274+// @Param lsp path string true "LSP client name"
275+// @Success 200 {object} object
276+// @Failure 404 {object} proto.Error
277+// @Failure 500 {object} proto.Error
278+// @Router /workspaces/{id}/lsps/{lsp}/diagnostics [get]
163func (c *controllerV1) handleGetWorkspaceLSPDiagnostics(w http.ResponseWriter, r *http.Request) {
279func (c *controllerV1) handleGetWorkspaceLSPDiagnostics(w http.ResponseWriter, r *http.Request) {
164 id := r.PathValue("id")
280 id := r.PathValue("id")
165 lspName := r.PathValue("lsp")
281 lspName := r.PathValue("lsp")
@@ -171,6 +287,16 @@ func (c *controllerV1) handleGetWorkspaceLSPDiagnostics(w http.ResponseWriter, r
171 jsonEncode(w, diagnostics)
287 jsonEncode(w, diagnostics)
172}
288}
173
289
290+// handleGetWorkspaceSessions lists sessions for a workspace.
291+//
292+// @Summary List sessions
293+// @Tags sessions
294+// @Produce json
295+// @Param id path string true "Workspace ID"
296+// @Success 200 {array} proto.Session
297+// @Failure 404 {object} proto.Error
298+// @Failure 500 {object} proto.Error
299+// @Router /workspaces/{id}/sessions [get]
174func (c *controllerV1) handleGetWorkspaceSessions(w http.ResponseWriter, r *http.Request) {
300func (c *controllerV1) handleGetWorkspaceSessions(w http.ResponseWriter, r *http.Request) {
175 id := r.PathValue("id")
301 id := r.PathValue("id")
176 sessions, err := c.backend.ListSessions(r.Context(), id)
302 sessions, err := c.backend.ListSessions(r.Context(), id)
@@ -181,6 +307,19 @@ func (c *controllerV1) handleGetWorkspaceSessions(w http.ResponseWriter, r *http
181 jsonEncode(w, sessions)
307 jsonEncode(w, sessions)
182}
308}
183
309
310+// handlePostWorkspaceSessions creates a new session in a workspace.
311+//
312+// @Summary Create session
313+// @Tags sessions
314+// @Accept json
315+// @Produce json
316+// @Param id path string true "Workspace ID"
317+// @Param request body proto.Session true "Session creation params (title)"
318+// @Success 200 {object} proto.Session
319+// @Failure 400 {object} proto.Error
320+// @Failure 404 {object} proto.Error
321+// @Failure 500 {object} proto.Error
322+// @Router /workspaces/{id}/sessions [post]
184func (c *controllerV1) handlePostWorkspaceSessions(w http.ResponseWriter, r *http.Request) {
323func (c *controllerV1) handlePostWorkspaceSessions(w http.ResponseWriter, r *http.Request) {
185 id := r.PathValue("id")
324 id := r.PathValue("id")
186
325
@@ -199,6 +338,17 @@ func (c *controllerV1) handlePostWorkspaceSessions(w http.ResponseWriter, r *htt
199 jsonEncode(w, sess)
338 jsonEncode(w, sess)
200}
339}
201
340
341+// handleGetWorkspaceSession returns a single session.
342+//
343+// @Summary Get session
344+// @Tags sessions
345+// @Produce json
346+// @Param id path string true "Workspace ID"
347+// @Param sid path string true "Session ID"
348+// @Success 200 {object} proto.Session
349+// @Failure 404 {object} proto.Error
350+// @Failure 500 {object} proto.Error
351+// @Router /workspaces/{id}/sessions/{sid} [get]
202func (c *controllerV1) handleGetWorkspaceSession(w http.ResponseWriter, r *http.Request) {
352func (c *controllerV1) handleGetWorkspaceSession(w http.ResponseWriter, r *http.Request) {
203 id := r.PathValue("id")
353 id := r.PathValue("id")
204 sid := r.PathValue("sid")
354 sid := r.PathValue("sid")
@@ -210,6 +360,17 @@ func (c *controllerV1) handleGetWorkspaceSession(w http.ResponseWriter, r *http.
210 jsonEncode(w, sess)
360 jsonEncode(w, sess)
211}
361}
212
362
363+// handleGetWorkspaceSessionHistory returns the history for a session.
364+//
365+// @Summary Get session history
366+// @Tags sessions
367+// @Produce json
368+// @Param id path string true "Workspace ID"
369+// @Param sid path string true "Session ID"
370+// @Success 200 {array} proto.File
371+// @Failure 404 {object} proto.Error
372+// @Failure 500 {object} proto.Error
373+// @Router /workspaces/{id}/sessions/{sid}/history [get]
213func (c *controllerV1) handleGetWorkspaceSessionHistory(w http.ResponseWriter, r *http.Request) {
374func (c *controllerV1) handleGetWorkspaceSessionHistory(w http.ResponseWriter, r *http.Request) {
214 id := r.PathValue("id")
375 id := r.PathValue("id")
215 sid := r.PathValue("sid")
376 sid := r.PathValue("sid")
@@ -221,6 +382,17 @@ func (c *controllerV1) handleGetWorkspaceSessionHistory(w http.ResponseWriter, r
221 jsonEncode(w, history)
382 jsonEncode(w, history)
222}
383}
223
384
385+// handleGetWorkspaceSessionMessages returns all messages for a session.
386+//
387+// @Summary Get session messages
388+// @Tags sessions
389+// @Produce json
390+// @Param id path string true "Workspace ID"
391+// @Param sid path string true "Session ID"
392+// @Success 200 {array} proto.Message
393+// @Failure 404 {object} proto.Error
394+// @Failure 500 {object} proto.Error
395+// @Router /workspaces/{id}/sessions/{sid}/messages [get]
224func (c *controllerV1) handleGetWorkspaceSessionMessages(w http.ResponseWriter, r *http.Request) {
396func (c *controllerV1) handleGetWorkspaceSessionMessages(w http.ResponseWriter, r *http.Request) {
225 id := r.PathValue("id")
397 id := r.PathValue("id")
226 sid := r.PathValue("sid")
398 sid := r.PathValue("sid")
@@ -232,6 +404,20 @@ func (c *controllerV1) handleGetWorkspaceSessionMessages(w http.ResponseWriter,
232 jsonEncode(w, messagesToProto(messages))
404 jsonEncode(w, messagesToProto(messages))
233}
405}
234
406
407+// handlePutWorkspaceSession updates a session.
408+//
409+// @Summary Update session
410+// @Tags sessions
411+// @Accept json
412+// @Produce json
413+// @Param id path string true "Workspace ID"
414+// @Param sid path string true "Session ID"
415+// @Param request body proto.Session true "Updated session"
416+// @Success 200 {object} proto.Session
417+// @Failure 400 {object} proto.Error
418+// @Failure 404 {object} proto.Error
419+// @Failure 500 {object} proto.Error
420+// @Router /workspaces/{id}/sessions/{sid} [put]
235func (c *controllerV1) handlePutWorkspaceSession(w http.ResponseWriter, r *http.Request) {
421func (c *controllerV1) handlePutWorkspaceSession(w http.ResponseWriter, r *http.Request) {
236 id := r.PathValue("id")
422 id := r.PathValue("id")
237
423
@@ -250,6 +436,16 @@ func (c *controllerV1) handlePutWorkspaceSession(w http.ResponseWriter, r *http.
250 jsonEncode(w, saved)
436 jsonEncode(w, saved)
251}
437}
252
438
439+// handleDeleteWorkspaceSession deletes a session.
440+//
441+// @Summary Delete session
442+// @Tags sessions
443+// @Param id path string true "Workspace ID"
444+// @Param sid path string true "Session ID"
445+// @Success 200
446+// @Failure 404 {object} proto.Error
447+// @Failure 500 {object} proto.Error
448+// @Router /workspaces/{id}/sessions/{sid} [delete]
253func (c *controllerV1) handleDeleteWorkspaceSession(w http.ResponseWriter, r *http.Request) {
449func (c *controllerV1) handleDeleteWorkspaceSession(w http.ResponseWriter, r *http.Request) {
254 id := r.PathValue("id")
450 id := r.PathValue("id")
255 sid := r.PathValue("sid")
451 sid := r.PathValue("sid")
@@ -260,6 +456,17 @@ func (c *controllerV1) handleDeleteWorkspaceSession(w http.ResponseWriter, r *ht
260 w.WriteHeader(http.StatusOK)
456 w.WriteHeader(http.StatusOK)
261}
457}
262
458
459+// handleGetWorkspaceSessionUserMessages returns user messages for a session.
460+//
461+// @Summary Get user messages for session
462+// @Tags sessions
463+// @Produce json
464+// @Param id path string true "Workspace ID"
465+// @Param sid path string true "Session ID"
466+// @Success 200 {array} proto.Message
467+// @Failure 404 {object} proto.Error
468+// @Failure 500 {object} proto.Error
469+// @Router /workspaces/{id}/sessions/{sid}/messages/user [get]
263func (c *controllerV1) handleGetWorkspaceSessionUserMessages(w http.ResponseWriter, r *http.Request) {
470func (c *controllerV1) handleGetWorkspaceSessionUserMessages(w http.ResponseWriter, r *http.Request) {
264 id := r.PathValue("id")
471 id := r.PathValue("id")
265 sid := r.PathValue("sid")
472 sid := r.PathValue("sid")
@@ -271,6 +478,16 @@ func (c *controllerV1) handleGetWorkspaceSessionUserMessages(w http.ResponseWrit
271 jsonEncode(w, messagesToProto(messages))
478 jsonEncode(w, messagesToProto(messages))
272}
479}
273
480
481+// handleGetWorkspaceAllUserMessages returns all user messages across sessions.
482+//
483+// @Summary Get all user messages for workspace
484+// @Tags workspaces
485+// @Produce json
486+// @Param id path string true "Workspace ID"
487+// @Success 200 {array} proto.Message
488+// @Failure 404 {object} proto.Error
489+// @Failure 500 {object} proto.Error
490+// @Router /workspaces/{id}/messages/user [get]
274func (c *controllerV1) handleGetWorkspaceAllUserMessages(w http.ResponseWriter, r *http.Request) {
491func (c *controllerV1) handleGetWorkspaceAllUserMessages(w http.ResponseWriter, r *http.Request) {
275 id := r.PathValue("id")
492 id := r.PathValue("id")
276 messages, err := c.backend.ListAllUserMessages(r.Context(), id)
493 messages, err := c.backend.ListAllUserMessages(r.Context(), id)
@@ -281,6 +498,17 @@ func (c *controllerV1) handleGetWorkspaceAllUserMessages(w http.ResponseWriter,
281 jsonEncode(w, messagesToProto(messages))
498 jsonEncode(w, messagesToProto(messages))
282}
499}
283
500
501+// handleGetWorkspaceSessionFileTrackerFiles lists files read in a session.
502+//
503+// @Summary List tracked files for session
504+// @Tags filetracker
505+// @Produce json
506+// @Param id path string true "Workspace ID"
507+// @Param sid path string true "Session ID"
508+// @Success 200 {array} string
509+// @Failure 404 {object} proto.Error
510+// @Failure 500 {object} proto.Error
511+// @Router /workspaces/{id}/sessions/{sid}/filetracker/files [get]
284func (c *controllerV1) handleGetWorkspaceSessionFileTrackerFiles(w http.ResponseWriter, r *http.Request) {
512func (c *controllerV1) handleGetWorkspaceSessionFileTrackerFiles(w http.ResponseWriter, r *http.Request) {
285 id := r.PathValue("id")
513 id := r.PathValue("id")
286 sid := r.PathValue("sid")
514 sid := r.PathValue("sid")
@@ -292,13 +520,22 @@ func (c *controllerV1) handleGetWorkspaceSessionFileTrackerFiles(w http.Response
292 jsonEncode(w, files)
520 jsonEncode(w, files)
293}
521}
294
522
523+// handlePostWorkspaceFileTrackerRead records a file read event.
524+//
525+// @Summary Record file read
526+// @Tags filetracker
527+// @Accept json
528+// @Param id path string true "Workspace ID"
529+// @Param request body proto.FileTrackerReadRequest true "File tracker read request"
530+// @Success 200
531+// @Failure 400 {object} proto.Error
532+// @Failure 404 {object} proto.Error
533+// @Failure 500 {object} proto.Error
534+// @Router /workspaces/{id}/filetracker/read [post]
295func (c *controllerV1) handlePostWorkspaceFileTrackerRead(w http.ResponseWriter, r *http.Request) {
535func (c *controllerV1) handlePostWorkspaceFileTrackerRead(w http.ResponseWriter, r *http.Request) {
296 id := r.PathValue("id")
536 id := r.PathValue("id")
297
537
298− var req struct {
538+ var req proto.FileTrackerReadRequest
299− SessionID string `json:"session_id"`
300− Path string `json:"path"`
301− }
302 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
539 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
303 c.server.logError(r, "Failed to decode request", "error", err)
540 c.server.logError(r, "Failed to decode request", "error", err)
304 jsonError(w, http.StatusBadRequest, "failed to decode request")
541 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -312,6 +549,18 @@ func (c *controllerV1) handlePostWorkspaceFileTrackerRead(w http.ResponseWriter,
312 w.WriteHeader(http.StatusOK)
549 w.WriteHeader(http.StatusOK)
313}
550}
314
551
552+// handleGetWorkspaceFileTrackerLastRead returns the last read time for a file.
553+//
554+// @Summary Get last read time for file
555+// @Tags filetracker
556+// @Produce json
557+// @Param id path string true "Workspace ID"
558+// @Param session_id query string false "Session ID"
559+// @Param path query string true "File path"
560+// @Success 200 {object} object
561+// @Failure 404 {object} proto.Error
562+// @Failure 500 {object} proto.Error
563+// @Router /workspaces/{id}/filetracker/lastread [get]
315func (c *controllerV1) handleGetWorkspaceFileTrackerLastRead(w http.ResponseWriter, r *http.Request) {
564func (c *controllerV1) handleGetWorkspaceFileTrackerLastRead(w http.ResponseWriter, r *http.Request) {
316 id := r.PathValue("id")
565 id := r.PathValue("id")
317 sid := r.URL.Query().Get("session_id")
566 sid := r.URL.Query().Get("session_id")
@@ -325,12 +574,22 @@ func (c *controllerV1) handleGetWorkspaceFileTrackerLastRead(w http.ResponseWrit
325 jsonEncode(w, t)
574 jsonEncode(w, t)
326}
575}
327
576
577+// handlePostWorkspaceLSPStart starts an LSP server for a path.
578+//
579+// @Summary Start LSP server
580+// @Tags lsp
581+// @Accept json
582+// @Param id path string true "Workspace ID"
583+// @Param request body proto.LSPStartRequest true "LSP start request"
584+// @Success 200
585+// @Failure 400 {object} proto.Error
586+// @Failure 404 {object} proto.Error
587+// @Failure 500 {object} proto.Error
588+// @Router /workspaces/{id}/lsps/start [post]
328func (c *controllerV1) handlePostWorkspaceLSPStart(w http.ResponseWriter, r *http.Request) {
589func (c *controllerV1) handlePostWorkspaceLSPStart(w http.ResponseWriter, r *http.Request) {
329 id := r.PathValue("id")
590 id := r.PathValue("id")
330
591
331− var req struct {
592+ var req proto.LSPStartRequest
332− Path string `json:"path"`
333− }
334 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
593 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
335 c.server.logError(r, "Failed to decode request", "error", err)
594 c.server.logError(r, "Failed to decode request", "error", err)
336 jsonError(w, http.StatusBadRequest, "failed to decode request")
595 jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -344,6 +603,15 @@ func (c *controllerV1) handlePostWorkspaceLSPStart(w http.ResponseWriter, r *htt
344 w.WriteHeader(http.StatusOK)
603 w.WriteHeader(http.StatusOK)
345}
604}
346
605
606+// handlePostWorkspaceLSPStopAll stops all LSP servers.
607+//
608+// @Summary Stop all LSP servers
609+// @Tags lsp
610+// @Param id path string true "Workspace ID"
611+// @Success 200
612+// @Failure 404 {object} proto.Error
613+// @Failure 500 {object} proto.Error
614+// @Router /workspaces/{id}/lsps/stop [post]
347func (c *controllerV1) handlePostWorkspaceLSPStopAll(w http.ResponseWriter, r *http.Request) {
615func (c *controllerV1) handlePostWorkspaceLSPStopAll(w http.ResponseWriter, r *http.Request) {
348 id := r.PathValue("id")
616 id := r.PathValue("id")
349 if err := c.backend.LSPStopAll(r.Context(), id); err != nil {
617 if err := c.backend.LSPStopAll(r.Context(), id); err != nil {
@@ -353,6 +621,16 @@ func (c *controllerV1) handlePostWorkspaceLSPStopAll(w http.ResponseWriter, r *h
353 w.WriteHeader(http.StatusOK)
621 w.WriteHeader(http.StatusOK)
354}
622}
355
623
624+// handleGetWorkspaceAgent returns agent info for a workspace.
625+//
626+// @Summary Get agent info
627+// @Tags agent
628+// @Produce json
629+// @Param id path string true "Workspace ID"
630+// @Success 200 {object} proto.AgentInfo
631+// @Failure 404 {object} proto.Error
632+// @Failure 500 {object} proto.Error
633+// @Router /workspaces/{id}/agent [get]
356func (c *controllerV1) handleGetWorkspaceAgent(w http.ResponseWriter, r *http.Request) {
634func (c *controllerV1) handleGetWorkspaceAgent(w http.ResponseWriter, r *http.Request) {
357 id := r.PathValue("id")
635 id := r.PathValue("id")
358 info, err := c.backend.GetAgentInfo(id)
636 info, err := c.backend.GetAgentInfo(id)
@@ -363,6 +641,18 @@ func (c *controllerV1) handleGetWorkspaceAgent(w http.ResponseWriter, r *http.Re
363 jsonEncode(w, info)
641 jsonEncode(w, info)
364}
642}
365
643
644+// handlePostWorkspaceAgent sends a message to the agent.
645+//
646+// @Summary Send message to agent
647+// @Tags agent
648+// @Accept json
649+// @Param id path string true "Workspace ID"
650+// @Param request body proto.AgentMessage true "Agent message"
651+// @Success 200
652+// @Failure 400 {object} proto.Error
653+// @Failure 404 {object} proto.Error
654+// @Failure 500 {object} proto.Error
655+// @Router /workspaces/{id}/agent [post]
366func (c *controllerV1) handlePostWorkspaceAgent(w http.ResponseWriter, r *http.Request) {
656func (c *controllerV1) handlePostWorkspaceAgent(w http.ResponseWriter, r *http.Request) {
367 id := r.PathValue("id")
657 id := r.PathValue("id")
368
658
@@ -380,6 +670,15 @@ func (c *controllerV1) handlePostWorkspaceAgent(w http.ResponseWriter, r *http.R
380 w.WriteHeader(http.StatusOK)
670 w.WriteHeader(http.StatusOK)
381}
671}
382
672
673+// handlePostWorkspaceAgentInit initializes the agent for a workspace.
674+//
675+// @Summary Initialize agent
676+// @Tags agent
677+// @Param id path string true "Workspace ID"
678+// @Success 200
679+// @Failure 404 {object} proto.Error
680+// @Failure 500 {object} proto.Error
681+// @Router /workspaces/{id}/agent/init [post]
383func (c *controllerV1) handlePostWorkspaceAgentInit(w http.ResponseWriter, r *http.Request) {
682func (c *controllerV1) handlePostWorkspaceAgentInit(w http.ResponseWriter, r *http.Request) {
384 id := r.PathValue("id")
683 id := r.PathValue("id")
385 if err := c.backend.InitAgent(r.Context(), id); err != nil {
684 if err := c.backend.InitAgent(r.Context(), id); err != nil {
@@ -389,6 +688,15 @@ func (c *controllerV1) handlePostWorkspaceAgentInit(w http.ResponseWriter, r *ht
389 w.WriteHeader(http.StatusOK)
688 w.WriteHeader(http.StatusOK)
390}
689}
391
690
691+// handlePostWorkspaceAgentUpdate updates the agent for a workspace.
692+//
693+// @Summary Update agent
694+// @Tags agent
695+// @Param id path string true "Workspace ID"
696+// @Success 200
697+// @Failure 404 {object} proto.Error
698+// @Failure 500 {object} proto.Error
699+// @Router /workspaces/{id}/agent/update [post]
392func (c *controllerV1) handlePostWorkspaceAgentUpdate(w http.ResponseWriter, r *http.Request) {
700func (c *controllerV1) handlePostWorkspaceAgentUpdate(w http.ResponseWriter, r *http.Request) {
393 id := r.PathValue("id")
701 id := r.PathValue("id")
394 if err := c.backend.UpdateAgent(r.Context(), id); err != nil {
702 if err := c.backend.UpdateAgent(r.Context(), id); err != nil {
@@ -398,6 +706,17 @@ func (c *controllerV1) handlePostWorkspaceAgentUpdate(w http.ResponseWriter, r *
398 w.WriteHeader(http.StatusOK)
706 w.WriteHeader(http.StatusOK)
399}
707}
400
708
709+// handleGetWorkspaceAgentSession returns a specific agent session.
710+//
711+// @Summary Get agent session
712+// @Tags agent
713+// @Produce json
714+// @Param id path string true "Workspace ID"
715+// @Param sid path string true "Session ID"
716+// @Success 200 {object} proto.AgentSession
717+// @Failure 404 {object} proto.Error
718+// @Failure 500 {object} proto.Error
719+// @Router /workspaces/{id}/agent/sessions/{sid} [get]
401func (c *controllerV1) handleGetWorkspaceAgentSession(w http.ResponseWriter, r *http.Request) {
720func (c *controllerV1) handleGetWorkspaceAgentSession(w http.ResponseWriter, r *http.Request) {
402 id := r.PathValue("id")
721 id := r.PathValue("id")
403 sid := r.PathValue("sid")
722 sid := r.PathValue("sid")
@@ -409,6 +728,16 @@ func (c *controllerV1) handleGetWorkspaceAgentSession(w http.ResponseWriter, r *
409 jsonEncode(w, agentSession)
728 jsonEncode(w, agentSession)
410}
729}
411
730
731+// handlePostWorkspaceAgentSessionCancel cancels a running agent session.
732+//
733+// @Summary Cancel agent session
734+// @Tags agent
735+// @Param id path string true "Workspace ID"
736+// @Param sid path string true "Session ID"
737+// @Success 200
738+// @Failure 404 {object} proto.Error
739+// @Failure 500 {object} proto.Error
740+// @Router /workspaces/{id}/agent/sessions/{sid}/cancel [post]
412func (c *controllerV1) handlePostWorkspaceAgentSessionCancel(w http.ResponseWriter, r *http.Request) {
741func (c *controllerV1) handlePostWorkspaceAgentSessionCancel(w http.ResponseWriter, r *http.Request) {
413 id := r.PathValue("id")
742 id := r.PathValue("id")
414 sid := r.PathValue("sid")
743 sid := r.PathValue("sid")
@@ -419,6 +748,17 @@ func (c *controllerV1) handlePostWorkspaceAgentSessionCancel(w http.ResponseWrit
419 w.WriteHeader(http.StatusOK)
748 w.WriteHeader(http.StatusOK)
420}
749}
421
750
751+// handleGetWorkspaceAgentSessionPromptQueued returns whether a queued prompt exists.
752+//
753+// @Summary Get queued prompt status
754+// @Tags agent
755+// @Produce json
756+// @Param id path string true "Workspace ID"
757+// @Param sid path string true "Session ID"
758+// @Success 200 {object} object
759+// @Failure 404 {object} proto.Error
760+// @Failure 500 {object} proto.Error
761+// @Router /workspaces/{id}/agent/sessions/{sid}/prompts/queued [get]
422func (c *controllerV1) handleGetWorkspaceAgentSessionPromptQueued(w http.ResponseWriter, r *http.Request) {
762func (c *controllerV1) handleGetWorkspaceAgentSessionPromptQueued(w http.ResponseWriter, r *http.Request) {
423 id := r.PathValue("id")
763 id := r.PathValue("id")
424 sid := r.PathValue("sid")
764 sid := r.PathValue("sid")
@@ -430,6 +770,16 @@ func (c *controllerV1) handleGetWorkspaceAgentSessionPromptQueued(w http.Respons
430 jsonEncode(w, queued)
770 jsonEncode(w, queued)
431}
771}
432
772
773+// handlePostWorkspaceAgentSessionPromptClear clears the prompt queue for a session.
774+//
775+// @Summary Clear prompt queue
776+// @Tags agent
777+// @Param id path string true "Workspace ID"
778+// @Param sid path string true "Session ID"
779+// @Success 200
780+// @Failure 404 {object} proto.Error
781+// @Failure 500 {object} proto.Error
782+// @Router /workspaces/{id}/agent/sessions/{sid}/prompts/clear [post]
433func (c *controllerV1) handlePostWorkspaceAgentSessionPromptClear(w http.ResponseWriter, r *http.Request) {
783func (c *controllerV1) handlePostWorkspaceAgentSessionPromptClear(w http.ResponseWriter, r *http.Request) {
434 id := r.PathValue("id")
784 id := r.PathValue("id")
435 sid := r.PathValue("sid")
785 sid := r.PathValue("sid")
@@ -440,6 +790,16 @@ func (c *controllerV1) handlePostWorkspaceAgentSessionPromptClear(w http.Respons
440 w.WriteHeader(http.StatusOK)
790 w.WriteHeader(http.StatusOK)
441}
791}
442
792
793+// handlePostWorkspaceAgentSessionSummarize summarizes a session.
794+//
795+// @Summary Summarize session
796+// @Tags agent
797+// @Param id path string true "Workspace ID"
798+// @Param sid path string true "Session ID"
799+// @Success 200
800+// @Failure 404 {object} proto.Error
801+// @Failure 500 {object} proto.Error
802+// @Router /workspaces/{id}/agent/sessions/{sid}/summarize [post]
443func (c *controllerV1) handlePostWorkspaceAgentSessionSummarize(w http.ResponseWriter, r *http.Request) {
803func (c *controllerV1) handlePostWorkspaceAgentSessionSummarize(w http.ResponseWriter, r *http.Request) {
444 id := r.PathValue("id")
804 id := r.PathValue("id")
445 sid := r.PathValue("sid")
805 sid := r.PathValue("sid")
@@ -450,6 +810,17 @@ func (c *controllerV1) handlePostWorkspaceAgentSessionSummarize(w http.ResponseW
450 w.WriteHeader(http.StatusOK)
810 w.WriteHeader(http.StatusOK)
451}
811}
452
812
813+// handleGetWorkspaceAgentSessionPromptList returns the list of queued prompts.
814+//
815+// @Summary List queued prompts
816+// @Tags agent
817+// @Produce json
818+// @Param id path string true "Workspace ID"
819+// @Param sid path string true "Session ID"
820+// @Success 200 {array} string
821+// @Failure 404 {object} proto.Error
822+// @Failure 500 {object} proto.Error
823+// @Router /workspaces/{id}/agent/sessions/{sid}/prompts/list [get]
453func (c *controllerV1) handleGetWorkspaceAgentSessionPromptList(w http.ResponseWriter, r *http.Request) {
824func (c *controllerV1) handleGetWorkspaceAgentSessionPromptList(w http.ResponseWriter, r *http.Request) {
454 id := r.PathValue("id")
825 id := r.PathValue("id")
455 sid := r.PathValue("sid")
826 sid := r.PathValue("sid")
@@ -461,6 +832,17 @@ func (c *controllerV1) handleGetWorkspaceAgentSessionPromptList(w http.ResponseW
461 jsonEncode(w, prompts)
832 jsonEncode(w, prompts)
462}
833}
463
834
835+// handleGetWorkspaceAgentDefaultSmallModel returns the default small model for a provider.
836+//
837+// @Summary Get default small model
838+// @Tags agent
839+// @Produce json
840+// @Param id path string true "Workspace ID"
841+// @Param provider_id query string false "Provider ID"
842+// @Success 200 {object} object
843+// @Failure 404 {object} proto.Error
844+// @Failure 500 {object} proto.Error
845+// @Router /workspaces/{id}/agent/default-small-model [get]
464func (c *controllerV1) handleGetWorkspaceAgentDefaultSmallModel(w http.ResponseWriter, r *http.Request) {
846func (c *controllerV1) handleGetWorkspaceAgentDefaultSmallModel(w http.ResponseWriter, r *http.Request) {
465 id := r.PathValue("id")
847 id := r.PathValue("id")
466 providerID := r.URL.Query().Get("provider_id")
848 providerID := r.URL.Query().Get("provider_id")
@@ -472,6 +854,18 @@ func (c *controllerV1) handleGetWorkspaceAgentDefaultSmallModel(w http.ResponseW
472 jsonEncode(w, model)
854 jsonEncode(w, model)
473}
855}
474
856
857+// handlePostWorkspacePermissionsGrant grants a permission request.
858+//
859+// @Summary Grant permission
860+// @Tags permissions
861+// @Accept json
862+// @Param id path string true "Workspace ID"
863+// @Param request body proto.PermissionGrant true "Permission grant"
864+// @Success 200
865+// @Failure 400 {object} proto.Error
866+// @Failure 404 {object} proto.Error
867+// @Failure 500 {object} proto.Error
868+// @Router /workspaces/{id}/permissions/grant [post]
475func (c *controllerV1) handlePostWorkspacePermissionsGrant(w http.ResponseWriter, r *http.Request) {
869func (c *controllerV1) handlePostWorkspacePermissionsGrant(w http.ResponseWriter, r *http.Request) {
476 id := r.PathValue("id")
870 id := r.PathValue("id")
477
871
@@ -489,6 +883,18 @@ func (c *controllerV1) handlePostWorkspacePermissionsGrant(w http.ResponseWriter
489 w.WriteHeader(http.StatusOK)
883 w.WriteHeader(http.StatusOK)
490}
884}
491
885
886+// handlePostWorkspacePermissionsSkip sets whether to skip permission prompts.
887+//
888+// @Summary Set skip permissions
889+// @Tags permissions
890+// @Accept json
891+// @Param id path string true "Workspace ID"
892+// @Param request body proto.PermissionSkipRequest true "Permission skip request"
893+// @Success 200
894+// @Failure 400 {object} proto.Error
895+// @Failure 404 {object} proto.Error
896+// @Failure 500 {object} proto.Error
897+// @Router /workspaces/{id}/permissions/skip [post]
492func (c *controllerV1) handlePostWorkspacePermissionsSkip(w http.ResponseWriter, r *http.Request) {
898func (c *controllerV1) handlePostWorkspacePermissionsSkip(w http.ResponseWriter, r *http.Request) {
493 id := r.PathValue("id")
899 id := r.PathValue("id")
494
900
@@ -505,6 +911,16 @@ func (c *controllerV1) handlePostWorkspacePermissionsSkip(w http.ResponseWriter,
505 }
911 }
506}
912}
507
913
914+// handleGetWorkspacePermissionsSkip returns whether permission prompts are skipped.
915+//
916+// @Summary Get skip permissions status
917+// @Tags permissions
918+// @Produce json
919+// @Param id path string true "Workspace ID"
920+// @Success 200 {object} proto.PermissionSkipRequest
921+// @Failure 404 {object} proto.Error
922+// @Failure 500 {object} proto.Error
923+// @Router /workspaces/{id}/permissions/skip [get]
508func (c *controllerV1) handleGetWorkspacePermissionsSkip(w http.ResponseWriter, r *http.Request) {
924func (c *controllerV1) handleGetWorkspacePermissionsSkip(w http.ResponseWriter, r *http.Request) {
509 id := r.PathValue("id")
925 id := r.PathValue("id")
510 skip, err := c.backend.GetPermissionsSkip(id)
926 skip, err := c.backend.GetPermissionsSkip(id)
internal/server/server.go+3
@@ -11,8 +11,10 @@ import (
11 "runtime"
11 "runtime"
12 "strings"
12 "strings"
13
13
14+ _ "github.com/charmbracelet/crush/docs"
14 "github.com/charmbracelet/crush/internal/backend"
15 "github.com/charmbracelet/crush/internal/backend"
15 "github.com/charmbracelet/crush/internal/config"
16 "github.com/charmbracelet/crush/internal/config"
17+ httpswagger "github.com/swaggo/http-swagger/v2"
16)
18)
17
19
18// ErrServerClosed is returned when the server is closed.
20// ErrServerClosed is returned when the server is closed.
@@ -161,6 +163,7 @@ func NewServer(cfg *config.ConfigStore, network, address string) *Server {
161 mux.HandleFunc("GET /v1/workspaces/{id}/mcp/states", c.handleGetWorkspaceMCPStates)
163 mux.HandleFunc("GET /v1/workspaces/{id}/mcp/states", c.handleGetWorkspaceMCPStates)
162 mux.HandleFunc("POST /v1/workspaces/{id}/mcp/refresh-prompts", c.handlePostWorkspaceMCPRefreshPrompts)
164 mux.HandleFunc("POST /v1/workspaces/{id}/mcp/refresh-prompts", c.handlePostWorkspaceMCPRefreshPrompts)
163 mux.HandleFunc("POST /v1/workspaces/{id}/mcp/refresh-resources", c.handlePostWorkspaceMCPRefreshResources)
165 mux.HandleFunc("POST /v1/workspaces/{id}/mcp/refresh-resources", c.handlePostWorkspaceMCPRefreshResources)
166+ mux.Handle("/v1/docs/", httpswagger.WrapHandler)
164 s.h = &http.Server{
167 s.h = &http.Server{
165 Protocols: &p,
168 Protocols: &p,
166 Handler: s.loggingHandler(mux),
169 Handler: s.loggingHandler(mux),
main.go+10
@@ -1,3 +1,13 @@
1+// Package main is the entry point for the Crush CLI.
2+//
3+// @title Crush API
4+// @version 1.0
5+// @description Crush is a terminal-based AI coding assistant. This API is served over a Unix socket (or Windows named pipe) and provides programmatic access to workspaces, sessions, agents, LSP, MCP, and more.
6+// @contact.name Charm
7+// @contact.url https://charm.sh
8+// @license.name MIT
9+// @license.url https://github.com/charmbracelet/crush/blob/main/LICENSE
10+// @BasePath /v1
1package main
11package main
2
12
3import (
13import (