@caretive/caret-cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmrc.tmp +2 -0
- package/README.md +72 -0
- package/cmd/cline/main.go +348 -0
- package/cmd/cline-host/main.go +71 -0
- package/e2e/default_update_test.go +154 -0
- package/e2e/helpers_test.go +378 -0
- package/e2e/main_test.go +47 -0
- package/e2e/mixed_stress_test.go +120 -0
- package/e2e/sqlite_helper.go +161 -0
- package/e2e/start_list_test.go +178 -0
- package/go.mod +64 -0
- package/go.sum +162 -0
- package/man/cline.1 +331 -0
- package/man/cline.1.md +332 -0
- package/package.json +54 -0
- package/pkg/cli/auth/auth_cline_provider.go +285 -0
- package/pkg/cli/auth/auth_menu.go +323 -0
- package/pkg/cli/auth/auth_subscription.go +130 -0
- package/pkg/cli/auth/byo_quick_setup.go +247 -0
- package/pkg/cli/auth/models_cline.go +141 -0
- package/pkg/cli/auth/models_list_fetch.go +156 -0
- package/pkg/cli/auth/models_list_static.go +69 -0
- package/pkg/cli/auth/providers_byo.go +184 -0
- package/pkg/cli/auth/providers_list.go +517 -0
- package/pkg/cli/auth/update_api_configurations.go +647 -0
- package/pkg/cli/auth/wizard_byo.go +764 -0
- package/pkg/cli/auth/wizard_byo_bedrock.go +193 -0
- package/pkg/cli/auth/wizard_byo_oca.go +366 -0
- package/pkg/cli/auth.go +43 -0
- package/pkg/cli/clerror/cline_error.go +187 -0
- package/pkg/cli/config/manager.go +208 -0
- package/pkg/cli/config/settings_renderer.go +198 -0
- package/pkg/cli/config.go +152 -0
- package/pkg/cli/display/ansi.go +27 -0
- package/pkg/cli/display/banner.go +211 -0
- package/pkg/cli/display/deduplicator.go +95 -0
- package/pkg/cli/display/markdown_renderer.go +139 -0
- package/pkg/cli/display/renderer.go +304 -0
- package/pkg/cli/display/segment_streamer.go +212 -0
- package/pkg/cli/display/streaming.go +134 -0
- package/pkg/cli/display/system_renderer.go +269 -0
- package/pkg/cli/display/tool_renderer.go +455 -0
- package/pkg/cli/display/tool_result_parser.go +371 -0
- package/pkg/cli/display/typewriter.go +210 -0
- package/pkg/cli/doctor.go +65 -0
- package/pkg/cli/global/cline-clients.go +501 -0
- package/pkg/cli/global/global.go +113 -0
- package/pkg/cli/global/registry.go +304 -0
- package/pkg/cli/handlers/ask_handlers.go +339 -0
- package/pkg/cli/handlers/handler.go +130 -0
- package/pkg/cli/handlers/say_handlers.go +521 -0
- package/pkg/cli/instances.go +506 -0
- package/pkg/cli/logs.go +382 -0
- package/pkg/cli/output/coordinator.go +167 -0
- package/pkg/cli/output/input_model.go +497 -0
- package/pkg/cli/sqlite/locks.go +366 -0
- package/pkg/cli/task/history_handler.go +72 -0
- package/pkg/cli/task/input_handler.go +577 -0
- package/pkg/cli/task/manager.go +1283 -0
- package/pkg/cli/task/settings_parser.go +754 -0
- package/pkg/cli/task/stream_coordinator.go +60 -0
- package/pkg/cli/task.go +675 -0
- package/pkg/cli/terminal/keyboard.go +695 -0
- package/pkg/cli/tui/HELP_WANTED.md +1 -0
- package/pkg/cli/types/history.go +17 -0
- package/pkg/cli/types/messages.go +329 -0
- package/pkg/cli/types/state.go +59 -0
- package/pkg/cli/updater/updater.go +409 -0
- package/pkg/cli/version.go +43 -0
- package/pkg/common/constants.go +6 -0
- package/pkg/common/schema.go +54 -0
- package/pkg/common/types.go +54 -0
- package/pkg/common/utils.go +185 -0
- package/pkg/generated/field_overrides.go +39 -0
- package/pkg/generated/providers.go +1584 -0
- package/pkg/hostbridge/diff.go +351 -0
- package/pkg/hostbridge/disabled/watch.go +39 -0
- package/pkg/hostbridge/disabled/window.go +63 -0
- package/pkg/hostbridge/disabled/workspace.go +66 -0
- package/pkg/hostbridge/env.go +166 -0
- package/pkg/hostbridge/grpc_server.go +113 -0
- package/pkg/hostbridge/simple.go +43 -0
- package/pkg/hostbridge/simple_workspace.go +85 -0
- package/pkg/hostbridge/window.go +129 -0
- package/scripts/publish-caret-cli.sh +39 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
package auth
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"fmt"
|
|
7
|
+
"strings"
|
|
8
|
+
"time"
|
|
9
|
+
|
|
10
|
+
"github.com/cline/cli/pkg/cli/global"
|
|
11
|
+
"github.com/cline/cli/pkg/cli/task"
|
|
12
|
+
"github.com/cline/grpc-go/cline"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// ProviderDisplay represents a configured provider for display purposes
|
|
16
|
+
type ProviderDisplay struct {
|
|
17
|
+
Mode string // "Plan" or "Act"
|
|
18
|
+
Provider cline.ApiProvider // Provider enum
|
|
19
|
+
ModelID string // Model identifier
|
|
20
|
+
HasAPIKey bool // Whether an API key is configured (never show actual key)
|
|
21
|
+
BaseURL string // Base URL for providers like Ollama (can be shown publicly)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ProviderListResult holds the parsed provider configuration from state
|
|
25
|
+
type ProviderListResult struct {
|
|
26
|
+
PlanProvider *ProviderDisplay
|
|
27
|
+
ActProvider *ProviderDisplay
|
|
28
|
+
apiConfig map[string]interface{} // Store the raw apiConfig for scanning all providers
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// GetProviderConfigurations retrieves and parses provider configurations from Cline Core state
|
|
32
|
+
func GetProviderConfigurations(ctx context.Context, manager *task.Manager) (*ProviderListResult, error) {
|
|
33
|
+
if global.Config.Verbose {
|
|
34
|
+
fmt.Println("[DEBUG] Retrieving provider configurations from Cline Core")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Get latest state from Cline Core
|
|
38
|
+
state, err := manager.GetClient().State.GetLatestState(ctx, &cline.EmptyRequest{})
|
|
39
|
+
if err != nil {
|
|
40
|
+
return nil, fmt.Errorf("failed to get state: %w", err)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
stateJSON := state.StateJson
|
|
44
|
+
|
|
45
|
+
if global.Config.Verbose {
|
|
46
|
+
fmt.Printf("[DEBUG] Retrieved state, parsing JSON (length: %d)\n", len(stateJSON))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Parse state_json as map[string]interface{}
|
|
50
|
+
var stateData map[string]interface{}
|
|
51
|
+
if err := json.Unmarshal([]byte(stateJSON), &stateData); err != nil {
|
|
52
|
+
return nil, fmt.Errorf("failed to parse state JSON: %w", err)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if global.Config.Verbose {
|
|
56
|
+
fmt.Printf("[DEBUG] Parsed state data with %d keys\n", len(stateData))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Extract apiConfiguration object from state
|
|
60
|
+
apiConfig, ok := stateData["apiConfiguration"].(map[string]interface{})
|
|
61
|
+
if !ok {
|
|
62
|
+
if global.Config.Verbose {
|
|
63
|
+
fmt.Println("[DEBUG] No apiConfiguration found in state")
|
|
64
|
+
}
|
|
65
|
+
return &ProviderListResult{
|
|
66
|
+
apiConfig: make(map[string]interface{}),
|
|
67
|
+
}, nil
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if global.Config.Verbose {
|
|
71
|
+
fmt.Printf("[DEBUG] Found apiConfiguration with %d keys\n", len(apiConfig))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Extract plan mode configuration
|
|
75
|
+
planProvider := extractProviderFromState(apiConfig, "plan")
|
|
76
|
+
if global.Config.Verbose && planProvider != nil {
|
|
77
|
+
fmt.Printf("[DEBUG] Plan mode: provider=%v, model=%s\n", planProvider.Provider, planProvider.ModelID)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Extract act mode configuration
|
|
81
|
+
actProvider := extractProviderFromState(apiConfig, "act")
|
|
82
|
+
if global.Config.Verbose && actProvider != nil {
|
|
83
|
+
fmt.Printf("[DEBUG] Act mode: provider=%v, model=%s\n", actProvider.Provider, actProvider.ModelID)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return &ProviderListResult{
|
|
87
|
+
PlanProvider: planProvider,
|
|
88
|
+
ActProvider: actProvider,
|
|
89
|
+
apiConfig: apiConfig,
|
|
90
|
+
}, nil
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// GetAllReadyProviders returns all providers that have both a model and API key configured
|
|
94
|
+
func (r *ProviderListResult) GetAllReadyProviders() []*ProviderDisplay {
|
|
95
|
+
if r.apiConfig == nil {
|
|
96
|
+
return []*ProviderDisplay{}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var readyProviders []*ProviderDisplay
|
|
100
|
+
seenProviders := make(map[cline.ApiProvider]bool)
|
|
101
|
+
|
|
102
|
+
// Check all possible providers
|
|
103
|
+
allProviders := []cline.ApiProvider{
|
|
104
|
+
cline.ApiProvider_CLINE,
|
|
105
|
+
cline.ApiProvider_ANTHROPIC,
|
|
106
|
+
cline.ApiProvider_OPENAI,
|
|
107
|
+
cline.ApiProvider_OPENAI_NATIVE,
|
|
108
|
+
cline.ApiProvider_OPENROUTER,
|
|
109
|
+
cline.ApiProvider_XAI,
|
|
110
|
+
cline.ApiProvider_BEDROCK,
|
|
111
|
+
cline.ApiProvider_GEMINI,
|
|
112
|
+
cline.ApiProvider_OLLAMA,
|
|
113
|
+
cline.ApiProvider_CEREBRAS,
|
|
114
|
+
cline.ApiProvider_NOUSRESEARCH,
|
|
115
|
+
cline.ApiProvider_OCA,
|
|
116
|
+
cline.ApiProvider_HICAP,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check each provider to see if it's ready to use
|
|
120
|
+
// We use "plan" mode to check, since both plan and act should have the same providers configured
|
|
121
|
+
for _, provider := range allProviders {
|
|
122
|
+
// Skip if we've already seen this provider
|
|
123
|
+
if seenProviders[provider] {
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check if this provider has a model configured
|
|
128
|
+
modelID := getProviderSpecificModelID(r.apiConfig, "plan", provider)
|
|
129
|
+
|
|
130
|
+
// Determine if credentials exist
|
|
131
|
+
hasCreds := checkAPIKeyExists(r.apiConfig, provider)
|
|
132
|
+
|
|
133
|
+
// Determine readiness: OCA uses auth state presence; others need creds and model
|
|
134
|
+
if provider == cline.ApiProvider_OCA {
|
|
135
|
+
state, _ := GetLatestOCAState(context.Background(), 2 *time.Second)
|
|
136
|
+
if state == nil || state.User == nil {
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
// Provider is not ready unless it has credentials AND a model configured
|
|
141
|
+
if !hasCreds || modelID == "" {
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Get base URL for Ollama
|
|
147
|
+
baseURL := ""
|
|
148
|
+
if provider == cline.ApiProvider_OLLAMA {
|
|
149
|
+
if url, ok := r.apiConfig["ollamaBaseUrl"].(string); ok {
|
|
150
|
+
baseURL = url
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// This provider is ready to use
|
|
155
|
+
readyProviders = append(readyProviders, &ProviderDisplay{
|
|
156
|
+
Mode: "Ready",
|
|
157
|
+
Provider: provider,
|
|
158
|
+
ModelID: modelID,
|
|
159
|
+
HasAPIKey: checkAPIKeyExists(r.apiConfig, provider),
|
|
160
|
+
BaseURL: baseURL,
|
|
161
|
+
})
|
|
162
|
+
seenProviders[provider] = true
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return readyProviders
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// extractProviderFromState extracts provider configuration for specific plan/act mode
|
|
169
|
+
func extractProviderFromState(stateData map[string]interface{}, mode string) *ProviderDisplay {
|
|
170
|
+
// Build key names based on mode
|
|
171
|
+
providerKey := mode + "ModeApiProvider"
|
|
172
|
+
|
|
173
|
+
// Extract provider string from state
|
|
174
|
+
providerStr, ok := stateData[providerKey].(string)
|
|
175
|
+
if !ok || providerStr == "" {
|
|
176
|
+
if global.Config.Verbose {
|
|
177
|
+
fmt.Printf("[DEBUG] No provider configured for %s mode\n", mode)
|
|
178
|
+
}
|
|
179
|
+
return nil
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Map provider string to enum
|
|
183
|
+
provider, ok := mapProviderStringToEnum(providerStr)
|
|
184
|
+
if !ok {
|
|
185
|
+
if global.Config.Verbose {
|
|
186
|
+
fmt.Printf("[DEBUG] Unknown provider type: %s\n", providerStr)
|
|
187
|
+
}
|
|
188
|
+
return nil
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Get provider-specific model ID
|
|
192
|
+
modelID := getProviderSpecificModelID(stateData, mode, provider)
|
|
193
|
+
|
|
194
|
+
// Check if API key exists
|
|
195
|
+
hasAPIKey := checkAPIKeyExists(stateData, provider)
|
|
196
|
+
|
|
197
|
+
// Get base URL for Ollama (can be shown publicly)
|
|
198
|
+
baseURL := ""
|
|
199
|
+
if provider == cline.ApiProvider_OLLAMA {
|
|
200
|
+
if url, ok := stateData["ollamaBaseUrl"].(string); ok {
|
|
201
|
+
baseURL = url
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return &ProviderDisplay{
|
|
206
|
+
Mode: capitalizeMode(mode),
|
|
207
|
+
Provider: provider,
|
|
208
|
+
ModelID: modelID,
|
|
209
|
+
HasAPIKey: hasAPIKey,
|
|
210
|
+
BaseURL: baseURL,
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// mapProviderStringToEnum converts provider string from state to ApiProvider enum
|
|
215
|
+
// Returns (provider, ok) where ok is false if the provider is unknown
|
|
216
|
+
func mapProviderStringToEnum(providerStr string) (cline.ApiProvider, bool) {
|
|
217
|
+
normalizedStr := strings.ToLower(providerStr)
|
|
218
|
+
|
|
219
|
+
// Map string values to enum values
|
|
220
|
+
switch normalizedStr {
|
|
221
|
+
case "anthropic":
|
|
222
|
+
return cline.ApiProvider_ANTHROPIC, true
|
|
223
|
+
case "openai", "openai-compatible": // internal name is 'openai', but this is actually the openai-compatible provider
|
|
224
|
+
return cline.ApiProvider_OPENAI, true
|
|
225
|
+
case "openai-native": // This is the native, official Open AI provider
|
|
226
|
+
return cline.ApiProvider_OPENAI_NATIVE, true
|
|
227
|
+
case "openrouter":
|
|
228
|
+
return cline.ApiProvider_OPENROUTER, true
|
|
229
|
+
case "xai":
|
|
230
|
+
return cline.ApiProvider_XAI, true
|
|
231
|
+
case "bedrock":
|
|
232
|
+
return cline.ApiProvider_BEDROCK, true
|
|
233
|
+
case "gemini":
|
|
234
|
+
return cline.ApiProvider_GEMINI, true
|
|
235
|
+
case "ollama":
|
|
236
|
+
return cline.ApiProvider_OLLAMA, true
|
|
237
|
+
case "cerebras":
|
|
238
|
+
return cline.ApiProvider_CEREBRAS, true
|
|
239
|
+
case "cline":
|
|
240
|
+
return cline.ApiProvider_CLINE, true
|
|
241
|
+
case "oca":
|
|
242
|
+
return cline.ApiProvider_OCA, true
|
|
243
|
+
case "hicap":
|
|
244
|
+
return cline.ApiProvider_HICAP, true
|
|
245
|
+
case "nousResearch":
|
|
246
|
+
return cline.ApiProvider_NOUSRESEARCH, true
|
|
247
|
+
default:
|
|
248
|
+
return cline.ApiProvider_ANTHROPIC, false // Return 0 value with false
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// GetProviderIDForEnum converts a provider enum to the provider ID string
|
|
253
|
+
// This is the inverse of mapProviderStringToEnum and is used for provider definitions
|
|
254
|
+
func GetProviderIDForEnum(provider cline.ApiProvider) string {
|
|
255
|
+
switch provider {
|
|
256
|
+
case cline.ApiProvider_ANTHROPIC:
|
|
257
|
+
return "anthropic"
|
|
258
|
+
case cline.ApiProvider_OPENAI:
|
|
259
|
+
return "openai-compatible"
|
|
260
|
+
case cline.ApiProvider_OPENAI_NATIVE:
|
|
261
|
+
return "openai-native"
|
|
262
|
+
case cline.ApiProvider_OPENROUTER:
|
|
263
|
+
return "openrouter"
|
|
264
|
+
case cline.ApiProvider_XAI:
|
|
265
|
+
return "xai"
|
|
266
|
+
case cline.ApiProvider_BEDROCK:
|
|
267
|
+
return "bedrock"
|
|
268
|
+
case cline.ApiProvider_GEMINI:
|
|
269
|
+
return "gemini"
|
|
270
|
+
case cline.ApiProvider_OLLAMA:
|
|
271
|
+
return "ollama"
|
|
272
|
+
case cline.ApiProvider_CEREBRAS:
|
|
273
|
+
return "cerebras"
|
|
274
|
+
case cline.ApiProvider_CLINE:
|
|
275
|
+
return "cline"
|
|
276
|
+
case cline.ApiProvider_OCA:
|
|
277
|
+
return "oca"
|
|
278
|
+
case cline.ApiProvider_HICAP:
|
|
279
|
+
return "hicap"
|
|
280
|
+
case cline.ApiProvider_NOUSRESEARCH:
|
|
281
|
+
return "nousResearch"
|
|
282
|
+
default:
|
|
283
|
+
return ""
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// getProviderSpecificModelID gets the provider-specific model ID field from state
|
|
288
|
+
func getProviderSpecificModelID(stateData map[string]interface{}, mode string, provider cline.ApiProvider) string {
|
|
289
|
+
modelKey, err := GetModelIDFieldName(provider, mode)
|
|
290
|
+
if err != nil {
|
|
291
|
+
if global.Config.Verbose {
|
|
292
|
+
fmt.Printf("[DEBUG] Error getting model ID field name: %v\n", err)
|
|
293
|
+
}
|
|
294
|
+
return ""
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if global.Config.Verbose {
|
|
298
|
+
fmt.Printf("[DEBUG] Looking for model ID in key: %s\n", modelKey)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Extract model ID from state
|
|
302
|
+
modelID, _ := stateData[modelKey].(string)
|
|
303
|
+
return modelID
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// checkAPIKeyExists checks if API key field exists in state (never retrieve actual key)
|
|
307
|
+
func checkAPIKeyExists(stateData map[string]interface{}, provider cline.ApiProvider) bool {
|
|
308
|
+
// Get field mapping from centralized function
|
|
309
|
+
fields, err := GetProviderFields(provider)
|
|
310
|
+
if err != nil {
|
|
311
|
+
return false
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
keyField := fields.APIKeyField
|
|
315
|
+
|
|
316
|
+
// Check if the key exists and is not empty
|
|
317
|
+
if value, ok := stateData[keyField]; ok {
|
|
318
|
+
if str, ok := value.(string); ok && str != "" {
|
|
319
|
+
return true
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return false
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// capitalizeMode capitalizes the mode string for display
|
|
327
|
+
func capitalizeMode(mode string) string {
|
|
328
|
+
if len(mode) == 0 {
|
|
329
|
+
return mode
|
|
330
|
+
}
|
|
331
|
+
return strings.ToUpper(mode[:1]) + mode[1:]
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// GetProviderDisplayName returns a user-friendly name for the provider
|
|
335
|
+
func GetProviderDisplayName(provider cline.ApiProvider) string {
|
|
336
|
+
switch provider {
|
|
337
|
+
case cline.ApiProvider_ANTHROPIC:
|
|
338
|
+
return "Anthropic"
|
|
339
|
+
case cline.ApiProvider_OPENAI:
|
|
340
|
+
return "OpenAI Compatible"
|
|
341
|
+
case cline.ApiProvider_OPENAI_NATIVE:
|
|
342
|
+
return "OpenAI (Official)"
|
|
343
|
+
case cline.ApiProvider_OPENROUTER:
|
|
344
|
+
return "OpenRouter"
|
|
345
|
+
case cline.ApiProvider_XAI:
|
|
346
|
+
return "X AI (Grok)"
|
|
347
|
+
case cline.ApiProvider_BEDROCK:
|
|
348
|
+
return "AWS Bedrock"
|
|
349
|
+
case cline.ApiProvider_GEMINI:
|
|
350
|
+
return "Google Gemini"
|
|
351
|
+
case cline.ApiProvider_OLLAMA:
|
|
352
|
+
return "Ollama"
|
|
353
|
+
case cline.ApiProvider_CEREBRAS:
|
|
354
|
+
return "Cerebras"
|
|
355
|
+
case cline.ApiProvider_CLINE:
|
|
356
|
+
return "Cline (Official)"
|
|
357
|
+
case cline.ApiProvider_OCA:
|
|
358
|
+
return "Oracle Code Assist"
|
|
359
|
+
case cline.ApiProvider_HICAP:
|
|
360
|
+
return "Hicap"
|
|
361
|
+
case cline.ApiProvider_NOUSRESEARCH:
|
|
362
|
+
return "NousResearch"
|
|
363
|
+
default:
|
|
364
|
+
return "Unknown"
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// FormatProviderList formats the complete provider list for console display
|
|
369
|
+
// This now shows ALL providers that have both a model and API key configured
|
|
370
|
+
func FormatProviderList(result *ProviderListResult) string {
|
|
371
|
+
var output strings.Builder
|
|
372
|
+
|
|
373
|
+
output.WriteString("\n=== Configured API Providers ===\n\n")
|
|
374
|
+
|
|
375
|
+
// Get the currently active provider
|
|
376
|
+
var activeProvider cline.ApiProvider
|
|
377
|
+
var activeProviderSet bool
|
|
378
|
+
if result.ActProvider != nil {
|
|
379
|
+
activeProvider = result.ActProvider.Provider
|
|
380
|
+
activeProviderSet = true
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Get all ready-to-use providers (those with both API key and model configured)
|
|
384
|
+
readyProviders := result.GetAllReadyProviders()
|
|
385
|
+
|
|
386
|
+
if len(readyProviders) == 0 {
|
|
387
|
+
output.WriteString(" No providers ready to use.\n")
|
|
388
|
+
output.WriteString(" A provider is ready when it has both a model and API key configured.\n")
|
|
389
|
+
output.WriteString(" Use 'Configure a new provider' to configure one.\n\n")
|
|
390
|
+
} else {
|
|
391
|
+
//output.WriteString(fmt.Sprintf(" %d provider(s) ready to use:\n\n", len(readyProviders)))
|
|
392
|
+
|
|
393
|
+
for _, display := range readyProviders {
|
|
394
|
+
// Check if this is the active provider
|
|
395
|
+
isActive := activeProviderSet && display.Provider == activeProvider
|
|
396
|
+
|
|
397
|
+
if isActive {
|
|
398
|
+
output.WriteString(fmt.Sprintf(" ✓ %s (ACTIVE)\n", GetProviderDisplayName(display.Provider)))
|
|
399
|
+
} else {
|
|
400
|
+
output.WriteString(fmt.Sprintf(" • %s\n", GetProviderDisplayName(display.Provider)))
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
output.WriteString(fmt.Sprintf(" Model: %s\n", display.ModelID))
|
|
404
|
+
|
|
405
|
+
// Show status based on provider type
|
|
406
|
+
if display.Provider == cline.ApiProvider_OLLAMA {
|
|
407
|
+
if display.BaseURL != "" {
|
|
408
|
+
output.WriteString(fmt.Sprintf(" Base URL: %s\n", display.BaseURL))
|
|
409
|
+
} else {
|
|
410
|
+
output.WriteString(" Base URL: (default)\n")
|
|
411
|
+
}
|
|
412
|
+
} else if display.Provider == cline.ApiProvider_CLINE || display.Provider == cline.ApiProvider_OCA {
|
|
413
|
+
output.WriteString(" Status: Authenticated\n")
|
|
414
|
+
} else {
|
|
415
|
+
output.WriteString(" API Key: Configured\n")
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
output.WriteString("\n")
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
output.WriteString("================================\n")
|
|
423
|
+
|
|
424
|
+
return output.String()
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// DetectAllConfiguredProviders scans the state to find all providers that have API keys configured.
|
|
428
|
+
// This allows switching between multiple providers even when only one is currently active.
|
|
429
|
+
func DetectAllConfiguredProviders(ctx context.Context, manager *task.Manager) ([]cline.ApiProvider, error) {
|
|
430
|
+
verboseLog("[DEBUG] Detecting all configured providers...")
|
|
431
|
+
|
|
432
|
+
// Get latest state from Cline Core
|
|
433
|
+
state, err := manager.GetClient().State.GetLatestState(ctx, &cline.EmptyRequest{})
|
|
434
|
+
if err != nil {
|
|
435
|
+
return nil, fmt.Errorf("failed to get state: %w", err)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
stateJSON := state.StateJson
|
|
439
|
+
|
|
440
|
+
// Parse state_json as map[string]interface{}
|
|
441
|
+
var stateData map[string]interface{}
|
|
442
|
+
if err := json.Unmarshal([]byte(stateJSON), &stateData); err != nil {
|
|
443
|
+
return nil, fmt.Errorf("failed to parse state JSON: %w", err)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Extract apiConfiguration object from state
|
|
447
|
+
apiConfig, ok := stateData["apiConfiguration"].(map[string]interface{})
|
|
448
|
+
if !ok {
|
|
449
|
+
verboseLog("[DEBUG] No apiConfiguration found in state")
|
|
450
|
+
verboseLog("[DEBUG] Available keys in stateData: %v", getMapKeys(stateData))
|
|
451
|
+
return []cline.ApiProvider{}, nil
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
verboseLog("[DEBUG] apiConfiguration keys: %v", getMapKeys(apiConfig))
|
|
455
|
+
|
|
456
|
+
var configuredProviders []cline.ApiProvider
|
|
457
|
+
|
|
458
|
+
// Check for Cline provider (uses authentication instead of API key)
|
|
459
|
+
if IsAuthenticated(ctx) {
|
|
460
|
+
configuredProviders = append(configuredProviders, cline.ApiProvider_CLINE)
|
|
461
|
+
verboseLog("[DEBUG] Cline provider is authenticated")
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Check OCA provider via global auth subscription (state presence)
|
|
465
|
+
if state, _ := GetLatestOCAState(context.Background(), 2*time.Second); state != nil && state.User != nil {
|
|
466
|
+
configuredProviders = append(configuredProviders, cline.ApiProvider_OCA)
|
|
467
|
+
verboseLog("[DEBUG] OCA provider has active auth state")
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Check each BYO provider for API key presence
|
|
471
|
+
providersToCheck := []struct {
|
|
472
|
+
provider cline.ApiProvider
|
|
473
|
+
keyField string
|
|
474
|
+
}{
|
|
475
|
+
{cline.ApiProvider_ANTHROPIC, "apiKey"},
|
|
476
|
+
{cline.ApiProvider_OPENAI, "openAiApiKey"},
|
|
477
|
+
{cline.ApiProvider_OPENAI_NATIVE, "openAiNativeApiKey"},
|
|
478
|
+
{cline.ApiProvider_OPENROUTER, "openRouterApiKey"},
|
|
479
|
+
{cline.ApiProvider_XAI, "xaiApiKey"},
|
|
480
|
+
{cline.ApiProvider_BEDROCK, "awsAccessKey"},
|
|
481
|
+
{cline.ApiProvider_GEMINI, "geminiApiKey"},
|
|
482
|
+
{cline.ApiProvider_OLLAMA, "ollamaBaseUrl"}, // Ollama uses baseUrl instead of API key
|
|
483
|
+
{cline.ApiProvider_CEREBRAS, "cerebrasApiKey"},
|
|
484
|
+
{cline.ApiProvider_HICAP, "hicapApiKey"},
|
|
485
|
+
{cline.ApiProvider_NOUSRESEARCH, "nousResearchApiKey"},
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
for _, providerCheck := range providersToCheck {
|
|
489
|
+
verboseLog("[DEBUG] Checking for %s key: %s", GetProviderDisplayName(providerCheck.provider), providerCheck.keyField)
|
|
490
|
+
if value, ok := apiConfig[providerCheck.keyField]; ok {
|
|
491
|
+
verboseLog("[DEBUG] Found key, value type: %T, is empty: %v", value, value == "")
|
|
492
|
+
if str, ok := value.(string); ok && str != "" {
|
|
493
|
+
configuredProviders = append(configuredProviders, providerCheck.provider)
|
|
494
|
+
verboseLog("[DEBUG] ✓ Provider %s is configured", GetProviderDisplayName(providerCheck.provider))
|
|
495
|
+
}
|
|
496
|
+
} else {
|
|
497
|
+
verboseLog("[DEBUG] Key %s not found", providerCheck.keyField)
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
verboseLog("[DEBUG] Total configured providers: %d", len(configuredProviders))
|
|
503
|
+
for _, p := range configuredProviders {
|
|
504
|
+
verboseLog("[DEBUG] - %s", GetProviderDisplayName(p))
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return configuredProviders, nil
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// getMapKeys returns the keys of a map for debugging
|
|
511
|
+
func getMapKeys(m map[string]interface{}) []string {
|
|
512
|
+
keys := make([]string, 0, len(m))
|
|
513
|
+
for k := range m {
|
|
514
|
+
keys = append(keys, k)
|
|
515
|
+
}
|
|
516
|
+
return keys
|
|
517
|
+
}
|