fraude-code 0.1.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.
Files changed (127) hide show
  1. package/README.md +68 -0
  2. package/dist/index.js +179297 -0
  3. package/package.json +88 -0
  4. package/src/agent/agent.ts +475 -0
  5. package/src/agent/contextManager.ts +141 -0
  6. package/src/agent/index.ts +14 -0
  7. package/src/agent/pendingChanges.ts +270 -0
  8. package/src/agent/prompts/AskPrompt.txt +10 -0
  9. package/src/agent/prompts/FastPrompt.txt +40 -0
  10. package/src/agent/prompts/PlannerPrompt.txt +51 -0
  11. package/src/agent/prompts/ReviewerPrompt.txt +57 -0
  12. package/src/agent/prompts/WorkerPrompt.txt +33 -0
  13. package/src/agent/subagents/askAgent.ts +37 -0
  14. package/src/agent/subagents/extractionAgent.ts +123 -0
  15. package/src/agent/subagents/fastAgent.ts +45 -0
  16. package/src/agent/subagents/managerAgent.ts +36 -0
  17. package/src/agent/subagents/relationAgent.ts +76 -0
  18. package/src/agent/subagents/researchSubAgent.ts +79 -0
  19. package/src/agent/subagents/reviewerSubAgent.ts +42 -0
  20. package/src/agent/subagents/workerSubAgent.ts +42 -0
  21. package/src/agent/tools/bashTool.ts +94 -0
  22. package/src/agent/tools/descriptions/bash.txt +47 -0
  23. package/src/agent/tools/descriptions/edit.txt +7 -0
  24. package/src/agent/tools/descriptions/glob.txt +4 -0
  25. package/src/agent/tools/descriptions/grep.txt +8 -0
  26. package/src/agent/tools/descriptions/lsp.txt +20 -0
  27. package/src/agent/tools/descriptions/plan.txt +3 -0
  28. package/src/agent/tools/descriptions/read.txt +9 -0
  29. package/src/agent/tools/descriptions/todo.txt +12 -0
  30. package/src/agent/tools/descriptions/write.txt +8 -0
  31. package/src/agent/tools/editTool.ts +44 -0
  32. package/src/agent/tools/globTool.ts +59 -0
  33. package/src/agent/tools/grepTool.ts +343 -0
  34. package/src/agent/tools/lspTool.ts +429 -0
  35. package/src/agent/tools/planTool.ts +118 -0
  36. package/src/agent/tools/readTool.ts +78 -0
  37. package/src/agent/tools/rememberTool.ts +91 -0
  38. package/src/agent/tools/testRunnerTool.ts +77 -0
  39. package/src/agent/tools/testTool.ts +44 -0
  40. package/src/agent/tools/todoTool.ts +224 -0
  41. package/src/agent/tools/writeTool.ts +33 -0
  42. package/src/commands/COMMANDS.ts +38 -0
  43. package/src/commands/cerebras/auth.ts +27 -0
  44. package/src/commands/cerebras/index.ts +31 -0
  45. package/src/commands/forget.ts +29 -0
  46. package/src/commands/google/auth.ts +24 -0
  47. package/src/commands/google/index.ts +31 -0
  48. package/src/commands/groq/add_model.ts +60 -0
  49. package/src/commands/groq/auth.ts +24 -0
  50. package/src/commands/groq/index.ts +33 -0
  51. package/src/commands/index.ts +65 -0
  52. package/src/commands/knowledge.ts +92 -0
  53. package/src/commands/log.ts +32 -0
  54. package/src/commands/mistral/auth.ts +27 -0
  55. package/src/commands/mistral/index.ts +31 -0
  56. package/src/commands/model/index.ts +145 -0
  57. package/src/commands/models/index.ts +16 -0
  58. package/src/commands/ollama/index.ts +29 -0
  59. package/src/commands/openrouter/add_model.ts +64 -0
  60. package/src/commands/openrouter/auth.ts +24 -0
  61. package/src/commands/openrouter/index.ts +33 -0
  62. package/src/commands/remember.ts +48 -0
  63. package/src/commands/serve.ts +31 -0
  64. package/src/commands/session/index.ts +21 -0
  65. package/src/commands/usage.ts +15 -0
  66. package/src/commands/visualize.ts +773 -0
  67. package/src/components/App.tsx +55 -0
  68. package/src/components/IntroComponent.tsx +70 -0
  69. package/src/components/LoaderComponent.tsx +68 -0
  70. package/src/components/OutputRenderer.tsx +88 -0
  71. package/src/components/SettingsRenderer.tsx +23 -0
  72. package/src/components/input/CommandSuggestions.tsx +41 -0
  73. package/src/components/input/FileSuggestions.tsx +61 -0
  74. package/src/components/input/InputBox.tsx +371 -0
  75. package/src/components/output/CheckpointView.tsx +13 -0
  76. package/src/components/output/CommandView.tsx +13 -0
  77. package/src/components/output/CommentView.tsx +12 -0
  78. package/src/components/output/ConfirmationView.tsx +179 -0
  79. package/src/components/output/ContextUsage.tsx +62 -0
  80. package/src/components/output/DiffView.tsx +202 -0
  81. package/src/components/output/ErrorView.tsx +14 -0
  82. package/src/components/output/InteractiveServerView.tsx +69 -0
  83. package/src/components/output/KnowledgeView.tsx +220 -0
  84. package/src/components/output/MarkdownView.tsx +15 -0
  85. package/src/components/output/ModelSelectView.tsx +71 -0
  86. package/src/components/output/ReasoningView.tsx +21 -0
  87. package/src/components/output/ToolCallView.tsx +45 -0
  88. package/src/components/settings/ModelList.tsx +250 -0
  89. package/src/components/settings/TokenUsage.tsx +274 -0
  90. package/src/config/schema.ts +19 -0
  91. package/src/config/settings.ts +229 -0
  92. package/src/index.tsx +100 -0
  93. package/src/parsers/tree-sitter-python.wasm +0 -0
  94. package/src/providers/providers.ts +71 -0
  95. package/src/services/PluginLoader.ts +123 -0
  96. package/src/services/cerebras.ts +69 -0
  97. package/src/services/embeddingService.ts +229 -0
  98. package/src/services/google.ts +65 -0
  99. package/src/services/graphSerializer.ts +248 -0
  100. package/src/services/groq.ts +23 -0
  101. package/src/services/knowledgeOrchestrator.ts +286 -0
  102. package/src/services/mistral.ts +79 -0
  103. package/src/services/ollama.ts +109 -0
  104. package/src/services/openrouter.ts +23 -0
  105. package/src/services/symbolExtractor.ts +277 -0
  106. package/src/store/useFraudeStore.ts +123 -0
  107. package/src/store/useSettingsStore.ts +38 -0
  108. package/src/theme.ts +26 -0
  109. package/src/types/Agent.ts +147 -0
  110. package/src/types/CommandDefinition.ts +8 -0
  111. package/src/types/Model.ts +94 -0
  112. package/src/types/OutputItem.ts +24 -0
  113. package/src/types/PluginContext.ts +55 -0
  114. package/src/types/TokenUsage.ts +5 -0
  115. package/src/types/assets.d.ts +4 -0
  116. package/src/utils/agentCognition.ts +1152 -0
  117. package/src/utils/fileSuggestions.ts +111 -0
  118. package/src/utils/index.ts +17 -0
  119. package/src/utils/initFraude.ts +8 -0
  120. package/src/utils/logger.ts +24 -0
  121. package/src/utils/lspClient.ts +1415 -0
  122. package/src/utils/paths.ts +24 -0
  123. package/src/utils/queryHandler.ts +227 -0
  124. package/src/utils/router.ts +278 -0
  125. package/src/utils/streamHandler.ts +132 -0
  126. package/src/utils/treeSitterQueries.ts +125 -0
  127. package/tsconfig.json +33 -0
@@ -0,0 +1,286 @@
1
+ /**
2
+ * KnowledgeOrchestrator - Centralized coordination of knowledge graph operations.
3
+ *
4
+ * Responsibilities:
5
+ * 1. Coordinate SymbolExtractor and AgentCognition for file indexing
6
+ * 2. Handle file change events (debounced)
7
+ * 3. Provide unified getContextForQuery() API for LLM context injection
8
+ */
9
+
10
+ import AgentCognition from "@/utils/agentCognition";
11
+ import { SymbolExtractor } from "@/services/symbolExtractor";
12
+ import { GraphSerializer } from "@/services/graphSerializer";
13
+ import fs from "fs";
14
+ import path from "path";
15
+ import ignore from "ignore";
16
+ import log from "@/utils/logger";
17
+
18
+ // Debounce time for file change events (ms)
19
+ const DEBOUNCE_MS = 500;
20
+
21
+ // File extensions to index
22
+ const INDEXABLE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".py"];
23
+
24
+ export class KnowledgeOrchestrator {
25
+ private static instance: KnowledgeOrchestrator;
26
+ private cognition: AgentCognition;
27
+ private extractor: SymbolExtractor;
28
+ private pendingChanges: Map<string, NodeJS.Timeout> = new Map();
29
+ private watchedDirs: Set<string> = new Set();
30
+ private watchers: Map<string, fs.FSWatcher> = new Map();
31
+ private ig = ignore();
32
+
33
+ private constructor() {
34
+ this.cognition = AgentCognition.getInstance();
35
+ this.extractor = new SymbolExtractor();
36
+ }
37
+
38
+ static getInstance(): KnowledgeOrchestrator {
39
+ if (!KnowledgeOrchestrator.instance) {
40
+ KnowledgeOrchestrator.instance = new KnowledgeOrchestrator();
41
+ }
42
+ return KnowledgeOrchestrator.instance;
43
+ }
44
+
45
+ /**
46
+ * Initialize the orchestrator with a project root.
47
+ * Loads .gitignore patterns and sets up file watching.
48
+ */
49
+ async init(rootPath: string): Promise<void> {
50
+ // Load .gitignore patterns
51
+ const gitignorePath = path.join(rootPath, ".gitignore");
52
+ if (fs.existsSync(gitignorePath)) {
53
+ const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
54
+ this.ig.add(gitignoreContent);
55
+ }
56
+ // Always ignore these
57
+ this.ig.add([".fraude", "node_modules", "dist", "build", ".git"]);
58
+
59
+ // Initialize cognition
60
+ await this.cognition.init();
61
+
62
+ log(`[KnowledgeOrchestrator] Initialized for ${rootPath}`);
63
+ }
64
+
65
+ /**
66
+ * Start watching a directory for file changes.
67
+ * Changes are debounced to avoid excessive re-indexing.
68
+ */
69
+ watchDirectory(dirPath: string): void {
70
+ if (this.watchedDirs.has(dirPath)) return;
71
+
72
+ try {
73
+ const watcher = fs.watch(
74
+ dirPath,
75
+ { recursive: true },
76
+ (eventType, filename) => {
77
+ if (!filename) return;
78
+
79
+ const fullPath = path.join(dirPath, filename);
80
+ const ext = path.extname(filename);
81
+
82
+ // Only process indexable files
83
+ if (!INDEXABLE_EXTENSIONS.includes(ext)) return;
84
+
85
+ // Check gitignore
86
+ const relativePath = path.relative(dirPath, fullPath);
87
+ if (this.ig.ignores(relativePath)) return;
88
+
89
+ // Debounce the change
90
+ this.debouncedReindex(fullPath);
91
+ },
92
+ );
93
+
94
+ this.watchers.set(dirPath, watcher);
95
+ this.watchedDirs.add(dirPath);
96
+ log(`[KnowledgeOrchestrator] Watching directory: ${dirPath}`);
97
+ } catch (e) {
98
+ log(`[KnowledgeOrchestrator] Failed to watch ${dirPath}: ${e}`);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Stop watching a directory.
104
+ */
105
+ unwatchDirectory(dirPath: string): void {
106
+ const watcher = this.watchers.get(dirPath);
107
+ if (watcher) {
108
+ watcher.close();
109
+ this.watchers.delete(dirPath);
110
+ this.watchedDirs.delete(dirPath);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Debounced file reindexing.
116
+ * Prevents rapid re-indexing when files are being actively edited.
117
+ */
118
+ private debouncedReindex(filePath: string): void {
119
+ // Cancel any pending reindex for this file
120
+ const existing = this.pendingChanges.get(filePath);
121
+ if (existing) {
122
+ clearTimeout(existing);
123
+ }
124
+
125
+ // Schedule new reindex
126
+ const timeout = setTimeout(async () => {
127
+ this.pendingChanges.delete(filePath);
128
+ await this.indexFile(filePath);
129
+ }, DEBOUNCE_MS);
130
+
131
+ this.pendingChanges.set(filePath, timeout);
132
+ }
133
+
134
+ /**
135
+ * Index a single file, updating the knowledge graph.
136
+ * Clears old symbols for the file before indexing.
137
+ */
138
+ async indexFile(filePath: string): Promise<void> {
139
+ if (!fs.existsSync(filePath)) {
140
+ log(`[KnowledgeOrchestrator] File not found, skipping: ${filePath}`);
141
+ return;
142
+ }
143
+
144
+ try {
145
+ await this.cognition.indexFile(filePath);
146
+ log(`[KnowledgeOrchestrator] Indexed: ${filePath}`);
147
+ } catch (e) {
148
+ log(`[KnowledgeOrchestrator] Failed to index ${filePath}: ${e}`);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Index an entire project directory.
154
+ * Respects .gitignore patterns and runs in background.
155
+ */
156
+ async indexProject(rootPath: string): Promise<void> {
157
+ await this.init(rootPath);
158
+ await this.cognition.indexDirectory(rootPath, INDEXABLE_EXTENSIONS);
159
+ this.watchDirectory(rootPath);
160
+ }
161
+
162
+ /**
163
+ * Handle a file modification event from external tools.
164
+ * Called by file-modifying tools to trigger re-indexing.
165
+ */
166
+ onFileModified(filePath: string): void {
167
+ const ext = path.extname(filePath);
168
+ if (!INDEXABLE_EXTENSIONS.includes(ext)) return;
169
+
170
+ this.debouncedReindex(filePath);
171
+ }
172
+
173
+ /**
174
+ * Get optimized context for a query.
175
+ * Combines relevant facts from the knowledge graph into LLM-friendly format.
176
+ */
177
+ async getContextForQuery(
178
+ query: string,
179
+ options?: { format?: "markdown" | "compact" },
180
+ ): Promise<string> {
181
+ const facts = await this.cognition.retrieveRelevant(query, 10);
182
+
183
+ if (facts.length === 0) {
184
+ return "";
185
+ }
186
+
187
+ // Use GraphSerializer for consistent formatting
188
+ const context = GraphSerializer.serialize(facts, undefined, {
189
+ format: options?.format || "markdown",
190
+ maxSymbols: 8,
191
+ maxDecisions: 4,
192
+ maxFacts: 4,
193
+ includeRelations: false,
194
+ });
195
+
196
+ return context
197
+ ? `<knowledge_context>\n${context}\n</knowledge_context>`
198
+ : "";
199
+ }
200
+
201
+ /**
202
+ * Get broad project context for session priming.
203
+ * Returns high-confidence facts without query-specific filtering.
204
+ */
205
+ // async getPrimingContext(): Promise<string> {
206
+ // await this.cognition.init();
207
+ // return this.cognition.getPrimingContext();
208
+ // }
209
+
210
+ /**
211
+ * Get a graph subset for serialization.
212
+ * Returns facts and their relationships within a specified depth.
213
+ */
214
+ async getGraphSubset(
215
+ nodeIds: string[],
216
+ depth: number = 1,
217
+ ): Promise<{
218
+ facts: Awaited<ReturnType<AgentCognition["findByType"]>>;
219
+ related: Map<string, Awaited<ReturnType<AgentCognition["findRelated"]>>>;
220
+ }> {
221
+ const related = new Map<
222
+ string,
223
+ Awaited<ReturnType<AgentCognition["findRelated"]>>
224
+ >();
225
+
226
+ // Collect starting facts
227
+ const allFacts = await this.cognition.query(
228
+ `MATCH (f:Fact) WHERE f.id IN $ids RETURN f`,
229
+ { ids: nodeIds },
230
+ );
231
+
232
+ // Get related facts for each starting node
233
+ for (const id of nodeIds) {
234
+ const relatedFacts = await this.cognition.findRelated(id, depth);
235
+ related.set(id, relatedFacts);
236
+ }
237
+
238
+ return { facts: allFacts as any, related };
239
+ }
240
+
241
+ /**
242
+ * Get all symbols defined in a specific file.
243
+ */
244
+ async getSymbolsForFile(
245
+ filePath: string,
246
+ ): Promise<Awaited<ReturnType<AgentCognition["findByType"]>>> {
247
+ const results = await this.cognition.query(
248
+ `
249
+ MATCH (f:Fact)
250
+ WHERE f.type = 'concept' AND f.data CONTAINS $file
251
+ RETURN f
252
+ `,
253
+ { file: `"${filePath}"` },
254
+ );
255
+
256
+ return results.map((row: any) => ({
257
+ id: row.f.id,
258
+ type: row.f.type,
259
+ content: row.f.content,
260
+ data: row.f.data ? JSON.parse(row.f.data) : undefined,
261
+ timestamp: row.f.timestamp,
262
+ sessionId: row.f.sessionId,
263
+ confidence: row.f.confidence,
264
+ validated: row.f.validated,
265
+ }));
266
+ }
267
+
268
+ /**
269
+ * Shutdown the orchestrator, closing all watchers.
270
+ */
271
+ shutdown(): void {
272
+ for (const [dir, watcher] of this.watchers) {
273
+ watcher.close();
274
+ }
275
+ this.watchers.clear();
276
+ this.watchedDirs.clear();
277
+ this.pendingChanges.clear();
278
+ log("[KnowledgeOrchestrator] Shutdown complete");
279
+ }
280
+ }
281
+
282
+ export function getKnowledgeOrchestrator(): KnowledgeOrchestrator {
283
+ return KnowledgeOrchestrator.getInstance();
284
+ }
285
+
286
+ export default KnowledgeOrchestrator;
@@ -0,0 +1,79 @@
1
+ import { UpdateSettings } from "@/config/settings";
2
+ import useSettingsStore from "../store/useSettingsStore";
3
+ import type { Model } from "../types/Model";
4
+
5
+ const getSettings = () => useSettingsStore.getState();
6
+
7
+ interface MistralModelsResponse {
8
+ object: string;
9
+ data: MistralModel[];
10
+ }
11
+
12
+ interface MistralModel {
13
+ capabilities: object;
14
+ created: number;
15
+ id: string;
16
+ max_context_length: number;
17
+ name: string;
18
+ owned_by: string;
19
+ }
20
+
21
+ class MistralClient {
22
+ async syncMistralModels() {
23
+ const apiKey = getSettings().mistral_api_key || process.env.MISTRAL_API_KEY;
24
+ if (!apiKey) {
25
+ return;
26
+ }
27
+ const url = `https://api.mistral.ai/v1/models`;
28
+ const options = {
29
+ method: "GET",
30
+ headers: { Authorization: `Bearer ${apiKey}` },
31
+ };
32
+ const response = await fetch(url, options);
33
+ if (!response.ok) {
34
+ throw new Error(`Failed to fetch Mistral models ${response.status}`);
35
+ }
36
+ const data = (await response.json()) as MistralModelsResponse;
37
+ const savedModels = getSettings().models;
38
+ const existingMistralModels = savedModels.filter(
39
+ (m) => m.type === "mistral",
40
+ );
41
+ const otherModels = savedModels.filter((m) => m.type !== "mistral");
42
+
43
+ const models: Model[] = data.data
44
+ .map((model) => {
45
+ const capabilities: string[] = [];
46
+ Object.entries(model.capabilities).map(([key, value]) => {
47
+ if (value === true) {
48
+ capabilities.push(key);
49
+ }
50
+ });
51
+
52
+ const existing = existingMistralModels.find((m) => m.name === model.id);
53
+
54
+ return {
55
+ type: "mistral",
56
+ name: model.id,
57
+ modified_at: new Date(model.created * 1000).toISOString(),
58
+ digest: existing?.digest || "",
59
+ capabilities,
60
+ usage: existing?.usage || {
61
+ promptTokens: 0,
62
+ completionTokens: 0,
63
+ totalTokens: 0,
64
+ },
65
+ details: {
66
+ provider: model.owned_by,
67
+ context_length: model.max_context_length,
68
+ ...existing?.details,
69
+ },
70
+ } as Model;
71
+ })
72
+ .filter((m) => m.name.includes("latest"));
73
+
74
+ const mergedModels = [...otherModels, ...models];
75
+ await UpdateSettings({ models: mergedModels });
76
+ }
77
+ }
78
+
79
+ export default new MistralClient();
@@ -0,0 +1,109 @@
1
+ import useSettingsStore from "../store/useSettingsStore";
2
+ import { UpdateSettings } from "../config/settings";
3
+ import type { Model } from "../types/Model";
4
+
5
+ const getSettings = () => useSettingsStore.getState();
6
+
7
+ class OllamaClient {
8
+ isOllamaHealthy = async (): Promise<boolean> => {
9
+ try {
10
+ const { ollamaUrl } = getSettings();
11
+ const response = await fetch(ollamaUrl);
12
+ const text = await response.text();
13
+ return response.ok && text === "Ollama is running";
14
+ } catch {
15
+ return false;
16
+ }
17
+ };
18
+
19
+ getOllamaModels = async (): Promise<Model[]> => {
20
+ const { ollamaUrl } = getSettings();
21
+ const response = await fetch(`${ollamaUrl}/api/tags`);
22
+ if (!response.ok) {
23
+ throw new Error(`Error connecting to Ollama: ${response.status}`);
24
+ }
25
+ const data: any = await response.json();
26
+ return data.models.map((m: any) => ({ ...m, type: "ollama" })) as Model[];
27
+ };
28
+
29
+ getOllamaModelDetails = async (model: string): Promise<any> => {
30
+ const { ollamaUrl } = getSettings();
31
+ const response = await fetch(`${ollamaUrl}/api/show`, {
32
+ method: "POST",
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ },
36
+ body: JSON.stringify({ model }),
37
+ });
38
+ if (!response.ok) {
39
+ throw new Error(`Error connecting to Ollama: ${response.status}`);
40
+ }
41
+ const data: any = await response.json();
42
+ return data;
43
+ };
44
+
45
+ syncOllamaModels = async () => {
46
+ try {
47
+ const savedModels = getSettings().models || [];
48
+ const availableModels = await this.getOllamaModels();
49
+
50
+ // Preserve non-Ollama models (groq, openrouter, etc.)
51
+ const nonOllamaModels = savedModels.filter((m) => m.type !== "ollama");
52
+
53
+ const mergedOllamaModels: Model[] = [];
54
+
55
+ for (const model of availableModels) {
56
+ const existing = savedModels.find(
57
+ (m) => m.name === model.name && m.type === "ollama",
58
+ );
59
+ if (existing && existing.details?.context_length) {
60
+ mergedOllamaModels.push(existing);
61
+ continue;
62
+ }
63
+
64
+ try {
65
+ const details = await this.getOllamaModelDetails(model.name);
66
+ let context_length: number | undefined;
67
+ if (
68
+ details.model_info &&
69
+ details.model_info["general.architecture"]
70
+ ) {
71
+ const arch = details.model_info["general.architecture"];
72
+ if (details.model_info[`${arch}.context_length`]) {
73
+ context_length = details.model_info[`${arch}.context_length`];
74
+ }
75
+ }
76
+
77
+ const enhancedModel: Model = {
78
+ ...model,
79
+ capabilities: details.capabilities || model.capabilities || [],
80
+ usage: existing?.usage || {
81
+ promptTokens: 0,
82
+ completionTokens: 0,
83
+ totalTokens: 0,
84
+ },
85
+ details: {
86
+ ...model.details,
87
+ ...details.details,
88
+ context_length: context_length,
89
+ },
90
+ };
91
+
92
+ mergedOllamaModels.push(enhancedModel);
93
+ } catch (err) {
94
+ console.error(`Failed to fetch details for ${model.name}`, err);
95
+ mergedOllamaModels.push(model);
96
+ }
97
+ }
98
+
99
+ // Combine non-Ollama models with the updated Ollama models
100
+ await UpdateSettings({
101
+ models: [...nonOllamaModels, ...mergedOllamaModels],
102
+ });
103
+ } catch (error) {
104
+ console.warn("Failed to sync Ollama models:", error);
105
+ }
106
+ };
107
+ }
108
+
109
+ export default new OllamaClient();
@@ -0,0 +1,23 @@
1
+ import useSettingsStore from "../store/useSettingsStore";
2
+
3
+ const getSettings = () => useSettingsStore.getState();
4
+
5
+ class OpenRouterClient {
6
+ async getModelData(model: string) {
7
+ const url = `https://openrouter.ai/api/v1/models/${model}/endpoints`;
8
+ const options = {
9
+ method: "GET",
10
+ headers: { Authorization: `Bearer ${getSettings().openrouter_api_key}` },
11
+ };
12
+ const response = await fetch(url, options);
13
+ if (!response.ok) {
14
+ throw new Error(
15
+ `Failed to fetch OpenRouter model ${model}: ${response.status}`
16
+ );
17
+ }
18
+ const data: any = await response.json();
19
+ return data;
20
+ }
21
+ }
22
+
23
+ export default new OpenRouterClient();