orquesta-cli 0.1.2 → 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 +151 -13
- 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 +213 -23
- 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
|
|
@@ -13,6 +13,8 @@ import { configManager } from '../../core/config/config-manager.js';
|
|
|
13
13
|
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
|
+
import { Logo } from './Logo.js';
|
|
17
|
+
import { OpenRouterModelBrowser } from './OpenRouterModelBrowser.js';
|
|
16
18
|
|
|
17
19
|
interface LLMSetupWizardProps {
|
|
18
20
|
onComplete: () => void;
|
|
@@ -28,9 +30,69 @@ interface FormData {
|
|
|
28
30
|
maxContextLength: string;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
type WizardStep = 'welcome' | 'token-input' | 'project-select' | 'manual-setup';
|
|
33
|
+
type WizardStep = 'welcome' | 'token-input' | 'project-select' | 'provider-select' | 'openrouter-browse' | 'manual-setup';
|
|
32
34
|
type FormField = 'name' | 'baseUrl' | 'apiKey' | 'modelId' | 'modelName' | 'maxContextLength' | 'buttons';
|
|
33
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
|
+
|
|
34
96
|
interface OrquestaProject {
|
|
35
97
|
id: string;
|
|
36
98
|
name: string;
|
|
@@ -55,6 +117,10 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
55
117
|
const [projectIndex, setProjectIndex] = useState(0);
|
|
56
118
|
const [isSyncing, setIsSyncing] = useState(false);
|
|
57
119
|
|
|
120
|
+
// Provider selection state
|
|
121
|
+
const [providerIndex, setProviderIndex] = useState(0);
|
|
122
|
+
const [selectedProvider, setSelectedProvider] = useState<ProviderPreset | null>(null);
|
|
123
|
+
|
|
58
124
|
// Manual setup form state
|
|
59
125
|
const [formData, setFormData] = useState<FormData>({
|
|
60
126
|
name: '',
|
|
@@ -86,14 +152,61 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
86
152
|
// "I have an Orquesta token"
|
|
87
153
|
setStep('token-input');
|
|
88
154
|
} else if (welcomeOptionIndex === 1) {
|
|
89
|
-
// "Set up manually"
|
|
90
|
-
setStep('
|
|
155
|
+
// "Set up manually" - go to provider selection
|
|
156
|
+
setStep('provider-select');
|
|
91
157
|
} else {
|
|
92
158
|
// "Skip for now"
|
|
93
159
|
onSkip();
|
|
94
160
|
}
|
|
95
161
|
}, [welcomeOptionIndex, onSkip]);
|
|
96
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
|
+
|
|
97
210
|
// Handle token validation
|
|
98
211
|
const handleTokenSubmit = useCallback(async () => {
|
|
99
212
|
if (!orquestaToken.trim()) {
|
|
@@ -338,6 +451,26 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
338
451
|
return;
|
|
339
452
|
}
|
|
340
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
|
+
|
|
341
474
|
// Manual setup form
|
|
342
475
|
if (step === 'manual-setup') {
|
|
343
476
|
if (key.tab) {
|
|
@@ -347,7 +480,12 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
347
480
|
} else if (key.return && formField === 'buttons') {
|
|
348
481
|
handleFormSubmit();
|
|
349
482
|
} else if (key.escape) {
|
|
350
|
-
|
|
483
|
+
// Go back to provider select or welcome
|
|
484
|
+
if (selectedProvider) {
|
|
485
|
+
setStep('provider-select');
|
|
486
|
+
} else {
|
|
487
|
+
setStep('welcome');
|
|
488
|
+
}
|
|
351
489
|
}
|
|
352
490
|
}
|
|
353
491
|
});
|
|
@@ -356,28 +494,22 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
356
494
|
if (step === 'welcome') {
|
|
357
495
|
return (
|
|
358
496
|
<Box flexDirection="column">
|
|
497
|
+
{/* Logo with animation */}
|
|
498
|
+
<Logo
|
|
499
|
+
showVersion={true}
|
|
500
|
+
showTagline={true}
|
|
501
|
+
animate={true}
|
|
502
|
+
/>
|
|
503
|
+
|
|
504
|
+
{/* Spacer */}
|
|
359
505
|
<Text>{' '}</Text>
|
|
360
|
-
<Text>{' '}</Text>
|
|
361
|
-
|
|
362
|
-
{/* Header */}
|
|
363
|
-
<Box borderStyle="double" borderColor="cyan" paddingX={2} marginBottom={1}>
|
|
364
|
-
<Text color="cyan" bold>
|
|
365
|
-
Welcome to Orquesta CLI!
|
|
366
|
-
</Text>
|
|
367
|
-
</Box>
|
|
368
|
-
|
|
369
|
-
{/* Description */}
|
|
370
|
-
<Box paddingX={1} marginBottom={1} flexDirection="column">
|
|
371
|
-
<Text color="white">AI-powered coding assistant with local LLM support.</Text>
|
|
372
|
-
<Text color="gray">Configure your LLM endpoint to get started.</Text>
|
|
373
|
-
</Box>
|
|
374
506
|
|
|
375
507
|
{/* Features */}
|
|
376
508
|
<Box paddingX={1} marginBottom={1} flexDirection="column">
|
|
377
|
-
<Text color="
|
|
378
|
-
<Text color="gray"> •
|
|
379
|
-
<Text color="gray"> •
|
|
380
|
-
<Text color="gray"> •
|
|
509
|
+
<Text color="white" bold>Get started with any LLM:</Text>
|
|
510
|
+
<Text color="gray"> • OpenAI, Anthropic, DeepSeek, OpenRouter</Text>
|
|
511
|
+
<Text color="gray"> • Local models via Ollama, vLLM, LM Studio</Text>
|
|
512
|
+
<Text color="gray"> • Sync configs with Orquesta dashboard</Text>
|
|
381
513
|
</Box>
|
|
382
514
|
|
|
383
515
|
{/* Options */}
|
|
@@ -403,7 +535,7 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
403
535
|
bold={welcomeOptionIndex === 2}
|
|
404
536
|
>
|
|
405
537
|
{welcomeOptionIndex === 2 ? '> ' : ' '}
|
|
406
|
-
⏭️ Skip for now (configure later via /
|
|
538
|
+
⏭️ Skip for now (configure later via /config)
|
|
407
539
|
</Text>
|
|
408
540
|
</Box>
|
|
409
541
|
|
|
@@ -526,6 +658,64 @@ export const LLMSetupWizard: React.FC<LLMSetupWizardProps> = ({ onComplete, onSk
|
|
|
526
658
|
);
|
|
527
659
|
}
|
|
528
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
|
+
|
|
529
719
|
// Render Manual Setup Form
|
|
530
720
|
return (
|
|
531
721
|
<Box flexDirection="column">
|