orquesta-cli 0.1.3 → 0.1.4
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/dist/core/slash-command-handler.d.ts.map +1 -1
- package/dist/core/slash-command-handler.js +82 -0
- package/dist/core/slash-command-handler.js.map +1 -1
- package/dist/ui/components/LLMSetupWizard.d.ts.map +1 -1
- package/dist/ui/components/LLMSetupWizard.js +144 -2
- package/dist/ui/components/LLMSetupWizard.js.map +1 -1
- package/dist/ui/components/OpenRouterModelBrowser.d.ts +13 -0
- package/dist/ui/components/OpenRouterModelBrowser.d.ts.map +1 -0
- package/dist/ui/components/OpenRouterModelBrowser.js +221 -0
- package/dist/ui/components/OpenRouterModelBrowser.js.map +1 -0
- package/package.json +1 -1
- package/src/core/slash-command-handler.ts +88 -0
- package/src/ui/components/LLMSetupWizard.tsx +199 -4
- package/src/ui/components/OpenRouterModelBrowser.tsx +365 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import TextInput from 'ink-text-input';
|
|
4
|
+
import Spinner from 'ink-spinner';
|
|
5
|
+
export const OpenRouterModelBrowser = ({ onSelect, onCancel, }) => {
|
|
6
|
+
const [mode, setMode] = useState('list');
|
|
7
|
+
const [models, setModels] = useState([]);
|
|
8
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
11
|
+
const [manualModelId, setManualModelId] = useState('');
|
|
12
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
13
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
14
|
+
const MAX_VISIBLE = 10;
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const fetchModels = async () => {
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetch('https://openrouter.ai/api/v1/models');
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error(`Failed to fetch models: ${response.status}`);
|
|
21
|
+
}
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
const sortedModels = (data.data || []).sort((a, b) => {
|
|
24
|
+
const aFree = parseFloat(a.pricing.prompt) === 0 && parseFloat(a.pricing.completion) === 0;
|
|
25
|
+
const bFree = parseFloat(b.pricing.prompt) === 0 && parseFloat(b.pricing.completion) === 0;
|
|
26
|
+
if (aFree && !bFree)
|
|
27
|
+
return -1;
|
|
28
|
+
if (!aFree && bFree)
|
|
29
|
+
return 1;
|
|
30
|
+
return a.name.localeCompare(b.name);
|
|
31
|
+
});
|
|
32
|
+
setModels(sortedModels);
|
|
33
|
+
setIsLoading(false);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch models');
|
|
37
|
+
setIsLoading(false);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
fetchModels();
|
|
41
|
+
}, []);
|
|
42
|
+
const filteredModels = useMemo(() => {
|
|
43
|
+
if (!searchQuery.trim())
|
|
44
|
+
return models;
|
|
45
|
+
const query = searchQuery.toLowerCase();
|
|
46
|
+
return models.filter(model => model.id.toLowerCase().includes(query) ||
|
|
47
|
+
model.name.toLowerCase().includes(query) ||
|
|
48
|
+
model.description?.toLowerCase().includes(query));
|
|
49
|
+
}, [models, searchQuery]);
|
|
50
|
+
const isFreeModel = (model) => {
|
|
51
|
+
return parseFloat(model.pricing.prompt) === 0 && parseFloat(model.pricing.completion) === 0;
|
|
52
|
+
};
|
|
53
|
+
const formatPricing = (model) => {
|
|
54
|
+
const promptPrice = parseFloat(model.pricing.prompt);
|
|
55
|
+
const completionPrice = parseFloat(model.pricing.completion);
|
|
56
|
+
if (promptPrice === 0 && completionPrice === 0) {
|
|
57
|
+
return 'Free';
|
|
58
|
+
}
|
|
59
|
+
const promptPer1M = (promptPrice * 1000000).toFixed(2);
|
|
60
|
+
return `$${promptPer1M}/1M`;
|
|
61
|
+
};
|
|
62
|
+
const handleSelect = () => {
|
|
63
|
+
if (mode === 'manual') {
|
|
64
|
+
if (manualModelId.trim()) {
|
|
65
|
+
onSelect({
|
|
66
|
+
id: manualModelId.trim(),
|
|
67
|
+
name: manualModelId.trim(),
|
|
68
|
+
contextLength: 128000,
|
|
69
|
+
isFree: false,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const model = filteredModels[selectedIndex];
|
|
75
|
+
if (model) {
|
|
76
|
+
onSelect({
|
|
77
|
+
id: model.id,
|
|
78
|
+
name: model.name,
|
|
79
|
+
contextLength: model.context_length,
|
|
80
|
+
isFree: isFreeModel(model),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
useInput((_input, key) => {
|
|
86
|
+
if (key.escape) {
|
|
87
|
+
if (mode === 'search' || mode === 'manual') {
|
|
88
|
+
setMode('list');
|
|
89
|
+
setSearchQuery('');
|
|
90
|
+
setManualModelId('');
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
onCancel();
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (mode === 'list') {
|
|
98
|
+
if (key.upArrow) {
|
|
99
|
+
setSelectedIndex((prev) => {
|
|
100
|
+
const newIndex = prev > 0 ? prev - 1 : filteredModels.length - 1;
|
|
101
|
+
if (newIndex < scrollOffset) {
|
|
102
|
+
setScrollOffset(newIndex);
|
|
103
|
+
}
|
|
104
|
+
return newIndex;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else if (key.downArrow) {
|
|
108
|
+
setSelectedIndex((prev) => {
|
|
109
|
+
const newIndex = prev < filteredModels.length - 1 ? prev + 1 : 0;
|
|
110
|
+
if (newIndex >= scrollOffset + MAX_VISIBLE) {
|
|
111
|
+
setScrollOffset(newIndex - MAX_VISIBLE + 1);
|
|
112
|
+
}
|
|
113
|
+
else if (newIndex < scrollOffset) {
|
|
114
|
+
setScrollOffset(0);
|
|
115
|
+
}
|
|
116
|
+
return newIndex;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else if (key.return) {
|
|
120
|
+
handleSelect();
|
|
121
|
+
}
|
|
122
|
+
else if (_input === '/' || _input === 's') {
|
|
123
|
+
setMode('search');
|
|
124
|
+
setSelectedIndex(0);
|
|
125
|
+
setScrollOffset(0);
|
|
126
|
+
}
|
|
127
|
+
else if (_input === 'm') {
|
|
128
|
+
setMode('manual');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (mode === 'search') {
|
|
132
|
+
if (key.return) {
|
|
133
|
+
setMode('list');
|
|
134
|
+
setSelectedIndex(0);
|
|
135
|
+
setScrollOffset(0);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else if (mode === 'manual') {
|
|
139
|
+
if (key.return) {
|
|
140
|
+
handleSelect();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (selectedIndex >= filteredModels.length) {
|
|
146
|
+
setSelectedIndex(Math.max(0, filteredModels.length - 1));
|
|
147
|
+
}
|
|
148
|
+
}, [filteredModels.length, selectedIndex]);
|
|
149
|
+
if (isLoading) {
|
|
150
|
+
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
151
|
+
React.createElement(Text, { color: "cyan" },
|
|
152
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
153
|
+
" Loading OpenRouter models...")));
|
|
154
|
+
}
|
|
155
|
+
if (error) {
|
|
156
|
+
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
|
|
157
|
+
React.createElement(Text, { color: "red" },
|
|
158
|
+
"Error: ",
|
|
159
|
+
error),
|
|
160
|
+
React.createElement(Text, { color: "gray" }, "Press 'm' to enter model ID manually, or Esc to go back")));
|
|
161
|
+
}
|
|
162
|
+
const visibleModels = filteredModels.slice(scrollOffset, scrollOffset + MAX_VISIBLE);
|
|
163
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
164
|
+
React.createElement(Box, { borderStyle: "round", borderColor: "magenta", paddingX: 2, marginBottom: 1 },
|
|
165
|
+
React.createElement(Text, { color: "magenta", bold: true },
|
|
166
|
+
"OpenRouter Models (",
|
|
167
|
+
filteredModels.length,
|
|
168
|
+
" available)")),
|
|
169
|
+
mode === 'search' && (React.createElement(Box, { paddingX: 1, marginBottom: 1 },
|
|
170
|
+
React.createElement(Text, { color: "yellow" }, "Search: "),
|
|
171
|
+
React.createElement(TextInput, { value: searchQuery, onChange: setSearchQuery, placeholder: "Type to filter models..." }))),
|
|
172
|
+
mode === 'manual' && (React.createElement(Box, { flexDirection: "column", paddingX: 1 },
|
|
173
|
+
React.createElement(Text, { color: "yellow", bold: true }, "Enter Model ID:"),
|
|
174
|
+
React.createElement(Box, { marginY: 1 },
|
|
175
|
+
React.createElement(Text, { color: "gray" }, "ID: "),
|
|
176
|
+
React.createElement(TextInput, { value: manualModelId, onChange: setManualModelId, placeholder: "provider/model-name" })),
|
|
177
|
+
React.createElement(Text, { color: "gray", dimColor: true }, "Example: anthropic/claude-3.5-sonnet, openai/gpt-4-turbo"),
|
|
178
|
+
React.createElement(Box, { marginTop: 1 },
|
|
179
|
+
React.createElement(Text, { dimColor: true }, "Enter: confirm | Esc: back to list")))),
|
|
180
|
+
mode !== 'manual' && (React.createElement(React.Fragment, null,
|
|
181
|
+
searchQuery && mode === 'list' && (React.createElement(Box, { paddingX: 1, marginBottom: 1 },
|
|
182
|
+
React.createElement(Text, { color: "gray" },
|
|
183
|
+
"Filtered by: \"",
|
|
184
|
+
searchQuery,
|
|
185
|
+
"\" "),
|
|
186
|
+
React.createElement(Text, { color: "cyan" }, "(press / to search again)"))),
|
|
187
|
+
scrollOffset > 0 && (React.createElement(Box, { paddingX: 1 },
|
|
188
|
+
React.createElement(Text, { color: "gray" },
|
|
189
|
+
"\u2191 ",
|
|
190
|
+
scrollOffset,
|
|
191
|
+
" more above"))),
|
|
192
|
+
React.createElement(Box, { flexDirection: "column", paddingX: 1 }, visibleModels.map((model, index) => {
|
|
193
|
+
const actualIndex = scrollOffset + index;
|
|
194
|
+
const isSelected = actualIndex === selectedIndex;
|
|
195
|
+
const isFree = isFreeModel(model);
|
|
196
|
+
return (React.createElement(Box, { key: model.id, flexDirection: "row" },
|
|
197
|
+
React.createElement(Text, { color: isSelected ? 'cyan' : undefined }, isSelected ? '> ' : ' '),
|
|
198
|
+
React.createElement(Text, { color: isFree ? 'green' : 'yellow', bold: true }, isFree ? '[FREE] ' : '[PAID] '),
|
|
199
|
+
React.createElement(Text, { color: isSelected ? 'cyan' : 'white', bold: isSelected }, model.id),
|
|
200
|
+
React.createElement(Text, { color: "gray" },
|
|
201
|
+
' ',
|
|
202
|
+
"(",
|
|
203
|
+
Math.round(model.context_length / 1000),
|
|
204
|
+
"k ctx)"),
|
|
205
|
+
!isFree && (React.createElement(Text, { color: "gray", dimColor: true },
|
|
206
|
+
' ',
|
|
207
|
+
formatPricing(model)))));
|
|
208
|
+
})),
|
|
209
|
+
scrollOffset + MAX_VISIBLE < filteredModels.length && (React.createElement(Box, { paddingX: 1 },
|
|
210
|
+
React.createElement(Text, { color: "gray" },
|
|
211
|
+
"\u2193 ",
|
|
212
|
+
filteredModels.length - scrollOffset - MAX_VISIBLE,
|
|
213
|
+
" more below"))),
|
|
214
|
+
filteredModels.length === 0 && (React.createElement(Box, { paddingX: 1 },
|
|
215
|
+
React.createElement(Text, { color: "yellow" }, "No models match your search."))))),
|
|
216
|
+
mode === 'list' && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
217
|
+
React.createElement(Text, { dimColor: true }, "\u2191\u2193: navigate | Enter: select | /: search | m: manual entry | Esc: cancel"))),
|
|
218
|
+
mode === 'search' && (React.createElement(Box, { marginTop: 1 },
|
|
219
|
+
React.createElement(Text, { dimColor: true }, "Type to filter | Enter: apply | Esc: clear search")))));
|
|
220
|
+
};
|
|
221
|
+
//# sourceMappingURL=OpenRouterModelBrowser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenRouterModelBrowser.js","sourceRoot":"","sources":["../../../src/ui/components/OpenRouterModelBrowser.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -9,6 +9,8 @@ import { Message, TodoItem } from '../types/index.js';
|
|
|
9
9
|
import { sessionManager } from './session/session-manager.js';
|
|
10
10
|
import { usageTracker } from './usage-tracker.js';
|
|
11
11
|
import { logger } from '../utils/logger.js';
|
|
12
|
+
import { fullSync } from '../orquesta/config-sync.js';
|
|
13
|
+
import { configManager } from './config/config-manager.js';
|
|
12
14
|
// DISABLED: docs feature removed
|
|
13
15
|
// import {
|
|
14
16
|
// getDocsInfo,
|
|
@@ -225,6 +227,91 @@ export async function executeSlashCommand(
|
|
|
225
227
|
};
|
|
226
228
|
}
|
|
227
229
|
|
|
230
|
+
// Sync command - sync LLM configs from Orquesta dashboard
|
|
231
|
+
if (trimmedCommand === '/sync') {
|
|
232
|
+
logger.flow('Sync command received');
|
|
233
|
+
|
|
234
|
+
// Check if connected to Orquesta
|
|
235
|
+
const orquestaConfig = configManager.getOrquestaConfig();
|
|
236
|
+
if (!orquestaConfig?.token) {
|
|
237
|
+
const notConnectedMessage = `❌ Not connected to Orquesta.\n\nTo sync configurations, first connect using an Orquesta CLI token:\n1. Go to https://orquesta.live/dashboard/orquesta-cli\n2. Generate a CLI token\n3. Run orquesta-cli again and enter your token`;
|
|
238
|
+
const updatedMessages = [
|
|
239
|
+
...context.messages,
|
|
240
|
+
{ role: 'assistant' as const, content: notConnectedMessage },
|
|
241
|
+
];
|
|
242
|
+
context.setMessages(updatedMessages);
|
|
243
|
+
return {
|
|
244
|
+
handled: true,
|
|
245
|
+
shouldContinue: false,
|
|
246
|
+
updatedContext: {
|
|
247
|
+
messages: updatedMessages,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Perform full sync
|
|
253
|
+
const syncingMessage = `🔄 Syncing with Orquesta (${orquestaConfig.projectName || 'project'})...`;
|
|
254
|
+
const inProgressMessages = [
|
|
255
|
+
...context.messages,
|
|
256
|
+
{ role: 'assistant' as const, content: syncingMessage },
|
|
257
|
+
];
|
|
258
|
+
context.setMessages(inProgressMessages);
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const { pullResult, pushResult } = await fullSync();
|
|
262
|
+
|
|
263
|
+
let resultMessage = '';
|
|
264
|
+
|
|
265
|
+
if (pullResult.success) {
|
|
266
|
+
if (pullResult.added > 0 || pullResult.updated > 0) {
|
|
267
|
+
resultMessage += `✅ Pulled from dashboard: ${pullResult.added} added, ${pullResult.updated} updated\n`;
|
|
268
|
+
} else {
|
|
269
|
+
resultMessage += `✅ Pulled from dashboard: No changes\n`;
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
resultMessage += `⚠️ Pull failed: ${pullResult.error}\n`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (pushResult.success) {
|
|
276
|
+
const pushed = pushResult.results?.length || 0;
|
|
277
|
+
if (pushed > 0) {
|
|
278
|
+
resultMessage += `✅ Pushed to dashboard: ${pushed} endpoint(s)`;
|
|
279
|
+
} else {
|
|
280
|
+
resultMessage += `✅ Pushed to dashboard: No local changes`;
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
resultMessage += `⚠️ Push failed: ${pushResult.error}`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const updatedMessages = [
|
|
287
|
+
...context.messages,
|
|
288
|
+
{ role: 'assistant' as const, content: resultMessage },
|
|
289
|
+
];
|
|
290
|
+
context.setMessages(updatedMessages);
|
|
291
|
+
return {
|
|
292
|
+
handled: true,
|
|
293
|
+
shouldContinue: false,
|
|
294
|
+
updatedContext: {
|
|
295
|
+
messages: updatedMessages,
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
} catch (error) {
|
|
299
|
+
const errorMessage = `❌ Sync failed: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
300
|
+
const updatedMessages = [
|
|
301
|
+
...context.messages,
|
|
302
|
+
{ role: 'assistant' as const, content: errorMessage },
|
|
303
|
+
];
|
|
304
|
+
context.setMessages(updatedMessages);
|
|
305
|
+
return {
|
|
306
|
+
handled: true,
|
|
307
|
+
shouldContinue: false,
|
|
308
|
+
updatedContext: {
|
|
309
|
+
messages: updatedMessages,
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
228
315
|
// DISABLED: /docs command removed
|
|
229
316
|
// The docs feature has been disabled
|
|
230
317
|
|
|
@@ -240,6 +327,7 @@ Available commands:
|
|
|
240
327
|
/tool - Enable/disable optional tools (Browser, Background)
|
|
241
328
|
/load - Load a saved session
|
|
242
329
|
/usage - Show token usage statistics
|
|
330
|
+
/sync - Sync LLM configs with Orquesta dashboard
|
|
243
331
|
|
|
244
332
|
Keyboard shortcuts:
|
|
245
333
|
Ctrl+C - Exit
|
|
@@ -14,6 +14,7 @@ import { LLMClient } from '../../core/llm/llm-client.js';
|
|
|
14
14
|
import { EndpointConfig } from '../../types/index.js';
|
|
15
15
|
import { fetchOrquestaProjects, syncOrquestaConfigs } from '../../orquesta/config-sync.js';
|
|
16
16
|
import { Logo } from './Logo.js';
|
|
17
|
+
import { OpenRouterModelBrowser } from './OpenRouterModelBrowser.js';
|
|
17
18
|
|
|
18
19
|
interface LLMSetupWizardProps {
|
|
19
20
|
onComplete: () => void;
|
|
@@ -29,9 +30,69 @@ interface FormData {
|
|
|
29
30
|
maxContextLength: string;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
type WizardStep = 'welcome' | 'token-input' | 'project-select' | 'manual-setup';
|
|
33
|
+
type WizardStep = 'welcome' | 'token-input' | 'project-select' | 'provider-select' | 'openrouter-browse' | 'manual-setup';
|
|
33
34
|
type FormField = 'name' | 'baseUrl' | 'apiKey' | 'modelId' | 'modelName' | 'maxContextLength' | 'buttons';
|
|
34
35
|
|
|
36
|
+
interface ProviderPreset {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
icon: string;
|
|
40
|
+
baseUrl: string;
|
|
41
|
+
requiresApiKey: boolean;
|
|
42
|
+
description: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const PROVIDER_PRESETS: ProviderPreset[] = [
|
|
46
|
+
{
|
|
47
|
+
id: 'openrouter',
|
|
48
|
+
name: 'OpenRouter',
|
|
49
|
+
icon: '🌐',
|
|
50
|
+
baseUrl: 'https://openrouter.ai/api/v1',
|
|
51
|
+
requiresApiKey: true,
|
|
52
|
+
description: 'Access 200+ models (GPT-4, Claude, Llama, etc.)',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'ollama',
|
|
56
|
+
name: 'Ollama (Local)',
|
|
57
|
+
icon: '🦙',
|
|
58
|
+
baseUrl: 'http://localhost:11434/v1',
|
|
59
|
+
requiresApiKey: false,
|
|
60
|
+
description: 'Run models locally with Ollama',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'openai',
|
|
64
|
+
name: 'OpenAI',
|
|
65
|
+
icon: '🤖',
|
|
66
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
67
|
+
requiresApiKey: true,
|
|
68
|
+
description: 'GPT-4, GPT-4 Turbo, GPT-3.5',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'anthropic',
|
|
72
|
+
name: 'Anthropic',
|
|
73
|
+
icon: '🧠',
|
|
74
|
+
baseUrl: 'https://api.anthropic.com/v1',
|
|
75
|
+
requiresApiKey: true,
|
|
76
|
+
description: 'Claude 3.5 Sonnet, Claude 3 Opus',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'deepseek',
|
|
80
|
+
name: 'DeepSeek',
|
|
81
|
+
icon: '🔍',
|
|
82
|
+
baseUrl: 'https://api.deepseek.com/v1',
|
|
83
|
+
requiresApiKey: true,
|
|
84
|
+
description: 'DeepSeek Coder, DeepSeek Chat',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'custom',
|
|
88
|
+
name: 'Custom Endpoint',
|
|
89
|
+
icon: '⚙️',
|
|
90
|
+
baseUrl: '',
|
|
91
|
+
requiresApiKey: false,
|
|
92
|
+
description: 'Configure any OpenAI-compatible API',
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
|
|
35
96
|
interface OrquestaProject {
|
|
36
97
|
id: string;
|
|
37
98
|
name: string;
|
|
@@ -56,6 +117,10 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
56
117
|
const [projectIndex, setProjectIndex] = useState(0);
|
|
57
118
|
const [isSyncing, setIsSyncing] = useState(false);
|
|
58
119
|
|
|
120
|
+
// Provider selection state
|
|
121
|
+
const [providerIndex, setProviderIndex] = useState(0);
|
|
122
|
+
const [selectedProvider, setSelectedProvider] = useState<ProviderPreset | null>(null);
|
|
123
|
+
|
|
59
124
|
// Manual setup form state
|
|
60
125
|
const [formData, setFormData] = useState<FormData>({
|
|
61
126
|
name: '',
|
|
@@ -87,14 +152,61 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
87
152
|
// "I have an Orquesta token"
|
|
88
153
|
setStep('token-input');
|
|
89
154
|
} else if (welcomeOptionIndex === 1) {
|
|
90
|
-
// "Set up manually"
|
|
91
|
-
setStep('
|
|
155
|
+
// "Set up manually" - go to provider selection
|
|
156
|
+
setStep('provider-select');
|
|
92
157
|
} else {
|
|
93
158
|
// "Skip for now"
|
|
94
159
|
onSkip();
|
|
95
160
|
}
|
|
96
161
|
}, [welcomeOptionIndex, onSkip]);
|
|
97
162
|
|
|
163
|
+
// Handle provider selection
|
|
164
|
+
const handleProviderSelect = useCallback((provider: ProviderPreset) => {
|
|
165
|
+
setSelectedProvider(provider);
|
|
166
|
+
|
|
167
|
+
if (provider.id === 'openrouter') {
|
|
168
|
+
// Go to OpenRouter model browser
|
|
169
|
+
setStep('openrouter-browse');
|
|
170
|
+
} else if (provider.id === 'custom') {
|
|
171
|
+
// Go to full manual setup
|
|
172
|
+
setFormData({
|
|
173
|
+
name: '',
|
|
174
|
+
baseUrl: '',
|
|
175
|
+
apiKey: '',
|
|
176
|
+
modelId: '',
|
|
177
|
+
modelName: '',
|
|
178
|
+
maxContextLength: '128000',
|
|
179
|
+
});
|
|
180
|
+
setStep('manual-setup');
|
|
181
|
+
} else {
|
|
182
|
+
// Pre-fill form with provider info
|
|
183
|
+
setFormData({
|
|
184
|
+
name: provider.name,
|
|
185
|
+
baseUrl: provider.baseUrl,
|
|
186
|
+
apiKey: '',
|
|
187
|
+
modelId: '',
|
|
188
|
+
modelName: '',
|
|
189
|
+
maxContextLength: '128000',
|
|
190
|
+
});
|
|
191
|
+
setFormField(provider.requiresApiKey ? 'apiKey' : 'modelId');
|
|
192
|
+
setStep('manual-setup');
|
|
193
|
+
}
|
|
194
|
+
}, []);
|
|
195
|
+
|
|
196
|
+
// Handle OpenRouter model selection
|
|
197
|
+
const handleOpenRouterModelSelect = useCallback((model: { id: string; name: string; contextLength: number; isFree: boolean }) => {
|
|
198
|
+
setFormData({
|
|
199
|
+
name: 'OpenRouter',
|
|
200
|
+
baseUrl: 'https://openrouter.ai/api/v1',
|
|
201
|
+
apiKey: '',
|
|
202
|
+
modelId: model.id,
|
|
203
|
+
modelName: model.name,
|
|
204
|
+
maxContextLength: model.contextLength.toString(),
|
|
205
|
+
});
|
|
206
|
+
setFormField('apiKey');
|
|
207
|
+
setStep('manual-setup');
|
|
208
|
+
}, []);
|
|
209
|
+
|
|
98
210
|
// Handle token validation
|
|
99
211
|
const handleTokenSubmit = useCallback(async () => {
|
|
100
212
|
if (!orquestaToken.trim()) {
|
|
@@ -339,6 +451,26 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
339
451
|
return;
|
|
340
452
|
}
|
|
341
453
|
|
|
454
|
+
// Provider selection screen
|
|
455
|
+
if (step === 'provider-select') {
|
|
456
|
+
if (key.upArrow) {
|
|
457
|
+
setProviderIndex((prev) => (prev > 0 ? prev - 1 : PROVIDER_PRESETS.length - 1));
|
|
458
|
+
} else if (key.downArrow) {
|
|
459
|
+
setProviderIndex((prev) => (prev < PROVIDER_PRESETS.length - 1 ? prev + 1 : 0));
|
|
460
|
+
} else if (key.return) {
|
|
461
|
+
handleProviderSelect(PROVIDER_PRESETS[providerIndex]!);
|
|
462
|
+
} else if (key.escape) {
|
|
463
|
+
setStep('welcome');
|
|
464
|
+
}
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// OpenRouter model browser - handled by component itself
|
|
469
|
+
if (step === 'openrouter-browse') {
|
|
470
|
+
// Component handles its own input
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
342
474
|
// Manual setup form
|
|
343
475
|
if (step === 'manual-setup') {
|
|
344
476
|
if (key.tab) {
|
|
@@ -348,7 +480,12 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
348
480
|
} else if (key.return && formField === 'buttons') {
|
|
349
481
|
handleFormSubmit();
|
|
350
482
|
} else if (key.escape) {
|
|
351
|
-
|
|
483
|
+
// Go back to provider select or welcome
|
|
484
|
+
if (selectedProvider) {
|
|
485
|
+
setStep('provider-select');
|
|
486
|
+
} else {
|
|
487
|
+
setStep('welcome');
|
|
488
|
+
}
|
|
352
489
|
}
|
|
353
490
|
}
|
|
354
491
|
});
|
|
@@ -521,6 +658,64 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
521
658
|
);
|
|
522
659
|
}
|
|
523
660
|
|
|
661
|
+
// Render Provider Selection Screen
|
|
662
|
+
if (step === 'provider-select') {
|
|
663
|
+
return (
|
|
664
|
+
<Box flexDirection="column">
|
|
665
|
+
<Text>{' '}</Text>
|
|
666
|
+
<Text>{' '}</Text>
|
|
667
|
+
|
|
668
|
+
{/* Header */}
|
|
669
|
+
<Box borderStyle="double" borderColor="cyan" paddingX={2} marginBottom={1}>
|
|
670
|
+
<Text color="cyan" bold>
|
|
671
|
+
Select LLM Provider
|
|
672
|
+
</Text>
|
|
673
|
+
</Box>
|
|
674
|
+
|
|
675
|
+
{/* Instructions */}
|
|
676
|
+
<Box paddingX={1} marginBottom={1}>
|
|
677
|
+
<Text color="gray">Choose a provider to get started quickly:</Text>
|
|
678
|
+
</Box>
|
|
679
|
+
|
|
680
|
+
{/* Provider List */}
|
|
681
|
+
<Box paddingX={1} flexDirection="column">
|
|
682
|
+
{PROVIDER_PRESETS.map((provider, index) => (
|
|
683
|
+
<Box key={provider.id} flexDirection="column" marginBottom={index < PROVIDER_PRESETS.length - 1 ? 1 : 0}>
|
|
684
|
+
<Text
|
|
685
|
+
color={providerIndex === index ? 'cyan' : undefined}
|
|
686
|
+
bold={providerIndex === index}
|
|
687
|
+
>
|
|
688
|
+
{providerIndex === index ? '> ' : ' '}
|
|
689
|
+
{provider.icon} {provider.name}
|
|
690
|
+
</Text>
|
|
691
|
+
<Text color="gray" dimColor>
|
|
692
|
+
{' '}{provider.description}
|
|
693
|
+
</Text>
|
|
694
|
+
</Box>
|
|
695
|
+
))}
|
|
696
|
+
</Box>
|
|
697
|
+
|
|
698
|
+
{/* Footer */}
|
|
699
|
+
<Box marginTop={2}>
|
|
700
|
+
<Text dimColor>↑↓: navigate | Enter: select | Esc: back</Text>
|
|
701
|
+
</Box>
|
|
702
|
+
</Box>
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Render OpenRouter Model Browser
|
|
707
|
+
if (step === 'openrouter-browse') {
|
|
708
|
+
return (
|
|
709
|
+
<Box flexDirection="column">
|
|
710
|
+
<Text>{' '}</Text>
|
|
711
|
+
<OpenRouterModelBrowser
|
|
712
|
+
onSelect={handleOpenRouterModelSelect}
|
|
713
|
+
onCancel={() => setStep('provider-select')}
|
|
714
|
+
/>
|
|
715
|
+
</Box>
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
|
|
524
719
|
// Render Manual Setup Form
|
|
525
720
|
return (
|
|
526
721
|
<Box flexDirection="column">
|