@@ -4,13 +4,13 @@ import (
4
4
"encoding/json"
5
5
"fmt"
6
6
"os"
7
- "sort"
8
7
"strings"
9
8
"text/tabwriter"
10
9
11
10
"github.com/spf13/cobra"
12
11
13
12
"github.com/stacklok/toolhive/pkg/registry"
13
+ transtypes "github.com/stacklok/toolhive/pkg/transport/types"
14
14
)
15
15
16
16
var registryCmd = & cobra.Command {
@@ -63,10 +63,8 @@ func registryListCmdFunc(_ *cobra.Command, _ []string) error {
63
63
return fmt .Errorf ("failed to list servers: %v" , err )
64
64
}
65
65
66
- // Sort servers by name
67
- sort .Slice (servers , func (i , j int ) bool {
68
- return servers [i ].Name < servers [j ].Name
69
- })
66
+ // Sort servers by name using the utility function
67
+ registry .SortServersByName (servers )
70
68
71
69
// Output based on format
72
70
switch registryFormat {
@@ -101,7 +99,7 @@ func registryInfoCmdFunc(_ *cobra.Command, args []string) error {
101
99
}
102
100
103
101
// printJSONServers prints servers in JSON format
104
- func printJSONServers (servers []* registry.ImageMetadata ) error {
102
+ func printJSONServers (servers []registry.ServerMetadata ) error {
105
103
// Marshal to JSON
106
104
jsonData , err := json .MarshalIndent (servers , "" , " " )
107
105
if err != nil {
@@ -114,8 +112,7 @@ func printJSONServers(servers []*registry.ImageMetadata) error {
114
112
}
115
113
116
114
// printJSONServer prints a single server in JSON format
117
- func printJSONServer (server * registry.ImageMetadata ) error {
118
- // Marshal to JSON
115
+ func printJSONServer (server registry.ServerMetadata ) error {
119
116
jsonData , err := json .MarshalIndent (server , "" , " " )
120
117
if err != nil {
121
118
return fmt .Errorf ("failed to marshal JSON: %v" , err )
@@ -127,29 +124,30 @@ func printJSONServer(server *registry.ImageMetadata) error {
127
124
}
128
125
129
126
// printTextServers prints servers in text format
130
- func printTextServers (servers []* registry.ImageMetadata ) {
127
+ func printTextServers (servers []registry.ServerMetadata ) {
131
128
// Create a tabwriter for pretty output
132
129
w := tabwriter .NewWriter (os .Stdout , 0 , 0 , 3 , ' ' , 0 )
133
- fmt .Fprintln (w , "NAME\t DESCRIPTION\t TIER\t STARS\t PULLS" )
130
+ fmt .Fprintln (w , "NAME\t TYPE \ t DESCRIPTION\t TIER\t STARS\t PULLS" )
134
131
135
132
// Print server information
136
133
for _ , server := range servers {
137
134
stars := 0
138
135
pulls := 0
139
- if server .Metadata != nil {
140
- stars = server . Metadata .Stars
141
- pulls = server . Metadata .Pulls
136
+ if metadata := server .GetMetadata (); metadata != nil {
137
+ stars = metadata .Stars
138
+ pulls = metadata .Pulls
142
139
}
143
140
144
- desc := server .Description
145
- if server .Status == "Deprecated" {
141
+ desc := server .GetDescription ()
142
+ if server .GetStatus () == "Deprecated" {
146
143
desc = "**DEPRECATED** " + desc
147
144
}
148
145
149
- fmt .Fprintf (w , "%s\t %s\t %s\t %d\t %d\n " ,
150
- server .Name ,
151
- truncateString (desc , 60 ),
152
- server .Tier ,
146
+ fmt .Fprintf (w , "%s\t %s\t %s\t %s\t %d\t %d\n " ,
147
+ server .GetName (),
148
+ getServerType (server ),
149
+ truncateString (desc , 50 ),
150
+ server .GetTier (),
153
151
stars ,
154
152
pulls ,
155
153
)
@@ -161,41 +159,139 @@ func printTextServers(servers []*registry.ImageMetadata) {
161
159
}
162
160
}
163
161
162
+ // getServerType returns the type of server (container or remote)
163
+ func getServerType (server registry.ServerMetadata ) string {
164
+ if server .IsRemote () {
165
+ return "remote"
166
+ }
167
+ return "container"
168
+ }
169
+
164
170
// printTextServerInfo prints detailed information about a server in text format
165
171
// nolint:gocyclo
166
- func printTextServerInfo (name string , server * registry.ImageMetadata ) {
167
- fmt .Printf ("Name: %s\n " , server .Name )
168
- fmt .Printf ("Image: %s\n " , server .Image )
169
- fmt .Printf ("Description: %s\n " , server .Description )
170
- fmt .Printf ("Tier: %s\n " , server .Tier )
171
- fmt .Printf ("Status: %s\n " , server .Status )
172
- fmt .Printf ("Transport: %s\n " , server .Transport )
173
- if (server .Transport == "sse" || server .Transport == "streamable-http" ) && server .TargetPort > 0 {
174
- fmt .Printf ("Target Port: %d\n " , server .TargetPort )
172
+ func printTextServerInfo (name string , server registry.ServerMetadata ) {
173
+ fmt .Printf ("Name: %s\n " , server .GetName ())
174
+ fmt .Printf ("Type: %s\n " , getServerType (server ))
175
+ fmt .Printf ("Description: %s\n " , server .GetDescription ())
176
+ fmt .Printf ("Tier: %s\n " , server .GetTier ())
177
+ fmt .Printf ("Status: %s\n " , server .GetStatus ())
178
+ fmt .Printf ("Transport: %s\n " , server .GetTransport ())
179
+
180
+ // Type-specific information
181
+ if ! server .IsRemote () {
182
+ // Container server
183
+ if img , ok := server .(* registry.ImageMetadata ); ok {
184
+ fmt .Printf ("Image: %s\n " , img .Image )
185
+ isHTTPTransport := img .Transport == transtypes .TransportTypeSSE .String () ||
186
+ img .Transport == transtypes .TransportTypeStreamableHTTP .String ()
187
+ if isHTTPTransport && img .TargetPort > 0 {
188
+ fmt .Printf ("Target Port: %d\n " , img .TargetPort )
189
+ }
190
+ fmt .Printf ("Has Provenance: %s\n " , map [bool ]string {true : "Yes" , false : "No" }[img .Provenance != nil ])
191
+
192
+ // Print permissions
193
+ if img .Permissions != nil {
194
+ fmt .Println ("\n Permissions:" )
195
+
196
+ // Print read permissions
197
+ if len (img .Permissions .Read ) > 0 {
198
+ fmt .Println (" Read:" )
199
+ for _ , path := range img .Permissions .Read {
200
+ fmt .Printf (" - %s\n " , path )
201
+ }
202
+ }
203
+
204
+ // Print write permissions
205
+ if len (img .Permissions .Write ) > 0 {
206
+ fmt .Println (" Write:" )
207
+ for _ , path := range img .Permissions .Write {
208
+ fmt .Printf (" - %s\n " , path )
209
+ }
210
+ }
211
+
212
+ // Print network permissions
213
+ if img .Permissions .Network != nil && img .Permissions .Network .Outbound != nil {
214
+ fmt .Println (" Network:" )
215
+ outbound := img .Permissions .Network .Outbound
216
+
217
+ if outbound .InsecureAllowAll {
218
+ fmt .Println (" Insecure Allow All: true" )
219
+ }
220
+
221
+ if len (outbound .AllowHost ) > 0 {
222
+ fmt .Printf (" Allow Host: %s\n " , strings .Join (outbound .AllowHost , ", " ))
223
+ }
224
+
225
+ if len (outbound .AllowPort ) > 0 {
226
+ ports := make ([]string , len (outbound .AllowPort ))
227
+ for i , port := range outbound .AllowPort {
228
+ ports [i ] = fmt .Sprintf ("%d" , port )
229
+ }
230
+ fmt .Printf (" Allow Port: %s\n " , strings .Join (ports , ", " ))
231
+ }
232
+ }
233
+ }
234
+ }
235
+ } else {
236
+ // Remote server
237
+ if remote , ok := server .(* registry.RemoteServerMetadata ); ok {
238
+ fmt .Printf ("URL: %s\n " , remote .URL )
239
+
240
+ // Print headers
241
+ if len (remote .Headers ) > 0 {
242
+ fmt .Println ("\n Headers:" )
243
+ for _ , header := range remote .Headers {
244
+ required := ""
245
+ if header .Required {
246
+ required = " (required)"
247
+ }
248
+ defaultValue := ""
249
+ if header .Default != "" {
250
+ defaultValue = fmt .Sprintf (" [default: %s]" , header .Default )
251
+ }
252
+ fmt .Printf (" - %s%s%s: %s\n " , header .Name , required , defaultValue , header .Description )
253
+ }
254
+ }
255
+
256
+ // Print OAuth config
257
+ if remote .OAuthConfig != nil {
258
+ fmt .Println ("\n OAuth Configuration:" )
259
+ if remote .OAuthConfig .Issuer != "" {
260
+ fmt .Printf (" Issuer: %s\n " , remote .OAuthConfig .Issuer )
261
+ }
262
+ if remote .OAuthConfig .ClientID != "" {
263
+ fmt .Printf (" Client ID: %s\n " , remote .OAuthConfig .ClientID )
264
+ }
265
+ if len (remote .OAuthConfig .Scopes ) > 0 {
266
+ fmt .Printf (" Scopes: %s\n " , strings .Join (remote .OAuthConfig .Scopes , ", " ))
267
+ }
268
+ }
269
+ }
175
270
}
176
- fmt .Printf ("Repository URL: %s\n " , server .RepositoryURL )
177
- fmt .Printf ("Has Provenance: %s\n " , map [bool ]string {true : "Yes" , false : "No" }[server .Provenance != nil ])
178
271
179
- if server .Metadata != nil {
180
- fmt .Printf ("Popularity: %d stars, %d pulls\n " , server .Metadata .Stars , server .Metadata .Pulls )
181
- fmt .Printf ("Last Updated: %s\n " , server .Metadata .LastUpdated )
272
+ fmt .Printf ("Repository URL: %s\n " , server .GetRepositoryURL ())
273
+
274
+ // Print metadata
275
+ if metadata := server .GetMetadata (); metadata != nil {
276
+ fmt .Printf ("Popularity: %d stars, %d pulls\n " , metadata .Stars , metadata .Pulls )
277
+ fmt .Printf ("Last Updated: %s\n " , metadata .LastUpdated )
182
278
} else {
183
279
fmt .Printf ("Popularity: 0 stars, 0 pulls\n " )
184
280
fmt .Printf ("Last Updated: N/A\n " )
185
281
}
186
282
187
283
// Print tools
188
- if len ( server .Tools ) > 0 {
189
- fmt .Println ("Tools :" )
190
- for _ , tool := range server . Tools {
284
+ if tools := server .GetTools (); len ( tools ) > 0 {
285
+ fmt .Println ("\n Tools :" )
286
+ for _ , tool := range tools {
191
287
fmt .Printf (" - %s\n " , tool )
192
288
}
193
289
}
194
290
195
291
// Print environment variables
196
- if len ( server .EnvVars ) > 0 {
292
+ if envVars := server .GetEnvVars (); len ( envVars ) > 0 {
197
293
fmt .Println ("\n Environment Variables:" )
198
- for _ , envVar := range server . EnvVars {
294
+ for _ , envVar := range envVars {
199
295
required := ""
200
296
if envVar .Required {
201
297
required = " (required)"
@@ -209,57 +305,18 @@ func printTextServerInfo(name string, server *registry.ImageMetadata) {
209
305
}
210
306
211
307
// Print tags
212
- if len (server .Tags ) > 0 {
213
- fmt .Println ("Tags:" )
214
- fmt .Printf (" %s\n " , strings .Join (server .Tags , ", " ))
215
- }
216
-
217
- // Print permissions
218
- if server .Permissions != nil {
219
- fmt .Println ("Permissions:" )
220
-
221
- // Print read permissions
222
- if len (server .Permissions .Read ) > 0 {
223
- fmt .Println (" Read:" )
224
- for _ , path := range server .Permissions .Read {
225
- fmt .Printf (" - %s\n " , path )
226
- }
227
- }
228
-
229
- // Print write permissions
230
- if len (server .Permissions .Write ) > 0 {
231
- fmt .Println (" Write:" )
232
- for _ , path := range server .Permissions .Write {
233
- fmt .Printf (" - %s\n " , path )
234
- }
235
- }
236
-
237
- // Print network permissions
238
- if server .Permissions .Network != nil && server .Permissions .Network .Outbound != nil {
239
- fmt .Println (" Network:" )
240
- outbound := server .Permissions .Network .Outbound
241
-
242
- if outbound .InsecureAllowAll {
243
- fmt .Println (" Insecure Allow All: true" )
244
- }
245
-
246
- if len (outbound .AllowHost ) > 0 {
247
- fmt .Printf (" Allow Host: %s\n " , strings .Join (outbound .AllowHost , ", " ))
248
- }
249
-
250
- if len (outbound .AllowPort ) > 0 {
251
- ports := make ([]string , len (outbound .AllowPort ))
252
- for i , port := range outbound .AllowPort {
253
- ports [i ] = fmt .Sprintf ("%d" , port )
254
- }
255
- fmt .Printf (" Allow Port: %s\n " , strings .Join (ports , ", " ))
256
- }
257
- }
308
+ if tags := server .GetTags (); len (tags ) > 0 {
309
+ fmt .Println ("\n Tags:" )
310
+ fmt .Printf (" %s\n " , strings .Join (tags , ", " ))
258
311
}
259
312
260
313
// Print example command
261
- fmt .Println ("Example Command:" )
262
- fmt .Printf (" thv run %s\n " , name )
314
+ fmt .Println ("\n Example Command:" )
315
+ if server .IsRemote () {
316
+ fmt .Printf (" thv proxy %s\n " , name )
317
+ } else {
318
+ fmt .Printf (" thv run %s\n " , name )
319
+ }
263
320
}
264
321
265
322
// truncateString truncates a string to the specified length and adds "..." if truncated
0 commit comments