tarsk 0.2.5 → 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/README.md +1 -7
- package/dist/index.d.ts +3 -0
- package/dist/index.js +92 -32
- package/dist/lib/response-builder.d.ts +50 -0
- package/dist/lib/response-builder.js +56 -0
- package/dist/lib/stream-helper.d.ts +39 -0
- package/dist/lib/stream-helper.js +43 -0
- package/dist/managers/ConversationManager.d.ts +83 -0
- package/dist/managers/ConversationManager.js +129 -0
- package/dist/managers/GitManager.d.ts +133 -0
- package/dist/managers/GitManager.js +330 -0
- package/dist/managers/MetadataManager.d.ts +139 -0
- package/dist/managers/MetadataManager.js +309 -0
- package/dist/managers/ModelManager.d.ts +57 -0
- package/dist/managers/ModelManager.js +129 -0
- package/dist/managers/NeovateExecutor.d.ts +40 -0
- package/dist/managers/NeovateExecutor.js +138 -0
- package/dist/managers/ProjectManager.d.ts +162 -0
- package/dist/managers/ProjectManager.js +353 -0
- package/dist/managers/ThreadManager.d.ts +181 -0
- package/dist/managers/ThreadManager.js +325 -0
- package/dist/managers/conversation-manager.d.ts +83 -0
- package/dist/managers/conversation-manager.js +129 -0
- package/dist/managers/git-manager.d.ts +133 -0
- package/dist/managers/git-manager.js +330 -0
- package/dist/managers/metadata-manager.d.ts +139 -0
- package/dist/managers/metadata-manager.js +305 -0
- package/dist/managers/model-manager.d.ts +59 -0
- package/dist/managers/model-manager.js +144 -0
- package/dist/managers/neovate-executor.d.ts +43 -0
- package/dist/managers/neovate-executor.js +205 -0
- package/dist/managers/processing-state-manager.d.ts +40 -0
- package/dist/managers/processing-state-manager.js +27 -0
- package/dist/managers/project-manager.d.ts +199 -0
- package/dist/managers/project-manager.js +465 -0
- package/dist/managers/thread-manager.d.ts +193 -0
- package/dist/managers/thread-manager.js +368 -0
- package/dist/model-info-aihubmix.d.ts +25 -0
- package/dist/model-info-aihubmix.js +117 -0
- package/dist/model-info-openai.d.ts +17 -0
- package/dist/model-info-openai.js +59 -0
- package/dist/model-info-openrouter.d.ts +25 -0
- package/dist/model-info-openrouter.js +101 -0
- package/dist/model-info.d.ts +37 -0
- package/dist/model-info.js +39 -0
- package/dist/provider-data.d.ts +101 -0
- package/dist/provider-data.js +471 -0
- package/dist/provider.d.ts +10 -0
- package/dist/provider.js +192 -0
- package/dist/public/android-chrome-192x192.png +0 -0
- package/dist/public/android-chrome-512x512.png +0 -0
- package/dist/public/apple-touch-icon.png +0 -0
- package/dist/public/assets/index-B443aj9k.js +8506 -0
- package/dist/public/assets/index-CjXGVbI7.css +1 -0
- package/dist/public/assets/index-DJC-p914.js +8506 -0
- package/dist/public/favicon-16x16.png +0 -0
- package/dist/public/favicon-32x32.png +0 -0
- package/dist/public/favicon.ico +0 -0
- package/dist/public/index.html +28 -0
- package/dist/public/manifest.json +82 -0
- package/dist/public/placeholder-logo.svg +1 -0
- package/dist/public/placeholder.svg +1 -0
- package/dist/public/snpro.woff2 +0 -0
- package/dist/public/tarsk-color.svg +12 -0
- package/dist/public/tarsk.png +0 -0
- package/dist/public/tarsk.svg +12 -0
- package/dist/public/zalando-sans.woff2 +0 -0
- package/dist/routes/chat-old.d.ts +21 -0
- package/dist/routes/chat-old.js +251 -0
- package/dist/routes/chat.d.ts +21 -0
- package/dist/routes/chat.js +217 -0
- package/dist/routes/git.d.ts +4 -0
- package/dist/routes/git.js +668 -0
- package/dist/routes/models.d.ts +18 -0
- package/dist/routes/models.js +128 -0
- package/dist/routes/projects-old.d.ts +20 -0
- package/dist/routes/projects-old.js +297 -0
- package/dist/routes/projects.d.ts +20 -0
- package/dist/routes/projects.js +365 -0
- package/dist/routes/providers.d.ts +15 -0
- package/dist/routes/providers.js +130 -0
- package/dist/routes/threads-old.d.ts +14 -0
- package/dist/routes/threads-old.js +393 -0
- package/dist/routes/threads.d.ts +14 -0
- package/dist/routes/threads.js +352 -0
- package/dist/types/models.d.ts +315 -0
- package/dist/types/models.js +11 -0
- package/dist/utils/env-manager.d.ts +3 -0
- package/dist/utils/env-manager.js +60 -0
- package/dist/utils/open-router-models.d.ts +45 -0
- package/dist/utils/open-router-models.js +103 -0
- package/dist/utils/openai-models.d.ts +63 -0
- package/dist/utils/openai-models.js +152 -0
- package/dist/utils/openai-pricing-scraper.d.ts +17 -0
- package/dist/utils/openai-pricing-scraper.js +185 -0
- package/dist/utils/validation.d.ts +10 -0
- package/dist/utils/validation.js +20 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +12 -0
- package/package.json +36 -22
- package/LICENSE.md +0 -7
- package/dist/agent/agent.js +0 -131
- package/dist/agent/interfaces.js +0 -1
- package/dist/api/encryption.js +0 -41
- package/dist/api/models.js +0 -169
- package/dist/api/prompt.js +0 -12
- package/dist/api/settings.js +0 -43
- package/dist/api/test.js +0 -29
- package/dist/api/tools.js +0 -287
- package/dist/api/utils.js +0 -18
- package/dist/interfaces/meta.js +0 -1
- package/dist/interfaces/model.js +0 -1
- package/dist/interfaces/settings.js +0 -1
- package/dist/log/log.js +0 -33
- package/dist/prompt.js +0 -49
- package/dist/tools.js +0 -84
- package/dist/utils/files.js +0 -14
- package/dist/utils/json-file.js +0 -28
- package/dist/utils/strip-markdown.js +0 -5
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetadataManager handles persistence of project and thread metadata
|
|
3
|
+
*
|
|
4
|
+
* This manager provides JSON file-based storage with atomic writes
|
|
5
|
+
* to ensure data consistency even if the process crashes during write operations.
|
|
6
|
+
*/
|
|
7
|
+
import { promises as fs } from 'fs';
|
|
8
|
+
import { join, dirname } from 'path';
|
|
9
|
+
export class MetadataManager {
|
|
10
|
+
projectsFile;
|
|
11
|
+
threadsFile;
|
|
12
|
+
stateFile;
|
|
13
|
+
metadataDir;
|
|
14
|
+
/**
|
|
15
|
+
* Create a new MetadataManager
|
|
16
|
+
* @param rootFolder - Base directory where metadata will be stored
|
|
17
|
+
*/
|
|
18
|
+
constructor(rootFolder) {
|
|
19
|
+
this.metadataDir = join(rootFolder, '.metadata');
|
|
20
|
+
this.projectsFile = join(this.metadataDir, 'projects.json');
|
|
21
|
+
this.threadsFile = join(this.metadataDir, 'threads.json');
|
|
22
|
+
this.stateFile = join(this.metadataDir, 'state.json');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Initialize metadata directory if it doesn't exist
|
|
26
|
+
*/
|
|
27
|
+
async initialize() {
|
|
28
|
+
try {
|
|
29
|
+
await fs.mkdir(this.metadataDir, { recursive: true });
|
|
30
|
+
// Create empty files if they don't exist
|
|
31
|
+
try {
|
|
32
|
+
await fs.access(this.projectsFile);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
await this.saveProjects([]);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
await fs.access(this.threadsFile);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
await this.saveThreads([]);
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await fs.access(this.stateFile);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
await this.saveState({ selectedThreadId: null, providerKeys: {}, enabledModels: {} });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new Error(`Failed to initialize metadata directory: ${error}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Save projects to disk using atomic write
|
|
56
|
+
* @param projects - Array of projects to save
|
|
57
|
+
*/
|
|
58
|
+
async saveProjects(projects) {
|
|
59
|
+
await this.atomicWrite(this.projectsFile, projects);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Load projects from disk
|
|
63
|
+
* @returns Array of projects
|
|
64
|
+
*/
|
|
65
|
+
async loadProjects() {
|
|
66
|
+
try {
|
|
67
|
+
const data = await fs.readFile(this.projectsFile, 'utf-8');
|
|
68
|
+
const projects = JSON.parse(data);
|
|
69
|
+
// Convert date strings back to Date objects
|
|
70
|
+
return projects.map((project) => ({
|
|
71
|
+
...project,
|
|
72
|
+
createdAt: new Date(project.createdAt)
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error.code === 'ENOENT') {
|
|
77
|
+
// File doesn't exist yet, return empty array
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`Failed to load projects: ${error}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Save threads to disk using atomic write
|
|
85
|
+
* @param threads - Array of threads to save
|
|
86
|
+
*/
|
|
87
|
+
async saveThreads(threads) {
|
|
88
|
+
console.log('[MetadataManager] Saving threads:', threads.map(t => ({
|
|
89
|
+
id: t.id,
|
|
90
|
+
title: t.title,
|
|
91
|
+
})));
|
|
92
|
+
await this.atomicWrite(this.threadsFile, threads);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Load threads from disk
|
|
96
|
+
* @returns Array of threads
|
|
97
|
+
*/
|
|
98
|
+
async loadThreads() {
|
|
99
|
+
try {
|
|
100
|
+
const data = await fs.readFile(this.threadsFile, 'utf-8');
|
|
101
|
+
const threads = JSON.parse(data);
|
|
102
|
+
// Convert date strings back to Date objects
|
|
103
|
+
const result = threads.map((thread) => ({
|
|
104
|
+
...thread,
|
|
105
|
+
createdAt: new Date(thread.createdAt)
|
|
106
|
+
}));
|
|
107
|
+
console.log('[MetadataManager] Loaded threads:', result.map((t) => ({
|
|
108
|
+
id: t.id,
|
|
109
|
+
title: t.title,
|
|
110
|
+
})));
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error.code === 'ENOENT') {
|
|
115
|
+
// File doesn't exist yet, return empty array
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`Failed to load threads: ${error}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Perform atomic write using temp file
|
|
123
|
+
*
|
|
124
|
+
* This ensures that if the process crashes during write,
|
|
125
|
+
* we don't end up with corrupted or partial data.
|
|
126
|
+
*
|
|
127
|
+
* @param filePath - Target file path
|
|
128
|
+
* @param data - Data to write
|
|
129
|
+
*/
|
|
130
|
+
async atomicWrite(filePath, data) {
|
|
131
|
+
const tempFile = `${filePath}.tmp`;
|
|
132
|
+
try {
|
|
133
|
+
// Ensure directory exists
|
|
134
|
+
await fs.mkdir(dirname(filePath), { recursive: true });
|
|
135
|
+
// Write to temp file
|
|
136
|
+
const jsonData = JSON.stringify(data, null, 2);
|
|
137
|
+
await fs.writeFile(tempFile, jsonData, 'utf-8');
|
|
138
|
+
// Atomic rename (overwrites target file)
|
|
139
|
+
await fs.rename(tempFile, filePath);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// Clean up temp file if it exists
|
|
143
|
+
try {
|
|
144
|
+
await fs.unlink(tempFile);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Ignore cleanup errors
|
|
148
|
+
}
|
|
149
|
+
throw new Error(`Failed to write ${filePath}: ${error}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get the metadata directory path
|
|
154
|
+
*/
|
|
155
|
+
getMetadataDir() {
|
|
156
|
+
return this.metadataDir;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get the projects file path
|
|
160
|
+
*/
|
|
161
|
+
getProjectsFile() {
|
|
162
|
+
return this.projectsFile;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get the threads file path
|
|
166
|
+
*/
|
|
167
|
+
getThreadsFile() {
|
|
168
|
+
return this.threadsFile;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Save application state to disk
|
|
172
|
+
* @param state - Application state to save
|
|
173
|
+
*/
|
|
174
|
+
async saveState(state) {
|
|
175
|
+
await this.atomicWrite(this.stateFile, state);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Load application state from disk
|
|
179
|
+
* @returns Application state
|
|
180
|
+
*/
|
|
181
|
+
async loadState() {
|
|
182
|
+
try {
|
|
183
|
+
const data = await fs.readFile(this.stateFile, 'utf-8');
|
|
184
|
+
const state = JSON.parse(data);
|
|
185
|
+
// Ensure providerKeys exists
|
|
186
|
+
if (!state.providerKeys) {
|
|
187
|
+
state.providerKeys = {};
|
|
188
|
+
}
|
|
189
|
+
// Ensure enabledModels exists
|
|
190
|
+
if (!state.enabledModels) {
|
|
191
|
+
state.enabledModels = {};
|
|
192
|
+
}
|
|
193
|
+
return state;
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error.code === 'ENOENT') {
|
|
197
|
+
return { selectedThreadId: null, providerKeys: {}, enabledModels: {} };
|
|
198
|
+
}
|
|
199
|
+
throw new Error(`Failed to load state: ${error}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Update all provider keys at once
|
|
204
|
+
* @param keys - Object mapping provider names to API keys
|
|
205
|
+
*/
|
|
206
|
+
async saveAllProviderKeys(keys) {
|
|
207
|
+
const state = await this.loadState();
|
|
208
|
+
state.providerKeys = { ...state.providerKeys, ...keys };
|
|
209
|
+
await this.saveState(state);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Update provider keys
|
|
213
|
+
* @param providerName - Name of the provider
|
|
214
|
+
* @param apiKey - API key to save
|
|
215
|
+
*/
|
|
216
|
+
async saveProviderKey(providerName, apiKey) {
|
|
217
|
+
const state = await this.loadState();
|
|
218
|
+
state.providerKeys[providerName] = apiKey;
|
|
219
|
+
await this.saveState(state);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get all provider keys
|
|
223
|
+
* @returns Record of provider names to API keys
|
|
224
|
+
*/
|
|
225
|
+
async getProviderKeys() {
|
|
226
|
+
const state = await this.loadState();
|
|
227
|
+
return state.providerKeys;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Set the selected thread ID
|
|
231
|
+
* @param threadId - Thread ID to select (or null to deselect)
|
|
232
|
+
*/
|
|
233
|
+
async setSelectedThread(threadId) {
|
|
234
|
+
const state = await this.loadState();
|
|
235
|
+
state.selectedThreadId = threadId;
|
|
236
|
+
await this.saveState(state);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get the selected thread ID
|
|
240
|
+
* @returns The selected thread ID or null
|
|
241
|
+
*/
|
|
242
|
+
async getSelectedThread() {
|
|
243
|
+
const state = await this.loadState();
|
|
244
|
+
return state.selectedThreadId;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Enable a model for a provider
|
|
248
|
+
* @param provider - Provider name
|
|
249
|
+
* @param modelId - Model ID to enable
|
|
250
|
+
*/
|
|
251
|
+
async enableModel(provider, modelId) {
|
|
252
|
+
const state = await this.loadState();
|
|
253
|
+
if (!state.enabledModels[provider]) {
|
|
254
|
+
state.enabledModels[provider] = [];
|
|
255
|
+
}
|
|
256
|
+
if (!state.enabledModels[provider].includes(modelId)) {
|
|
257
|
+
state.enabledModels[provider].push(modelId);
|
|
258
|
+
}
|
|
259
|
+
await this.saveState(state);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Disable a model for a provider
|
|
263
|
+
* @param provider - Provider name
|
|
264
|
+
* @param modelId - Model ID to disable
|
|
265
|
+
*/
|
|
266
|
+
async disableModel(provider, modelId) {
|
|
267
|
+
const state = await this.loadState();
|
|
268
|
+
if (state.enabledModels[provider]) {
|
|
269
|
+
state.enabledModels[provider] = state.enabledModels[provider].filter((id) => id !== modelId);
|
|
270
|
+
if (state.enabledModels[provider].length === 0) {
|
|
271
|
+
delete state.enabledModels[provider];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
await this.saveState(state);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Get enabled models for a provider
|
|
278
|
+
* @param provider - Provider name
|
|
279
|
+
* @returns Array of enabled model IDs
|
|
280
|
+
*/
|
|
281
|
+
async getEnabledModels(provider) {
|
|
282
|
+
const state = await this.loadState();
|
|
283
|
+
return state.enabledModels[provider] || [];
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get all enabled models across all providers
|
|
287
|
+
* @returns Object mapping provider to array of model IDs
|
|
288
|
+
*/
|
|
289
|
+
async getAllEnabledModels() {
|
|
290
|
+
const state = await this.loadState();
|
|
291
|
+
return state.enabledModels;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Set enabled models for a provider
|
|
295
|
+
* @param provider - Provider name
|
|
296
|
+
* @param modelIds - Array of model IDs to enable
|
|
297
|
+
*/
|
|
298
|
+
async setEnabledModels(provider, modelIds) {
|
|
299
|
+
const state = await this.loadState();
|
|
300
|
+
if (modelIds.length === 0) {
|
|
301
|
+
delete state.enabledModels[provider];
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
state.enabledModels[provider] = modelIds;
|
|
305
|
+
}
|
|
306
|
+
await this.saveState(state);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=MetadataManager.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModelManager
|
|
3
|
+
*
|
|
4
|
+
* Manages available and enabled models across different providers
|
|
5
|
+
*/
|
|
6
|
+
import { Model } from '../types/models.js';
|
|
7
|
+
import { MetadataManager } from './MetadataManager.js';
|
|
8
|
+
/**
|
|
9
|
+
* ModelManager handles model availability and selection
|
|
10
|
+
*/
|
|
11
|
+
export declare class ModelManager {
|
|
12
|
+
private metadataManager;
|
|
13
|
+
/**
|
|
14
|
+
* Create a new ModelManager
|
|
15
|
+
* @param metadataManager - MetadataManager instance for persistence
|
|
16
|
+
*/
|
|
17
|
+
constructor(metadataManager: MetadataManager);
|
|
18
|
+
/**
|
|
19
|
+
* Get available models for a provider
|
|
20
|
+
* @param provider - Provider name (e.g., "openrouter", "openai")
|
|
21
|
+
* @returns Array of Model objects
|
|
22
|
+
* @throws Error if provider is not supported
|
|
23
|
+
*/
|
|
24
|
+
getAvailableModels(provider: string): Promise<Model[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Get enabled models for a provider
|
|
27
|
+
* @param provider - Provider name
|
|
28
|
+
* @returns Array of Model objects that are enabled
|
|
29
|
+
*/
|
|
30
|
+
getEnabledModels(provider: string): Promise<Model[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Get a specific model by ID from a provider
|
|
33
|
+
* @param provider - Provider name
|
|
34
|
+
* @param modelId - Model ID
|
|
35
|
+
* @returns Model object or null if not found
|
|
36
|
+
*/
|
|
37
|
+
getModel(provider: string, modelId: string): Promise<Model | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Enable a model for a provider
|
|
40
|
+
* @param provider - Provider name
|
|
41
|
+
* @param modelId - Model ID to enable
|
|
42
|
+
* @throws Error if model is not found
|
|
43
|
+
*/
|
|
44
|
+
enableModel(provider: string, modelId: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Disable a model for a provider
|
|
47
|
+
* @param provider - Provider name
|
|
48
|
+
* @param modelId - Model ID to disable
|
|
49
|
+
*/
|
|
50
|
+
disableModel(provider: string, modelId: string): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Get all enabled models across all providers
|
|
53
|
+
* @returns Object mapping provider to array of enabled Models
|
|
54
|
+
*/
|
|
55
|
+
getAllEnabledModels(): Promise<Record<string, Model[]>>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=ModelManager.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModelManager
|
|
3
|
+
*
|
|
4
|
+
* Manages available and enabled models across different providers
|
|
5
|
+
*/
|
|
6
|
+
import { PROVIDER_DATA } from '../provider-data.js';
|
|
7
|
+
/**
|
|
8
|
+
* Convert provider model names to Model interface
|
|
9
|
+
* @param provider - Provider ID
|
|
10
|
+
* @param modelNames - Array of model name strings
|
|
11
|
+
* @returns Array of Model objects
|
|
12
|
+
*/
|
|
13
|
+
function convertProvidersToModels(provider, modelNames) {
|
|
14
|
+
return modelNames.map((modelId) => ({
|
|
15
|
+
id: modelId,
|
|
16
|
+
name: modelId, // Use model ID as name since provider-data doesn't have human-readable names
|
|
17
|
+
description: `${provider} model: ${modelId}`,
|
|
18
|
+
provider: provider,
|
|
19
|
+
pricing: {
|
|
20
|
+
prompt: 0,
|
|
21
|
+
completion: 0,
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* ModelManager handles model availability and selection
|
|
27
|
+
*/
|
|
28
|
+
export class ModelManager {
|
|
29
|
+
metadataManager;
|
|
30
|
+
/**
|
|
31
|
+
* Create a new ModelManager
|
|
32
|
+
* @param metadataManager - MetadataManager instance for persistence
|
|
33
|
+
*/
|
|
34
|
+
constructor(metadataManager) {
|
|
35
|
+
this.metadataManager = metadataManager;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get available models for a provider
|
|
39
|
+
* @param provider - Provider name (e.g., "openrouter", "openai")
|
|
40
|
+
* @returns Array of Model objects
|
|
41
|
+
* @throws Error if provider is not supported
|
|
42
|
+
*/
|
|
43
|
+
async getAvailableModels(provider) {
|
|
44
|
+
const providerData = PROVIDER_DATA.find(p => p.id === provider.toLowerCase());
|
|
45
|
+
if (!providerData) {
|
|
46
|
+
throw new Error(`Provider "${provider}" is not supported`);
|
|
47
|
+
}
|
|
48
|
+
return convertProvidersToModels(provider, providerData.models);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get enabled models for a provider
|
|
52
|
+
* @param provider - Provider name
|
|
53
|
+
* @returns Array of Model objects that are enabled
|
|
54
|
+
*/
|
|
55
|
+
async getEnabledModels(provider) {
|
|
56
|
+
const enabledModelIds = await this.metadataManager.getEnabledModels(provider);
|
|
57
|
+
if (enabledModelIds.length === 0) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const availableModels = await this.getAvailableModels(provider);
|
|
62
|
+
return availableModels.filter((model) => enabledModelIds.includes(model.id));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// If we can't fetch available models, return empty
|
|
66
|
+
console.error(`Failed to get enabled models for ${provider}:`, error);
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get a specific model by ID from a provider
|
|
72
|
+
* @param provider - Provider name
|
|
73
|
+
* @param modelId - Model ID
|
|
74
|
+
* @returns Model object or null if not found
|
|
75
|
+
*/
|
|
76
|
+
async getModel(provider, modelId) {
|
|
77
|
+
try {
|
|
78
|
+
const availableModels = await this.getAvailableModels(provider);
|
|
79
|
+
return availableModels.find((m) => m.id === modelId) || null;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error(`Failed to get model ${modelId} from ${provider}:`, error);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Enable a model for a provider
|
|
88
|
+
* @param provider - Provider name
|
|
89
|
+
* @param modelId - Model ID to enable
|
|
90
|
+
* @throws Error if model is not found
|
|
91
|
+
*/
|
|
92
|
+
async enableModel(provider, modelId) {
|
|
93
|
+
// Verify the model exists
|
|
94
|
+
const model = await this.getModel(provider, modelId);
|
|
95
|
+
if (!model) {
|
|
96
|
+
throw new Error(`Model "${modelId}" not found in provider "${provider}"`);
|
|
97
|
+
}
|
|
98
|
+
await this.metadataManager.enableModel(provider, modelId);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Disable a model for a provider
|
|
102
|
+
* @param provider - Provider name
|
|
103
|
+
* @param modelId - Model ID to disable
|
|
104
|
+
*/
|
|
105
|
+
async disableModel(provider, modelId) {
|
|
106
|
+
await this.metadataManager.disableModel(provider, modelId);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get all enabled models across all providers
|
|
110
|
+
* @returns Object mapping provider to array of enabled Models
|
|
111
|
+
*/
|
|
112
|
+
async getAllEnabledModels() {
|
|
113
|
+
const enabledByProvider = await this.metadataManager.getAllEnabledModels();
|
|
114
|
+
const result = {};
|
|
115
|
+
for (const provider of Object.keys(enabledByProvider)) {
|
|
116
|
+
try {
|
|
117
|
+
const models = await this.getEnabledModels(provider);
|
|
118
|
+
if (models.length > 0) {
|
|
119
|
+
result[provider] = models;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error(`Failed to get enabled models for ${provider}:`, error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=ModelManager.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NeovateExecutor handles execution of neovate commands via the @neovate/code SDK
|
|
3
|
+
*
|
|
4
|
+
* This manager is responsible for:
|
|
5
|
+
* - Executing streaming sessions via the SDK
|
|
6
|
+
* - Streaming result messages back to the caller
|
|
7
|
+
* - Handling execution errors
|
|
8
|
+
*/
|
|
9
|
+
import { NeovateEvent, ExecutionContext } from '../types/models.js';
|
|
10
|
+
/**
|
|
11
|
+
* NeovateExecutor interface defines the contract for neovate command execution
|
|
12
|
+
*/
|
|
13
|
+
export interface NeovateExecutor {
|
|
14
|
+
/**
|
|
15
|
+
* Executes a neovate command with the given prompt and context
|
|
16
|
+
* @param prompt - The user's message/prompt to send to neovate
|
|
17
|
+
* @param context - ExecutionContext containing thread info and options
|
|
18
|
+
* @yields NeovateEvent objects for system, message, result, and error events
|
|
19
|
+
*/
|
|
20
|
+
execute(prompt: string, context: ExecutionContext): AsyncGenerator<NeovateEvent>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* NeovateExecutorImpl provides the implementation for neovate command execution
|
|
24
|
+
* using the @neovate/code SDK with streaming sessions.
|
|
25
|
+
*/
|
|
26
|
+
export declare class NeovateExecutorImpl implements NeovateExecutor {
|
|
27
|
+
/**
|
|
28
|
+
* Executes a neovate prompt using the SDK via streaming sessions
|
|
29
|
+
*
|
|
30
|
+
* @param userPrompt - The user's message/prompt to send to neovate
|
|
31
|
+
* @param context - ExecutionContext with threadId, threadPath, model, and attachments
|
|
32
|
+
* @yields NeovateEvent objects for message, result, and error events
|
|
33
|
+
*
|
|
34
|
+
* Requirements:
|
|
35
|
+
* - 5.2 - WHEN the CLI receives a chat message, THE CLI SHALL execute the Neovate command
|
|
36
|
+
* - 5.4 - WHEN the Neovate command executes, THE CLI SHALL stream the output back to the App
|
|
37
|
+
*/
|
|
38
|
+
execute(userPrompt: string, context: ExecutionContext): AsyncGenerator<NeovateEvent>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=NeovateExecutor.d.ts.map
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NeovateExecutor handles execution of neovate commands via the @neovate/code SDK
|
|
3
|
+
*
|
|
4
|
+
* This manager is responsible for:
|
|
5
|
+
* - Executing streaming sessions via the SDK
|
|
6
|
+
* - Streaming result messages back to the caller
|
|
7
|
+
* - Handling execution errors
|
|
8
|
+
*/
|
|
9
|
+
import { createSession } from '@neovate/code';
|
|
10
|
+
import { resolve } from 'path';
|
|
11
|
+
const getErrorCode = (error) => {
|
|
12
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
13
|
+
const code = error.code;
|
|
14
|
+
if (typeof code === 'string') {
|
|
15
|
+
return code;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* NeovateExecutorImpl provides the implementation for neovate command execution
|
|
22
|
+
* using the @neovate/code SDK with streaming sessions.
|
|
23
|
+
*/
|
|
24
|
+
export class NeovateExecutorImpl {
|
|
25
|
+
/**
|
|
26
|
+
* Executes a neovate prompt using the SDK via streaming sessions
|
|
27
|
+
*
|
|
28
|
+
* @param userPrompt - The user's message/prompt to send to neovate
|
|
29
|
+
* @param context - ExecutionContext with threadId, threadPath, model, and attachments
|
|
30
|
+
* @yields NeovateEvent objects for message, result, and error events
|
|
31
|
+
*
|
|
32
|
+
* Requirements:
|
|
33
|
+
* - 5.2 - WHEN the CLI receives a chat message, THE CLI SHALL execute the Neovate command
|
|
34
|
+
* - 5.4 - WHEN the Neovate command executes, THE CLI SHALL stream the output back to the App
|
|
35
|
+
*/
|
|
36
|
+
async *execute(userPrompt, context) {
|
|
37
|
+
let session = null;
|
|
38
|
+
try {
|
|
39
|
+
// Ensure model is always defined
|
|
40
|
+
// The model is passed from the frontend (e.g., "openrouter/minimax/minimax-m2.1")
|
|
41
|
+
// We'll validate and ensure it's properly formatted for the SDK
|
|
42
|
+
let model = context.model?.trim();
|
|
43
|
+
if (!model) {
|
|
44
|
+
console.warn('[NeovateExecutor] No model provided in context, using default');
|
|
45
|
+
model = 'openrouter/minimax/minimax-m2.1';
|
|
46
|
+
}
|
|
47
|
+
const cwd = resolve(context.threadPath);
|
|
48
|
+
console.log('[NeovateExecutor] Execution context:', { model, cwd, promptLength: userPrompt.length });
|
|
49
|
+
// CRITICAL FIX: Ensure model is passed correctly to createSession
|
|
50
|
+
// The model MUST be a non-empty string, not null or undefined
|
|
51
|
+
const sessionConfig = {
|
|
52
|
+
model, // This is the critical parameter
|
|
53
|
+
cwd,
|
|
54
|
+
productName: 'Tarsk.io'
|
|
55
|
+
};
|
|
56
|
+
console.log('[NeovateExecutor] Creating session with model:', model);
|
|
57
|
+
console.log('[NeovateExecutor] Session config has model:', !!sessionConfig.model);
|
|
58
|
+
// Create a streaming session - MUST have model defined
|
|
59
|
+
session = await createSession(sessionConfig);
|
|
60
|
+
if (!session) {
|
|
61
|
+
throw new Error('Failed to create session: createSession returned falsy value');
|
|
62
|
+
}
|
|
63
|
+
console.log('[NeovateExecutor] Session created:', session.sessionId);
|
|
64
|
+
// Send the user prompt to the session
|
|
65
|
+
console.log('[NeovateExecutor] Sending prompt to session...');
|
|
66
|
+
await session.send(userPrompt);
|
|
67
|
+
console.log('[NeovateExecutor] Prompt sent, waiting for responses...');
|
|
68
|
+
// Stream messages from the session
|
|
69
|
+
for await (const msg of session.receive()) {
|
|
70
|
+
const contentLength = typeof msg?.content === 'string' ? msg.content.length : undefined;
|
|
71
|
+
console.log('[NeovateExecutor] Received msg:', { type: msg?.type, contentLength });
|
|
72
|
+
if (msg?.type === 'message') {
|
|
73
|
+
// Yield conversation messages (assistant responses)
|
|
74
|
+
yield {
|
|
75
|
+
type: 'message',
|
|
76
|
+
role: msg.role || 'assistant',
|
|
77
|
+
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
else if (msg?.type === 'result') {
|
|
81
|
+
// Yield the final result
|
|
82
|
+
yield {
|
|
83
|
+
type: 'result',
|
|
84
|
+
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
|
|
85
|
+
};
|
|
86
|
+
break; // Exit the loop after receiving the result
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.error('[NeovateExecutor] Error during execution:', error);
|
|
92
|
+
let errCode;
|
|
93
|
+
let errMessage;
|
|
94
|
+
let errStack;
|
|
95
|
+
let errDetails;
|
|
96
|
+
if (error instanceof Error) {
|
|
97
|
+
errMessage = error.message || 'Unknown error';
|
|
98
|
+
errStack = error.stack;
|
|
99
|
+
errCode = getErrorCode(error);
|
|
100
|
+
}
|
|
101
|
+
else if (typeof error === 'object' && error !== null) {
|
|
102
|
+
errMessage = error.message
|
|
103
|
+
? String(error.message)
|
|
104
|
+
: JSON.stringify(error);
|
|
105
|
+
errCode = getErrorCode(error);
|
|
106
|
+
errDetails = error;
|
|
107
|
+
}
|
|
108
|
+
else if (typeof error === 'string') {
|
|
109
|
+
errMessage = error;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
errMessage = String(error);
|
|
113
|
+
}
|
|
114
|
+
console.error('[NeovateExecutor] Error details:', { errCode, errMessage, errStack });
|
|
115
|
+
yield {
|
|
116
|
+
type: 'error',
|
|
117
|
+
error: {
|
|
118
|
+
code: errCode || 'EXECUTION_ERROR',
|
|
119
|
+
message: errMessage || 'An error occurred during execution',
|
|
120
|
+
details: { stack: errStack, originalError: errDetails }
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
// Ensure the session is properly closed
|
|
126
|
+
if (session) {
|
|
127
|
+
try {
|
|
128
|
+
await session.close?.();
|
|
129
|
+
console.log('[NeovateExecutor] Session closed');
|
|
130
|
+
}
|
|
131
|
+
catch (closeError) {
|
|
132
|
+
console.error('[NeovateExecutor] Error closing session:', closeError);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=NeovateExecutor.js.map
|