gencode-ai 0.2.0 → 0.3.0
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/agent/agent.d.ts +9 -2
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +37 -8
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/types.d.ts +5 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/cli/components/App.d.ts.map +1 -1
- package/dist/cli/components/App.js +15 -9
- package/dist/cli/components/App.js.map +1 -1
- package/dist/cli/components/Messages.js +1 -1
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/ModelSelector.d.ts +4 -3
- package/dist/cli/components/ModelSelector.d.ts.map +1 -1
- package/dist/cli/components/ModelSelector.js +54 -37
- package/dist/cli/components/ModelSelector.js.map +1 -1
- package/dist/cli/components/ProviderManager.d.ts +2 -2
- package/dist/cli/components/ProviderManager.d.ts.map +1 -1
- package/dist/cli/components/ProviderManager.js +137 -156
- package/dist/cli/components/ProviderManager.js.map +1 -1
- package/dist/cli/index.js +30 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/levels.d.ts +5 -5
- package/dist/config/levels.d.ts.map +1 -1
- package/dist/config/levels.js +20 -20
- package/dist/config/levels.js.map +1 -1
- package/dist/config/merger.js +1 -1
- package/dist/config/merger.js.map +1 -1
- package/dist/config/providers-config.d.ts +8 -5
- package/dist/config/providers-config.d.ts.map +1 -1
- package/dist/config/providers-config.js +19 -22
- package/dist/config/providers-config.js.map +1 -1
- package/dist/config/test-utils.d.ts +2 -2
- package/dist/config/test-utils.d.ts.map +1 -1
- package/dist/config/test-utils.js +4 -4
- package/dist/config/test-utils.js.map +1 -1
- package/dist/config/types.d.ts +23 -17
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +14 -14
- package/dist/config/types.js.map +1 -1
- package/dist/memory/memory-manager.d.ts +25 -12
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +241 -112
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/test-utils.d.ts +1 -1
- package/dist/memory/test-utils.d.ts.map +1 -1
- package/dist/memory/test-utils.js +3 -3
- package/dist/memory/test-utils.js.map +1 -1
- package/dist/memory/types.d.ts +20 -10
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +13 -13
- package/dist/memory/types.js.map +1 -1
- package/dist/migration/migrate.d.ts +24 -0
- package/dist/migration/migrate.d.ts.map +1 -0
- package/dist/migration/migrate.js +164 -0
- package/dist/migration/migrate.js.map +1 -0
- package/dist/permissions/persistence.d.ts +2 -2
- package/dist/permissions/persistence.js +4 -4
- package/dist/permissions/persistence.js.map +1 -1
- package/dist/planning/plan-file.d.ts +1 -1
- package/dist/planning/plan-file.js +2 -2
- package/dist/planning/plan-file.js.map +1 -1
- package/dist/prompts/index.d.ts +5 -4
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +11 -8
- package/dist/prompts/index.js.map +1 -1
- package/dist/providers/anthropic.d.ts +2 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +7 -0
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -1
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +7 -0
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts +20 -10
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +48 -24
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts +2 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +7 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/registry.d.ts +48 -34
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +72 -88
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/store.d.ts +43 -17
- package/dist/providers/store.d.ts.map +1 -1
- package/dist/providers/store.js +112 -19
- package/dist/providers/store.js.map +1 -1
- package/dist/providers/types.d.ts +23 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/vertex-ai.d.ts +15 -7
- package/dist/providers/vertex-ai.d.ts.map +1 -1
- package/dist/providers/vertex-ai.js +46 -13
- package/dist/providers/vertex-ai.js.map +1 -1
- package/dist/session/types.js +1 -1
- package/dist/session/types.js.map +1 -1
- package/docs/config-system-comparison.md +50 -50
- package/docs/cost-tracking-comparison.md +2 -2
- package/docs/memory-system.md +124 -31
- package/docs/permissions.md +2 -2
- package/docs/proposals/0006-memory-system.md +4 -4
- package/docs/proposals/0008-checkpointing.md +109 -2
- package/docs/proposals/0011-custom-commands.md +2 -1
- package/docs/proposals/0021-skills-system.md +2 -1
- package/docs/proposals/0023-permission-enhancements.md +2 -2
- package/docs/proposals/0033-enterprise-deployment.md +1 -1
- package/docs/proposals/0041-configuration-system.md +17 -19
- package/docs/proposals/0042-prompt-optimization.md +17 -9
- package/docs/proposals/README.md +5 -5
- package/docs/providers.md +94 -9
- package/package.json +3 -2
- package/scripts/migrate.ts +449 -0
- package/src/agent/agent.ts +51 -9
- package/src/agent/types.ts +5 -1
- package/src/cli/components/App.tsx +17 -8
- package/src/cli/components/Messages.tsx +1 -1
- package/src/cli/components/ModelSelector.tsx +62 -43
- package/src/cli/components/ProviderManager.tsx +278 -323
- package/src/cli/index.tsx +36 -17
- package/src/config/index.ts +5 -3
- package/src/config/levels.test.ts +22 -22
- package/src/config/levels.ts +22 -22
- package/src/config/loader.test.ts +14 -14
- package/src/config/manager.test.ts +19 -19
- package/src/config/merger.test.ts +23 -23
- package/src/config/merger.ts +1 -1
- package/src/config/providers-config.ts +23 -21
- package/src/config/test-utils.ts +6 -6
- package/src/config/types.ts +30 -20
- package/src/memory/memory-manager.test.ts +242 -24
- package/src/memory/memory-manager.ts +270 -141
- package/src/memory/test-utils.ts +4 -4
- package/src/memory/types.ts +28 -17
- package/src/permissions/persistence.ts +4 -4
- package/src/planning/plan-file.ts +2 -2
- package/src/prompts/index.ts +13 -9
- package/src/providers/anthropic.ts +9 -0
- package/src/providers/gemini.ts +9 -0
- package/src/providers/index.ts +76 -33
- package/src/providers/openai.ts +9 -0
- package/src/providers/registry.ts +116 -111
- package/src/providers/store.ts +130 -28
- package/src/providers/types.ts +33 -1
- package/src/providers/vertex-ai.ts +49 -13
- package/src/session/types.ts +1 -1
|
@@ -8,31 +8,32 @@ import { colors, icons } from './theme.js';
|
|
|
8
8
|
import { LoadingSpinner } from './Spinner.js';
|
|
9
9
|
import {
|
|
10
10
|
getProvidersSorted,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
getProviderClasses,
|
|
12
|
+
isProviderReady,
|
|
13
13
|
getSearchProvidersSorted,
|
|
14
|
-
type
|
|
15
|
-
type
|
|
14
|
+
type ProviderClass,
|
|
15
|
+
type ProviderMeta,
|
|
16
16
|
type SearchProviderDefinition,
|
|
17
17
|
} from '../../providers/registry.js';
|
|
18
18
|
import { getProviderStore, type ModelInfo } from '../../providers/store.js';
|
|
19
|
-
import { createProvider, type
|
|
19
|
+
import { createProvider, type Provider } from '../../providers/index.js';
|
|
20
20
|
import { isSearchProviderAvailable, type SearchProviderName } from '../../providers/search/index.js';
|
|
21
|
+
import type { AuthMethod } from '../../providers/types.js';
|
|
21
22
|
|
|
22
23
|
interface ProviderManagerProps {
|
|
23
24
|
onClose: () => void;
|
|
24
|
-
onProviderChange?: (providerId:
|
|
25
|
+
onProviderChange?: (providerId: Provider, model: string) => void;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
type View = 'list' | 'select-connection' | 'confirm-remove' | 'search-list';
|
|
28
29
|
type Tab = 'llm' | 'search';
|
|
29
30
|
|
|
30
31
|
interface ProviderItem {
|
|
31
|
-
|
|
32
|
+
providerMeta: ProviderMeta;
|
|
32
33
|
connected: boolean;
|
|
33
34
|
modelCount: number;
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
authMethod?: AuthMethod;
|
|
36
|
+
availableClasses: ProviderClass[];
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
interface SearchProviderItem {
|
|
@@ -51,22 +52,24 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
51
52
|
const [connectionIndex, setConnectionIndex] = useState(0);
|
|
52
53
|
const [loading, setLoading] = useState(false);
|
|
53
54
|
const [message, setMessage] = useState<string | null>(null);
|
|
54
|
-
const [
|
|
55
|
+
const [selectedProviderMeta, setSelectedProviderMeta] = useState<ProviderMeta | null>(null);
|
|
55
56
|
const [searchSelectedIndex, setSearchSelectedIndex] = useState(0);
|
|
56
57
|
|
|
57
58
|
// Build provider list
|
|
58
59
|
const buildProviderList = useCallback((): ProviderItem[] => {
|
|
59
60
|
const allProviders = getProvidersSorted();
|
|
60
|
-
return allProviders.map((
|
|
61
|
-
const connected = store.isConnected(
|
|
62
|
-
const connection = store.getConnection(
|
|
63
|
-
const
|
|
61
|
+
return allProviders.map((providerMeta) => {
|
|
62
|
+
const connected = store.isConnected(providerMeta.id);
|
|
63
|
+
const connection = store.getConnection(providerMeta.id);
|
|
64
|
+
const availableClasses = getProviderClasses(providerMeta.id).filter((cls) =>
|
|
65
|
+
isProviderReady(cls)
|
|
66
|
+
);
|
|
64
67
|
return {
|
|
65
|
-
|
|
68
|
+
providerMeta,
|
|
66
69
|
connected,
|
|
67
|
-
modelCount: store.getModelCount(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
modelCount: store.getModelCount(providerMeta.id),
|
|
71
|
+
authMethod: connection?.authMethod,
|
|
72
|
+
availableClasses,
|
|
70
73
|
};
|
|
71
74
|
});
|
|
72
75
|
}, [store]);
|
|
@@ -105,8 +108,8 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
105
108
|
const filterLower = filter.toLowerCase();
|
|
106
109
|
const filteredProviders = providerList.filter(
|
|
107
110
|
(item) =>
|
|
108
|
-
item.
|
|
109
|
-
item.
|
|
111
|
+
item.providerMeta.name.toLowerCase().includes(filterLower) ||
|
|
112
|
+
item.providerMeta.id.toLowerCase().includes(filterLower)
|
|
110
113
|
);
|
|
111
114
|
|
|
112
115
|
// Split into connected and available
|
|
@@ -121,61 +124,61 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
121
124
|
setSelectedIndex(0);
|
|
122
125
|
}, [filter]);
|
|
123
126
|
|
|
124
|
-
// Fetch and cache models for a provider
|
|
127
|
+
// Fetch and cache models for a provider with specific auth method
|
|
125
128
|
const fetchModels = async (
|
|
126
|
-
providerId:
|
|
127
|
-
|
|
129
|
+
providerId: Provider,
|
|
130
|
+
authMethod: AuthMethod
|
|
128
131
|
): Promise<ModelInfo[]> => {
|
|
129
132
|
try {
|
|
130
|
-
|
|
131
|
-
const implId = connOption?.providerImpl || providerId;
|
|
132
|
-
const provider = createProvider({ provider: implId });
|
|
133
|
+
const provider = createProvider({ provider: providerId, authMethod });
|
|
133
134
|
const models = await provider.listModels();
|
|
134
|
-
store.cacheModels(providerId, models);
|
|
135
|
+
store.cacheModels(providerId, authMethod, models);
|
|
135
136
|
return models;
|
|
136
137
|
} catch {
|
|
137
138
|
return [];
|
|
138
139
|
}
|
|
139
140
|
};
|
|
140
141
|
|
|
141
|
-
// Connect with a specific
|
|
142
|
-
const
|
|
142
|
+
// Connect with a specific provider class (auth method)
|
|
143
|
+
const connectWithClass = async (item: ProviderItem, providerClass: ProviderClass) => {
|
|
143
144
|
setLoading(true);
|
|
144
|
-
setMessage(`Connecting via ${
|
|
145
|
-
store.connect(
|
|
146
|
-
|
|
145
|
+
setMessage(`Connecting via ${providerClass.meta.displayName}...`);
|
|
146
|
+
store.connect(
|
|
147
|
+
item.providerMeta.id,
|
|
148
|
+
providerClass.meta.authMethod,
|
|
149
|
+
providerClass.meta.displayName
|
|
150
|
+
);
|
|
151
|
+
const models = await fetchModels(item.providerMeta.id, providerClass.meta.authMethod);
|
|
147
152
|
setLoading(false);
|
|
148
153
|
setMessage(`Connected! Cached ${models.length} models`);
|
|
149
154
|
refreshList();
|
|
150
155
|
setView('list');
|
|
151
|
-
|
|
156
|
+
setSelectedProviderMeta(null);
|
|
152
157
|
setTimeout(() => setMessage(null), 2000);
|
|
153
158
|
};
|
|
154
159
|
|
|
155
160
|
// Handle connect/refresh
|
|
156
161
|
const handleConnect = async (item: ProviderItem) => {
|
|
157
162
|
if (item.connected) {
|
|
158
|
-
// Refresh: re-fetch models
|
|
163
|
+
// Refresh: re-fetch models using the saved authMethod
|
|
159
164
|
setLoading(true);
|
|
160
|
-
setMessage(`Refreshing ${item.
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
const connOption = item.provider.connections.find((c) => c.method === connMethod);
|
|
164
|
-
const models = await fetchModels(item.provider.id, connOption);
|
|
165
|
+
setMessage(`Refreshing ${item.providerMeta.name}...`);
|
|
166
|
+
const authMethod = item.authMethod || 'api_key';
|
|
167
|
+
const models = await fetchModels(item.providerMeta.id, authMethod);
|
|
165
168
|
setLoading(false);
|
|
166
169
|
setMessage(`Cached ${models.length} models`);
|
|
167
170
|
refreshList();
|
|
168
171
|
setTimeout(() => setMessage(null), 2000);
|
|
169
172
|
} else {
|
|
170
|
-
// Check
|
|
171
|
-
const
|
|
173
|
+
// Check available provider classes
|
|
174
|
+
const availableClasses = item.availableClasses;
|
|
172
175
|
|
|
173
|
-
if (
|
|
174
|
-
// One
|
|
175
|
-
await
|
|
176
|
+
if (availableClasses.length === 1) {
|
|
177
|
+
// One available auth method - auto-connect
|
|
178
|
+
await connectWithClass(item, availableClasses[0]);
|
|
176
179
|
} else {
|
|
177
|
-
// Zero or multiple
|
|
178
|
-
|
|
180
|
+
// Zero or multiple auth methods - show selection view
|
|
181
|
+
setSelectedProviderMeta(item.providerMeta);
|
|
179
182
|
setConnectionIndex(0);
|
|
180
183
|
setView('select-connection');
|
|
181
184
|
}
|
|
@@ -185,299 +188,260 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
185
188
|
// Handle remove
|
|
186
189
|
const handleRemove = (item: ProviderItem) => {
|
|
187
190
|
if (!item.connected) return;
|
|
188
|
-
|
|
191
|
+
setSelectedProviderMeta(item.providerMeta);
|
|
189
192
|
setView('confirm-remove');
|
|
190
193
|
};
|
|
191
194
|
|
|
192
195
|
// Confirm remove
|
|
193
196
|
const confirmRemove = () => {
|
|
194
|
-
if (
|
|
195
|
-
store.disconnect(
|
|
197
|
+
if (selectedProviderMeta) {
|
|
198
|
+
store.disconnect(selectedProviderMeta.id);
|
|
196
199
|
refreshList();
|
|
197
200
|
setView('list');
|
|
198
|
-
|
|
201
|
+
setSelectedProviderMeta(null);
|
|
199
202
|
setMessage('Provider removed');
|
|
200
203
|
setTimeout(() => setMessage(null), 2000);
|
|
201
204
|
}
|
|
202
205
|
};
|
|
203
206
|
|
|
204
|
-
//
|
|
205
|
-
const goBack = () => {
|
|
206
|
-
setView('list');
|
|
207
|
-
setSelectedProvider(null);
|
|
208
|
-
setConnectionIndex(0);
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// Keyboard navigation for list view (LLM providers)
|
|
212
|
-
useInput(
|
|
213
|
-
(input, key) => {
|
|
214
|
-
if (key.tab || input === 's') {
|
|
215
|
-
// Switch to search tab
|
|
216
|
-
setTab('search');
|
|
217
|
-
setSearchSelectedIndex(0);
|
|
218
|
-
} else if (key.upArrow) {
|
|
219
|
-
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
220
|
-
} else if (key.downArrow) {
|
|
221
|
-
setSelectedIndex((i) => Math.min(allItems.length - 1, i + 1));
|
|
222
|
-
} else if (key.return && allItems.length > 0) {
|
|
223
|
-
handleConnect(allItems[selectedIndex]);
|
|
224
|
-
} else if (input === 'r' && allItems.length > 0 && allItems[selectedIndex].connected) {
|
|
225
|
-
handleRemove(allItems[selectedIndex]);
|
|
226
|
-
} else if (key.escape) {
|
|
227
|
-
onClose();
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
{ isActive: view === 'list' && tab === 'llm' && !loading }
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
// Keyboard navigation for search providers tab
|
|
207
|
+
// Input handling for list view
|
|
234
208
|
useInput(
|
|
235
209
|
(input, key) => {
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
210
|
+
if (view === 'list' && tab === 'llm') {
|
|
211
|
+
if (key.downArrow || input === 'j') {
|
|
212
|
+
setSelectedIndex((i) => Math.min(i + 1, allItems.length - 1));
|
|
213
|
+
} else if (key.upArrow || input === 'k') {
|
|
214
|
+
setSelectedIndex((i) => Math.max(i - 1, 0));
|
|
215
|
+
} else if (key.return) {
|
|
216
|
+
const item = allItems[selectedIndex];
|
|
217
|
+
if (item) handleConnect(item);
|
|
218
|
+
} else if (input === 'r' || input === 'R') {
|
|
219
|
+
const item = allItems[selectedIndex];
|
|
220
|
+
if (item?.connected) handleConnect(item);
|
|
221
|
+
} else if (input === 'd' || input === 'D') {
|
|
222
|
+
const item = allItems[selectedIndex];
|
|
223
|
+
if (item) handleRemove(item);
|
|
224
|
+
} else if (input === 'q' || input === 'Q' || key.escape) {
|
|
225
|
+
onClose();
|
|
226
|
+
} else if (input === '/') {
|
|
227
|
+
// Start filter mode (handled by TextInput)
|
|
228
|
+
} else if (key.tab) {
|
|
229
|
+
setTab('search');
|
|
230
|
+
setSearchSelectedIndex(0);
|
|
248
231
|
}
|
|
249
|
-
} else if (
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
(_input, key) => {
|
|
259
|
-
if (!selectedProvider) return;
|
|
260
|
-
const allConns = selectedProvider.connections;
|
|
261
|
-
|
|
262
|
-
if (key.upArrow) {
|
|
263
|
-
setConnectionIndex((i) => Math.max(0, i - 1));
|
|
264
|
-
} else if (key.downArrow) {
|
|
265
|
-
setConnectionIndex((i) => Math.min(allConns.length - 1, i + 1));
|
|
266
|
-
} else if (key.return && allConns.length > 0) {
|
|
267
|
-
const selectedConn = allConns[connectionIndex];
|
|
268
|
-
if (isConnectionReady(selectedConn)) {
|
|
269
|
-
const item = allItems.find((i) => i.provider.id === selectedProvider.id);
|
|
270
|
-
if (item) {
|
|
271
|
-
connectWithOption(item, selectedConn);
|
|
232
|
+
} else if (view === 'list' && tab === 'search') {
|
|
233
|
+
if (key.downArrow || input === 'j') {
|
|
234
|
+
setSearchSelectedIndex((i) => Math.min(i + 1, searchProviders.length - 1));
|
|
235
|
+
} else if (key.upArrow || input === 'k') {
|
|
236
|
+
setSearchSelectedIndex((i) => Math.max(i - 1, 0));
|
|
237
|
+
} else if (key.return) {
|
|
238
|
+
const item = searchProviders[searchSelectedIndex];
|
|
239
|
+
if (item && item.isAvailable) {
|
|
240
|
+
selectSearchProvider(item.provider.id);
|
|
272
241
|
}
|
|
242
|
+
} else if (input === 'q' || input === 'Q' || key.escape) {
|
|
243
|
+
onClose();
|
|
244
|
+
} else if (key.tab) {
|
|
245
|
+
setTab('llm');
|
|
246
|
+
setSelectedIndex(0);
|
|
247
|
+
}
|
|
248
|
+
} else if (view === 'select-connection') {
|
|
249
|
+
const item = allItems.find((p) => p.providerMeta.id === selectedProviderMeta?.id);
|
|
250
|
+
const availableClasses = item?.availableClasses || [];
|
|
251
|
+
|
|
252
|
+
if (key.downArrow || input === 'j') {
|
|
253
|
+
setConnectionIndex((i) => Math.min(i + 1, availableClasses.length - 1));
|
|
254
|
+
} else if (key.upArrow || input === 'k') {
|
|
255
|
+
setConnectionIndex((i) => Math.max(i - 1, 0));
|
|
256
|
+
} else if (key.return) {
|
|
257
|
+
if (item && availableClasses[connectionIndex]) {
|
|
258
|
+
connectWithClass(item, availableClasses[connectionIndex]);
|
|
259
|
+
}
|
|
260
|
+
} else if (key.escape) {
|
|
261
|
+
setView('list');
|
|
262
|
+
setSelectedProviderMeta(null);
|
|
263
|
+
}
|
|
264
|
+
} else if (view === 'confirm-remove') {
|
|
265
|
+
if (input === 'y' || input === 'Y') {
|
|
266
|
+
confirmRemove();
|
|
267
|
+
} else if (input === 'n' || input === 'N' || key.escape) {
|
|
268
|
+
setView('list');
|
|
269
|
+
setSelectedProviderMeta(null);
|
|
273
270
|
}
|
|
274
|
-
// If not ready, do nothing (user needs to set env vars first)
|
|
275
|
-
} else if (key.escape) {
|
|
276
|
-
goBack();
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
{ isActive: view === 'select-connection' }
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
// Keyboard for confirm-remove view
|
|
283
|
-
useInput(
|
|
284
|
-
(input, key) => {
|
|
285
|
-
if (input === 'y' || input === 'Y') {
|
|
286
|
-
confirmRemove();
|
|
287
|
-
} else if (input === 'n' || input === 'N' || key.escape) {
|
|
288
|
-
goBack();
|
|
289
271
|
}
|
|
290
272
|
},
|
|
291
|
-
{ isActive:
|
|
273
|
+
{ isActive: !loading }
|
|
292
274
|
);
|
|
293
275
|
|
|
276
|
+
// Render connection selection view
|
|
277
|
+
if (view === 'select-connection' && selectedProviderMeta) {
|
|
278
|
+
const item = allItems.find((p) => p.providerMeta.id === selectedProviderMeta.id);
|
|
279
|
+
const availableClasses = item?.availableClasses || [];
|
|
294
280
|
|
|
295
|
-
// Loading state
|
|
296
|
-
if (loading) {
|
|
297
281
|
return (
|
|
298
|
-
<Box flexDirection="column">
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
<Text color={colors.textMuted}> {message || 'Loading...'}</Text>
|
|
302
|
-
</Box>
|
|
303
|
-
</Box>
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Confirm remove view
|
|
308
|
-
if (view === 'confirm-remove' && selectedProvider) {
|
|
309
|
-
return (
|
|
310
|
-
<Box flexDirection="column">
|
|
311
|
-
<Text color={colors.warning}>Remove {selectedProvider.name}?</Text>
|
|
312
|
-
<Text color={colors.textMuted}>
|
|
313
|
-
This will clear cached models for this provider.
|
|
282
|
+
<Box flexDirection="column" padding={1}>
|
|
283
|
+
<Text bold color={colors.primary}>
|
|
284
|
+
Select Authentication Method for {selectedProviderMeta.name}
|
|
314
285
|
</Text>
|
|
286
|
+
<Text dimColor>Choose how to connect to this provider</Text>
|
|
287
|
+
<Box marginTop={1} flexDirection="column">
|
|
288
|
+
{availableClasses.length === 0 ? (
|
|
289
|
+
<Text color={colors.error}>
|
|
290
|
+
{icons.warning} No authentication methods available
|
|
291
|
+
</Text>
|
|
292
|
+
) : (
|
|
293
|
+
availableClasses.map((providerClass, idx) => (
|
|
294
|
+
<Box key={providerClass.meta.authMethod}>
|
|
295
|
+
<Text color={idx === connectionIndex ? colors.success : undefined}>
|
|
296
|
+
{idx === connectionIndex ? icons.radio : ' '}{' '}
|
|
297
|
+
</Text>
|
|
298
|
+
<Text
|
|
299
|
+
bold={idx === connectionIndex}
|
|
300
|
+
color={idx === connectionIndex ? colors.success : undefined}
|
|
301
|
+
>
|
|
302
|
+
{providerClass.meta.displayName}
|
|
303
|
+
</Text>
|
|
304
|
+
<Text dimColor> - {providerClass.meta.description}</Text>
|
|
305
|
+
</Box>
|
|
306
|
+
))
|
|
307
|
+
)}
|
|
308
|
+
</Box>
|
|
315
309
|
<Box marginTop={1}>
|
|
316
|
-
<Text
|
|
310
|
+
<Text dimColor>Press Enter to connect, Esc to cancel</Text>
|
|
317
311
|
</Box>
|
|
318
312
|
</Box>
|
|
319
313
|
);
|
|
320
314
|
}
|
|
321
315
|
|
|
322
|
-
//
|
|
323
|
-
if (view === '
|
|
324
|
-
const allConns = selectedProvider.connections;
|
|
325
|
-
const selectedConn = allConns[connectionIndex];
|
|
326
|
-
const isSelectedReady = selectedConn ? isConnectionReady(selectedConn) : false;
|
|
327
|
-
|
|
316
|
+
// Render confirm remove view
|
|
317
|
+
if (view === 'confirm-remove' && selectedProviderMeta) {
|
|
328
318
|
return (
|
|
329
|
-
<Box flexDirection="column">
|
|
330
|
-
<Text color={colors.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
<
|
|
334
|
-
{allConns.map((conn, idx) => {
|
|
335
|
-
const isSelected = idx === connectionIndex;
|
|
336
|
-
const ready = isConnectionReady(conn);
|
|
337
|
-
return (
|
|
338
|
-
<Box key={conn.method} paddingLeft={2} flexDirection="column">
|
|
339
|
-
<Box>
|
|
340
|
-
<Text color={isSelected ? colors.primary : colors.textMuted}>
|
|
341
|
-
{isSelected ? icons.arrow : ' '}
|
|
342
|
-
</Text>
|
|
343
|
-
<Text color={isSelected ? colors.text : colors.textSecondary} bold={isSelected}>
|
|
344
|
-
{conn.name}
|
|
345
|
-
</Text>
|
|
346
|
-
{ready ? (
|
|
347
|
-
<Text color={colors.success}> (ready)</Text>
|
|
348
|
-
) : (
|
|
349
|
-
<Text color={colors.textMuted}> (not configured)</Text>
|
|
350
|
-
)}
|
|
351
|
-
{conn.description && (
|
|
352
|
-
<Text color={colors.textMuted}> - {conn.description}</Text>
|
|
353
|
-
)}
|
|
354
|
-
</Box>
|
|
355
|
-
{isSelected && !ready && (
|
|
356
|
-
<Text color={colors.textMuted} dimColor>
|
|
357
|
-
{' '}Set: {conn.envVars.join(' or ')}
|
|
358
|
-
</Text>
|
|
359
|
-
)}
|
|
360
|
-
</Box>
|
|
361
|
-
);
|
|
362
|
-
})}
|
|
363
|
-
</Box>
|
|
364
|
-
|
|
319
|
+
<Box flexDirection="column" padding={1}>
|
|
320
|
+
<Text bold color={colors.error}>
|
|
321
|
+
Remove {selectedProviderMeta.name}?
|
|
322
|
+
</Text>
|
|
323
|
+
<Text dimColor>This will clear all cached models for this provider</Text>
|
|
365
324
|
<Box marginTop={1}>
|
|
366
|
-
<Text
|
|
367
|
-
↑↓ navigate · {isSelectedReady ? 'Enter connect · ' : ''}Esc back
|
|
368
|
-
</Text>
|
|
325
|
+
<Text dimColor>Press Y to confirm, N to cancel</Text>
|
|
369
326
|
</Box>
|
|
370
327
|
</Box>
|
|
371
328
|
);
|
|
372
329
|
}
|
|
373
330
|
|
|
374
|
-
//
|
|
331
|
+
// Render main list view
|
|
375
332
|
return (
|
|
376
|
-
<Box flexDirection="column">
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
{/* Tabs */}
|
|
382
|
-
<Box marginTop={1}>
|
|
383
|
-
<Text
|
|
384
|
-
color={tab === 'llm' ? colors.primary : colors.textMuted}
|
|
385
|
-
bold={tab === 'llm'}
|
|
386
|
-
>
|
|
387
|
-
[L] LLM Providers
|
|
388
|
-
</Text>
|
|
389
|
-
<Text color={colors.textMuted}> | </Text>
|
|
390
|
-
<Text
|
|
391
|
-
color={tab === 'search' ? colors.primary : colors.textMuted}
|
|
392
|
-
bold={tab === 'search'}
|
|
393
|
-
>
|
|
394
|
-
[S] Search Providers
|
|
333
|
+
<Box flexDirection="column" padding={1}>
|
|
334
|
+
{/* Title */}
|
|
335
|
+
<Box>
|
|
336
|
+
<Text bold color={colors.primary}>
|
|
337
|
+
Provider Manager
|
|
395
338
|
</Text>
|
|
339
|
+
<Text dimColor> - Manage LLM and Search providers</Text>
|
|
396
340
|
</Box>
|
|
397
341
|
|
|
398
|
-
{
|
|
399
|
-
|
|
400
|
-
|
|
342
|
+
{/* Tabs */}
|
|
343
|
+
<Box marginTop={1}>
|
|
344
|
+
<Box marginRight={2}>
|
|
345
|
+
<Text bold={tab === 'llm'} color={tab === 'llm' ? colors.primary : 'gray'}>
|
|
346
|
+
{tab === 'llm' ? '▶' : ' '} LLM Providers
|
|
347
|
+
</Text>
|
|
401
348
|
</Box>
|
|
402
|
-
|
|
349
|
+
<Box>
|
|
350
|
+
<Text bold={tab === 'search'} color={tab === 'search' ? colors.primary : 'gray'}>
|
|
351
|
+
{tab === 'search' ? '▶' : ' '} Search Providers
|
|
352
|
+
</Text>
|
|
353
|
+
</Box>
|
|
354
|
+
</Box>
|
|
403
355
|
|
|
404
356
|
{/* LLM Providers Tab */}
|
|
405
357
|
{tab === 'llm' && (
|
|
406
358
|
<>
|
|
359
|
+
{/* Filter */}
|
|
407
360
|
<Box marginTop={1}>
|
|
408
|
-
<Text
|
|
409
|
-
<TextInput value={filter} onChange={setFilter} placeholder="
|
|
361
|
+
<Text dimColor>Filter: </Text>
|
|
362
|
+
<TextInput value={filter} onChange={setFilter} placeholder="Type to filter..." />
|
|
410
363
|
</Box>
|
|
411
364
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
{
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
</Text>
|
|
432
|
-
<Text color={colors.textMuted}>
|
|
433
|
-
{' '}({connName}) · {item.modelCount} models
|
|
434
|
-
</Text>
|
|
435
|
-
<Text color={colors.success}> {icons.success}</Text>
|
|
436
|
-
</Box>
|
|
437
|
-
);
|
|
438
|
-
})}
|
|
439
|
-
</>
|
|
440
|
-
)}
|
|
441
|
-
|
|
442
|
-
{/* Available section */}
|
|
443
|
-
{available.length > 0 && (
|
|
444
|
-
<>
|
|
445
|
-
<Text color={colors.textMuted} dimColor={connected.length > 0}>
|
|
446
|
-
{connected.length > 0 ? '\n' : ''}Available:
|
|
365
|
+
{/* Status */}
|
|
366
|
+
{message && (
|
|
367
|
+
<Box marginTop={1}>
|
|
368
|
+
<Text color={colors.success}>{message}</Text>
|
|
369
|
+
</Box>
|
|
370
|
+
)}
|
|
371
|
+
|
|
372
|
+
{loading && (
|
|
373
|
+
<Box marginTop={1}>
|
|
374
|
+
<LoadingSpinner />
|
|
375
|
+
</Box>
|
|
376
|
+
)}
|
|
377
|
+
|
|
378
|
+
{/* Connected Providers */}
|
|
379
|
+
{connected.length > 0 && (
|
|
380
|
+
<>
|
|
381
|
+
<Box marginTop={1}>
|
|
382
|
+
<Text bold color={colors.success}>
|
|
383
|
+
Connected ({connected.length})
|
|
447
384
|
</Text>
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
<
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
</Text>
|
|
461
|
-
|
|
462
|
-
|
|
385
|
+
</Box>
|
|
386
|
+
{connected.map((item, idx) => {
|
|
387
|
+
const globalIdx = idx;
|
|
388
|
+
const isSelected = globalIdx === selectedIndex;
|
|
389
|
+
return (
|
|
390
|
+
<Box key={item.providerMeta.id}>
|
|
391
|
+
<Text color={isSelected ? colors.success : undefined}>
|
|
392
|
+
{isSelected ? icons.radio : ' '}{' '}
|
|
393
|
+
</Text>
|
|
394
|
+
<Text bold={isSelected}>{item.providerMeta.name}</Text>
|
|
395
|
+
<Text dimColor> ({item.modelCount} models)</Text>
|
|
396
|
+
{item.authMethod && (
|
|
397
|
+
<Text dimColor> - {item.authMethod}</Text>
|
|
398
|
+
)}
|
|
399
|
+
</Box>
|
|
400
|
+
);
|
|
401
|
+
})}
|
|
402
|
+
</>
|
|
403
|
+
)}
|
|
404
|
+
|
|
405
|
+
{/* Available Providers */}
|
|
406
|
+
{available.length > 0 && (
|
|
407
|
+
<>
|
|
408
|
+
<Box marginTop={1}>
|
|
409
|
+
<Text bold dimColor>
|
|
410
|
+
Available ({available.length})
|
|
411
|
+
</Text>
|
|
412
|
+
</Box>
|
|
413
|
+
{available.map((item, idx) => {
|
|
414
|
+
const globalIdx = connected.length + idx;
|
|
415
|
+
const isSelected = globalIdx === selectedIndex;
|
|
416
|
+
const hasAuth = item.availableClasses.length > 0;
|
|
417
|
+
return (
|
|
418
|
+
<Box key={item.providerMeta.id}>
|
|
419
|
+
<Text color={isSelected ? colors.success : undefined}>
|
|
420
|
+
{isSelected ? icons.radio : ' '}{' '}
|
|
421
|
+
</Text>
|
|
422
|
+
<Text
|
|
423
|
+
bold={isSelected}
|
|
424
|
+
dimColor={!hasAuth}
|
|
425
|
+
color={!hasAuth ? colors.error : undefined}
|
|
426
|
+
>
|
|
427
|
+
{item.providerMeta.name}
|
|
428
|
+
</Text>
|
|
429
|
+
{!hasAuth && (
|
|
430
|
+
<Text dimColor color={colors.error}>
|
|
431
|
+
{' '}
|
|
432
|
+
(no credentials)
|
|
463
433
|
</Text>
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
</>
|
|
471
|
-
)}
|
|
472
|
-
|
|
473
|
-
{allItems.length === 0 && (
|
|
474
|
-
<Text color={colors.textMuted}>No providers match "{filter}"</Text>
|
|
475
|
-
)}
|
|
476
|
-
</Box>
|
|
434
|
+
)}
|
|
435
|
+
</Box>
|
|
436
|
+
);
|
|
437
|
+
})}
|
|
438
|
+
</>
|
|
439
|
+
)}
|
|
477
440
|
|
|
441
|
+
{/* Help */}
|
|
478
442
|
<Box marginTop={1}>
|
|
479
|
-
<Text
|
|
480
|
-
|
|
443
|
+
<Text dimColor>
|
|
444
|
+
Enter: Connect/Refresh | R: Refresh | D: Remove | Tab: Switch | Q: Quit
|
|
481
445
|
</Text>
|
|
482
446
|
</Box>
|
|
483
447
|
</>
|
|
@@ -486,46 +450,37 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
486
450
|
{/* Search Providers Tab */}
|
|
487
451
|
{tab === 'search' && (
|
|
488
452
|
<>
|
|
489
|
-
<Box
|
|
490
|
-
<Text
|
|
491
|
-
{searchProviders.map((item, idx) => {
|
|
492
|
-
const isSelected = idx === searchSelectedIndex;
|
|
493
|
-
const envVars = item.provider.connections[0]?.envVars || [];
|
|
494
|
-
|
|
495
|
-
return (
|
|
496
|
-
<Box key={item.provider.id} paddingLeft={2} flexDirection="column">
|
|
497
|
-
<Box>
|
|
498
|
-
<Text color={isSelected ? colors.primary : colors.textMuted}>
|
|
499
|
-
{isSelected ? icons.arrow : ' '}{' '}
|
|
500
|
-
</Text>
|
|
501
|
-
<Text color={isSelected ? colors.text : colors.textSecondary} bold={isSelected}>
|
|
502
|
-
{item.provider.name}
|
|
503
|
-
</Text>
|
|
504
|
-
{!item.provider.requiresKey && (
|
|
505
|
-
<Text color={colors.success}> (no key required)</Text>
|
|
506
|
-
)}
|
|
507
|
-
{item.provider.requiresKey && item.isAvailable && (
|
|
508
|
-
<Text color={colors.success}> (configured)</Text>
|
|
509
|
-
)}
|
|
510
|
-
{item.provider.requiresKey && !item.isAvailable && (
|
|
511
|
-
<Text color={colors.textMuted}> (not configured)</Text>
|
|
512
|
-
)}
|
|
513
|
-
{item.isSelected && <Text color={colors.success}> {icons.success}</Text>}
|
|
514
|
-
</Box>
|
|
515
|
-
{isSelected && item.provider.requiresKey && !item.isAvailable && (
|
|
516
|
-
<Text color={colors.textMuted} dimColor>
|
|
517
|
-
{' '}Set: {envVars.join(' or ')}
|
|
518
|
-
</Text>
|
|
519
|
-
)}
|
|
520
|
-
</Box>
|
|
521
|
-
);
|
|
522
|
-
})}
|
|
453
|
+
<Box marginTop={1}>
|
|
454
|
+
<Text bold>Search Providers</Text>
|
|
523
455
|
</Box>
|
|
524
456
|
|
|
457
|
+
{searchProviders.map((item, idx) => {
|
|
458
|
+
const isSelected = idx === searchSelectedIndex;
|
|
459
|
+
return (
|
|
460
|
+
<Box key={item.provider.id}>
|
|
461
|
+
<Text color={isSelected ? colors.success : undefined}>
|
|
462
|
+
{isSelected ? icons.radio : ' '}{' '}
|
|
463
|
+
</Text>
|
|
464
|
+
<Text
|
|
465
|
+
bold={isSelected}
|
|
466
|
+
color={item.isSelected ? colors.success : undefined}
|
|
467
|
+
dimColor={!item.isAvailable}
|
|
468
|
+
>
|
|
469
|
+
{item.provider.name}
|
|
470
|
+
</Text>
|
|
471
|
+
{item.isSelected && <Text color={colors.success}> (current)</Text>}
|
|
472
|
+
{!item.isAvailable && (
|
|
473
|
+
<Text dimColor color={colors.error}>
|
|
474
|
+
{' '}
|
|
475
|
+
(not available)
|
|
476
|
+
</Text>
|
|
477
|
+
)}
|
|
478
|
+
</Box>
|
|
479
|
+
);
|
|
480
|
+
})}
|
|
481
|
+
|
|
525
482
|
<Box marginTop={1}>
|
|
526
|
-
<Text
|
|
527
|
-
↑↓ navigate · Enter select · Tab/l LLM providers · Esc exit
|
|
528
|
-
</Text>
|
|
483
|
+
<Text dimColor>Enter: Select | Tab: Switch | Q: Quit</Text>
|
|
529
484
|
</Box>
|
|
530
485
|
</>
|
|
531
486
|
)}
|