tarsk 0.3.3 → 0.3.16

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 (43) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.js +22 -0
  3. package/dist/index.js +3 -2
  4. package/dist/public/assets/{index-DJC-p914.js → index-CLr9LKtA.js} +1679 -1682
  5. package/dist/public/index.html +1 -1
  6. package/node_modules/@neovate/code/LICENSE +21 -0
  7. package/node_modules/@neovate/code/README.md +56 -0
  8. package/node_modules/@neovate/code/dist/cli.mjs +714 -716
  9. package/node_modules/@neovate/code/dist/index.d.ts +0 -373
  10. package/node_modules/@neovate/code/dist/index.mjs +790 -792
  11. package/node_modules/@neovate/code/package.json +138 -2
  12. package/node_modules/@neovate/code/vendor/ripgrep/COPYING +3 -0
  13. package/node_modules/@neovate/code/vendor/ripgrep/arm64-darwin/rg +0 -0
  14. package/node_modules/@neovate/code/vendor/ripgrep/arm64-linux/rg +0 -0
  15. package/node_modules/@neovate/code/vendor/ripgrep/x64-darwin/rg +0 -0
  16. package/node_modules/@neovate/code/vendor/ripgrep/x64-linux/rg +0 -0
  17. package/node_modules/@neovate/code/vendor/ripgrep/x64-win32/rg.exe +0 -0
  18. package/package.json +2 -2
  19. package/dist/managers/ConversationManager.d.ts +0 -83
  20. package/dist/managers/ConversationManager.js +0 -129
  21. package/dist/managers/GitManager.d.ts +0 -133
  22. package/dist/managers/GitManager.js +0 -330
  23. package/dist/managers/MetadataManager.d.ts +0 -139
  24. package/dist/managers/MetadataManager.js +0 -309
  25. package/dist/managers/ModelManager.d.ts +0 -57
  26. package/dist/managers/ModelManager.js +0 -129
  27. package/dist/managers/NeovateExecutor.d.ts +0 -40
  28. package/dist/managers/NeovateExecutor.js +0 -138
  29. package/dist/managers/ProjectManager.d.ts +0 -162
  30. package/dist/managers/ProjectManager.js +0 -353
  31. package/dist/managers/ThreadManager.d.ts +0 -181
  32. package/dist/managers/ThreadManager.js +0 -325
  33. package/dist/model-info-openai.d.ts +0 -17
  34. package/dist/model-info-openai.js +0 -59
  35. package/dist/public/assets/index-B443aj9k.js +0 -8506
  36. package/dist/routes/chat-old.d.ts +0 -21
  37. package/dist/routes/chat-old.js +0 -251
  38. package/dist/routes/projects-old.d.ts +0 -20
  39. package/dist/routes/projects-old.js +0 -297
  40. package/dist/routes/threads-old.d.ts +0 -14
  41. package/dist/routes/threads-old.js +0 -393
  42. package/dist/utils/openai-pricing-scraper.d.ts +0 -17
  43. package/dist/utils/openai-pricing-scraper.js +0 -185
@@ -1,393 +0,0 @@
1
- /**
2
- * Thread routes for the REST API
3
- *
4
- * Handles all thread-related operations:
5
- * - POST /api/threads - Create a new thread
6
- * - GET /api/threads?projectId=xxx - List threads for a project
7
- * - DELETE /api/threads/:id - Delete a thread
8
- */
9
- import { Hono } from 'hono';
10
- import { validateThreadName } from '../utils/validation.js';
11
- import { streamAsyncGenerator } from '../lib/stream-helper.js';
12
- const extractTextBlocksFromContent = (content) => {
13
- const trimmed = content.trim();
14
- if (!trimmed.startsWith('[') && !trimmed.startsWith('{')) {
15
- return [];
16
- }
17
- try {
18
- const parsed = JSON.parse(trimmed);
19
- if (Array.isArray(parsed)) {
20
- return parsed
21
- .filter((block) => block && typeof block === 'object' && block.type === 'text')
22
- .map((block) => (typeof block.text === 'string' ? block.text : ''))
23
- .filter((text) => text.length > 0);
24
- }
25
- if (parsed && typeof parsed === 'object' && typeof parsed.text === 'string') {
26
- return [parsed.text];
27
- }
28
- }
29
- catch {
30
- return [];
31
- }
32
- return [];
33
- };
34
- const extractAssistantContent = (events, fallback) => {
35
- if (!events || events.length === 0) {
36
- return fallback;
37
- }
38
- const resultEvent = [...events].reverse().find((event) => event.type === 'result');
39
- if (resultEvent && typeof resultEvent.content === 'string' && resultEvent.content.trim().length > 0) {
40
- return resultEvent.content;
41
- }
42
- const assistantMessages = events.filter((event) => event.type === 'message' && event.role === 'assistant');
43
- let lastTextBlock = null;
44
- const assistantChunks = [];
45
- for (const event of assistantMessages) {
46
- if (typeof event.content !== 'string') {
47
- continue;
48
- }
49
- assistantChunks.push(event.content);
50
- const textBlocks = extractTextBlocksFromContent(event.content);
51
- if (textBlocks.length > 0) {
52
- lastTextBlock = textBlocks[textBlocks.length - 1];
53
- }
54
- }
55
- if (lastTextBlock) {
56
- return lastTextBlock;
57
- }
58
- if (assistantChunks.length === 0) {
59
- return fallback;
60
- }
61
- const combined = assistantChunks.join('');
62
- return combined.trim().length > 0 ? combined : fallback;
63
- };
64
- export function createThreadRoutes(threadManager, gitManager, conversationManager) {
65
- const router = new Hono();
66
- /**
67
- * POST /api/threads
68
- * Create a new thread for a project
69
- *
70
- * Request body:
71
- * {
72
- * "projectId": "uuid",
73
- * "title": "Optional thread title"
74
- * }
75
- *
76
- * Response: Newline-delimited JSON stream of ThreadEvent objects
77
- *
78
- * Requirements:
79
- * - 6.3 - THE CLI SHALL expose REST API endpoints for Thread operations
80
- * - 8.3 - WHEN creating a Thread, THE App SHALL send a POST request with the Project identifier to the CLI
81
- * - 8.4 - THE CLI SHALL support streaming responses to the client
82
- */
83
- router.post('/', async (c) => {
84
- try {
85
- const body = await c.req.json();
86
- const { projectId, title } = body;
87
- // Validate projectId is provided
88
- if (!projectId || typeof projectId !== 'string') {
89
- const errorResponse = {
90
- error: {
91
- code: 'INVALID_REQUEST',
92
- message: 'projectId is required and must be a string',
93
- timestamp: new Date().toISOString()
94
- }
95
- };
96
- return c.json(errorResponse, 400);
97
- }
98
- // Validate title if provided
99
- if (title !== undefined && typeof title !== 'string') {
100
- const errorResponse = {
101
- error: {
102
- code: 'INVALID_REQUEST',
103
- message: 'title must be a string if provided',
104
- timestamp: new Date().toISOString()
105
- }
106
- };
107
- return c.json(errorResponse, 400);
108
- }
109
- // Validate thread name (empty/whitespace is allowed, will auto-generate)
110
- if (title && title.trim() !== '') {
111
- const nameError = validateThreadName(title);
112
- if (nameError) {
113
- const errorResponse = {
114
- error: {
115
- code: 'INVALID_REQUEST',
116
- message: nameError,
117
- timestamp: new Date().toISOString()
118
- }
119
- };
120
- return c.json(errorResponse, 400);
121
- }
122
- // Check for duplicate branch names
123
- const existingThreads = await threadManager.listThreads(projectId);
124
- const sanitizedNewName = gitManager.sanitizeBranchName(title);
125
- const existingSanitized = existingThreads.map(t => gitManager.sanitizeBranchName(t.title));
126
- if (existingSanitized.includes(sanitizedNewName)) {
127
- const errorResponse = {
128
- error: {
129
- code: 'DUPLICATE_BRANCH',
130
- message: `A thread with branch name "${sanitizedNewName}" already exists`,
131
- timestamp: new Date().toISOString()
132
- }
133
- };
134
- return c.json(errorResponse, 409);
135
- }
136
- }
137
- // Stream the thread creation events
138
- return streamAsyncGenerator(c, threadManager.createThread(projectId, title));
139
- }
140
- catch (error) {
141
- const errorResponse = {
142
- error: {
143
- code: 'REQUEST_PARSE_ERROR',
144
- message: 'Failed to parse request body',
145
- details: error instanceof Error ? error.message : String(error),
146
- timestamp: new Date().toISOString()
147
- }
148
- };
149
- return c.json(errorResponse, 400);
150
- }
151
- });
152
- /**
153
- * GET /api/threads?projectId=xxx
154
- * List all threads for a specific project
155
- *
156
- * Query parameters:
157
- * - projectId (required): The project ID to filter threads
158
- *
159
- * Response:
160
- * [
161
- * {
162
- * "id": "uuid",
163
- * "projectId": "uuid",
164
- * "title": "Thread title",
165
- * "path": "/path/to/thread",
166
- * "currentBranch": "branch-name",
167
- * "createdAt": "2024-01-01T00:00:00Z"
168
- * }
169
- * ]
170
- *
171
- * Requirements:
172
- * - 6.3 - THE CLI SHALL expose REST API endpoints for Thread operations
173
- */
174
- router.get('/', async (c) => {
175
- try {
176
- const projectId = c.req.query('projectId');
177
- // Validate projectId is provided
178
- if (!projectId) {
179
- const errorResponse = {
180
- error: {
181
- code: 'INVALID_REQUEST',
182
- message: 'projectId query parameter is required',
183
- timestamp: new Date().toISOString()
184
- }
185
- };
186
- return c.json(errorResponse, 400);
187
- }
188
- const threads = await threadManager.listThreads(projectId);
189
- return c.json(threads);
190
- }
191
- catch (error) {
192
- const errorResponse = {
193
- error: {
194
- code: 'LIST_THREADS_ERROR',
195
- message: 'Failed to list threads',
196
- details: error instanceof Error ? error.message : String(error),
197
- timestamp: new Date().toISOString()
198
- }
199
- };
200
- return c.json(errorResponse, 500);
201
- }
202
- });
203
- /**
204
- * DELETE /api/threads/:id
205
- * Delete a thread
206
- *
207
- * Response:
208
- * {
209
- * "success": true,
210
- * "message": "Thread deleted successfully"
211
- * }
212
- *
213
- * Requirements:
214
- * - 6.3 - THE CLI SHALL expose REST API endpoints for Thread operations
215
- * - 8.4 - WHEN deleting a Thread, THE App SHALL send a DELETE request with the Thread identifier to the CLI
216
- */
217
- router.delete('/:id', async (c) => {
218
- try {
219
- const threadId = c.req.param('id');
220
- if (!threadId) {
221
- const errorResponse = {
222
- error: {
223
- code: 'INVALID_REQUEST',
224
- message: 'Thread ID is required',
225
- timestamp: new Date().toISOString()
226
- }
227
- };
228
- return c.json(errorResponse, 400);
229
- }
230
- await threadManager.deleteThread(threadId);
231
- return c.json({
232
- success: true,
233
- message: 'Thread deleted successfully'
234
- });
235
- }
236
- catch (error) {
237
- const errorMessage = error instanceof Error ? error.message : String(error);
238
- // Check if it's a "not found" error
239
- if (errorMessage.includes('not found')) {
240
- const errorResponse = {
241
- error: {
242
- code: 'THREAD_NOT_FOUND',
243
- message: errorMessage,
244
- timestamp: new Date().toISOString()
245
- }
246
- };
247
- return c.json(errorResponse, 404);
248
- }
249
- const errorResponse = {
250
- error: {
251
- code: 'DELETE_THREAD_ERROR',
252
- message: 'Failed to delete thread',
253
- details: errorMessage,
254
- timestamp: new Date().toISOString()
255
- }
256
- };
257
- return c.json(errorResponse, 500);
258
- }
259
- });
260
- /**
261
- * POST /api/threads/:id/select
262
- * Select a thread as the active thread
263
- *
264
- * Response:
265
- * {
266
- * "success": true,
267
- * "message": "Thread selected successfully",
268
- * "threadId": "uuid"
269
- * }
270
- *
271
- * Requirements:
272
- * - 6.3 - THE CLI SHALL expose REST API endpoints for Thread operations
273
- */
274
- router.post('/:id/select', async (c) => {
275
- try {
276
- const threadId = c.req.param('id');
277
- if (!threadId) {
278
- const errorResponse = {
279
- error: {
280
- code: 'INVALID_REQUEST',
281
- message: 'Thread ID is required',
282
- timestamp: new Date().toISOString()
283
- }
284
- };
285
- return c.json(errorResponse, 400);
286
- }
287
- // Verify thread exists
288
- const thread = await threadManager.getThread(threadId);
289
- if (!thread) {
290
- const errorResponse = {
291
- error: {
292
- code: 'THREAD_NOT_FOUND',
293
- message: `Thread not found: ${threadId}`,
294
- timestamp: new Date().toISOString()
295
- }
296
- };
297
- return c.json(errorResponse, 404);
298
- }
299
- await threadManager.selectThread(threadId);
300
- return c.json({
301
- success: true,
302
- message: 'Thread selected successfully',
303
- threadId
304
- });
305
- }
306
- catch (error) {
307
- const errorResponse = {
308
- error: {
309
- code: 'SELECT_THREAD_ERROR',
310
- message: 'Failed to select thread',
311
- details: error instanceof Error ? error.message : String(error),
312
- timestamp: new Date().toISOString()
313
- }
314
- };
315
- return c.json(errorResponse, 500);
316
- }
317
- });
318
- /**
319
- * GET /api/threads/:id/messages
320
- * Get chat history for a thread
321
- */
322
- router.get('/:id/messages', async (c) => {
323
- try {
324
- const threadId = c.req.param('id');
325
- if (!threadId) {
326
- const errorResponse = {
327
- error: {
328
- code: 'INVALID_REQUEST',
329
- message: 'Thread ID is required',
330
- timestamp: new Date().toISOString()
331
- }
332
- };
333
- return c.json(errorResponse, 400);
334
- }
335
- const thread = await threadManager.getThread(threadId);
336
- if (!thread) {
337
- const errorResponse = {
338
- error: {
339
- code: 'THREAD_NOT_FOUND',
340
- message: `Thread not found: ${threadId}`,
341
- timestamp: new Date().toISOString()
342
- }
343
- };
344
- return c.json(errorResponse, 404);
345
- }
346
- const history = await conversationManager.getConversationHistory(thread.path);
347
- if (!history || history.messages.length === 0) {
348
- return c.json([]);
349
- }
350
- const messages = history.messages.flatMap((message) => {
351
- const userMessage = {
352
- messageId: `user-${message.id}`,
353
- sender: 'user',
354
- content: message.request.message,
355
- contentType: 'text',
356
- timestamp: message.timestamp,
357
- model: message.request.model,
358
- attachments: message.request.attachments?.map((attachment) => ({
359
- fileName: attachment.name,
360
- content: attachment.content,
361
- size: attachment.size,
362
- }))
363
- };
364
- if (!message.response) {
365
- return [userMessage];
366
- }
367
- const agentMessage = {
368
- messageId: `agent-${message.id}`,
369
- sender: 'agent',
370
- content: extractAssistantContent(message.response.events, message.response.content),
371
- contentType: 'markdown',
372
- timestamp: message.response.completedAt,
373
- model: message.request.model,
374
- };
375
- return [userMessage, agentMessage];
376
- });
377
- return c.json(messages);
378
- }
379
- catch (error) {
380
- const errorResponse = {
381
- error: {
382
- code: 'GET_THREAD_MESSAGES_ERROR',
383
- message: 'Failed to load thread messages',
384
- details: error instanceof Error ? error.message : String(error),
385
- timestamp: new Date().toISOString()
386
- }
387
- };
388
- return c.json(errorResponse, 500);
389
- }
390
- });
391
- return router;
392
- }
393
- //# sourceMappingURL=threads-old.js.map
@@ -1,17 +0,0 @@
1
- /**
2
- * OpenAI Pricing Scraper
3
- *
4
- * Scrapes pricing information from OpenAI's pricing page and caches it locally for 24 hours.
5
- * This is necessary because the OpenAI API doesn't return pricing in the models list.
6
- */
7
- import type { ModelInfoPricing } from '../model-info.js';
8
- /**
9
- * Get OpenAI pricing with caching.
10
- * Returns cached pricing if available and fresh, otherwise attempts to scrape and cache new data.
11
- * Falls back to hardcoded pricing if scraping fails (due to Cloudflare or other blocks).
12
- *
13
- * @param cacheDir - Directory to store cache file (typically .metadata)
14
- * @returns Map of model name -> ModelInfoPricing
15
- */
16
- export declare function getOpenAIPricing(cacheDir: string): Promise<Map<string, ModelInfoPricing>>;
17
- //# sourceMappingURL=openai-pricing-scraper.d.ts.map
@@ -1,185 +0,0 @@
1
- /**
2
- * OpenAI Pricing Scraper
3
- *
4
- * Scrapes pricing information from OpenAI's pricing page and caches it locally for 24 hours.
5
- * This is necessary because the OpenAI API doesn't return pricing in the models list.
6
- */
7
- import { promises as fs } from 'fs';
8
- import { join } from 'path';
9
- const PRICING_URL = 'https://openai.com/api/pricing';
10
- const CACHE_DURATION_MS = 24 * 60 * 60 * 1000; // 24 hours
11
- // Fallback pricing data in case scraping fails or Cloudflare blocks access
12
- // Source: OpenAI's public pricing page (manually curated)
13
- // Updated: February 2026
14
- const FALLBACK_PRICING = {
15
- 'gpt-4-turbo': { input: 0.000010, output: 0.000030 },
16
- 'gpt-4': { input: 0.000030, output: 0.000060 },
17
- 'gpt-4.1': { input: 0.000003, output: 0.000012 },
18
- 'gpt-4o': { input: 0.000005, output: 0.000015 },
19
- 'gpt-4o-mini': { input: 0.00000015, output: 0.0000006 },
20
- 'gpt-3.5-turbo': { input: 0.0000005, output: 0.0000015 },
21
- 'o1': { input: 0.000015, output: 0.000060 },
22
- 'o1-mini': { input: 0.000003, output: 0.000012 },
23
- };
24
- /** Strip HTML tags and normalize whitespace */
25
- function stripTags(html) {
26
- return html.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim();
27
- }
28
- /** Parse price from text (e.g., "$0.005 / 1M tokens" -> 0.000005) */
29
- function parsePrice(text) {
30
- if (!text)
31
- return undefined;
32
- const match = text.match(/[\d.]+/);
33
- if (!match)
34
- return undefined;
35
- const price = Number(match[0]);
36
- // Convert from price per 1M tokens to price per token
37
- return price / 1_000_000;
38
- }
39
- /** Fetch and parse pricing from OpenAI's pricing page */
40
- async function scrapePricingFromWeb() {
41
- console.log('[OpenAI Pricing] Fetching pricing from', PRICING_URL);
42
- const response = await fetch(PRICING_URL, {
43
- headers: {
44
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
45
- },
46
- });
47
- if (!response.ok) {
48
- throw new Error(`Failed to fetch pricing page: ${response.status}`);
49
- }
50
- const html = await response.text();
51
- const pricing = {
52
- currency: 'USD',
53
- unit: 'per_token',
54
- last_updated: new Date().toISOString(),
55
- text_models: {},
56
- };
57
- // Extract all tables
58
- const tables = [...html.matchAll(/<table[\s\S]*?<\/table>/gi)];
59
- console.log(`[OpenAI Pricing] Found ${tables.length} tables`);
60
- for (const tableMatch of tables) {
61
- const table = tableMatch[0];
62
- // Extract rows
63
- const rows = [...table.matchAll(/<tr[\s\S]*?<\/tr>/gi)];
64
- for (const rowMatch of rows) {
65
- const cells = [...rowMatch[0].matchAll(/<t[hd][\s\S]*?<\/t[hd]>/gi)].map((c) => stripTags(c[0]));
66
- if (cells.length < 2)
67
- continue;
68
- const [model, input, cached, output] = cells;
69
- console.log(`[OpenAI Pricing] Parsed row - model: "${model}", input: "${input}", cached: "${cached}", output: "${output}"`);
70
- const inputPrice = parsePrice(input);
71
- const cachedPrice = parsePrice(cached);
72
- const outputPrice = parsePrice(output);
73
- if (!model || (!inputPrice && !cachedPrice && !outputPrice))
74
- continue;
75
- pricing.text_models[model] = {
76
- ...(inputPrice !== undefined && { input: inputPrice }),
77
- ...(cachedPrice !== undefined && { cached_input: cachedPrice }),
78
- ...(outputPrice !== undefined && { output: outputPrice }),
79
- };
80
- if (Object.keys(pricing.text_models).length % 5 === 0) {
81
- console.log(`[OpenAI Pricing] Parsed ${Object.keys(pricing.text_models).length} models...`);
82
- }
83
- }
84
- }
85
- console.log(`[OpenAI Pricing] Successfully scraped ${Object.keys(pricing.text_models).length} models`);
86
- return pricing;
87
- }
88
- /** Check if cache file exists and is fresh */
89
- async function isCacheFresh(cacheFile) {
90
- try {
91
- const stats = await fs.stat(cacheFile);
92
- const age = Date.now() - stats.mtime.getTime();
93
- const isFresh = age < CACHE_DURATION_MS;
94
- console.log(`[OpenAI Pricing] Cache age: ${Math.round(age / 1000 / 60)} minutes, fresh: ${isFresh}`);
95
- return isFresh;
96
- }
97
- catch {
98
- console.log('[OpenAI Pricing] No cache file found');
99
- return false;
100
- }
101
- }
102
- /** Read cached pricing from file */
103
- async function readCachedPricing(cacheFile) {
104
- const content = await fs.readFile(cacheFile, 'utf-8');
105
- return JSON.parse(content);
106
- }
107
- /** Write pricing cache to file */
108
- async function writeCachedPricing(cacheFile, pricing) {
109
- const dir = cacheFile.substring(0, cacheFile.lastIndexOf('/'));
110
- await fs.mkdir(dir, { recursive: true });
111
- await fs.writeFile(cacheFile, JSON.stringify(pricing, null, 2));
112
- }
113
- /**
114
- * Get OpenAI pricing with caching.
115
- * Returns cached pricing if available and fresh, otherwise attempts to scrape and cache new data.
116
- * Falls back to hardcoded pricing if scraping fails (due to Cloudflare or other blocks).
117
- *
118
- * @param cacheDir - Directory to store cache file (typically .metadata)
119
- * @returns Map of model name -> ModelInfoPricing
120
- */
121
- export async function getOpenAIPricing(cacheDir) {
122
- const cacheFile = join(cacheDir, 'openai-pricing.json');
123
- const result = new Map();
124
- try {
125
- let pricing = null;
126
- // Try to use cache if it's fresh
127
- if (await isCacheFresh(cacheFile)) {
128
- try {
129
- pricing = await readCachedPricing(cacheFile);
130
- console.log('[OpenAI Pricing] Using cached pricing');
131
- }
132
- catch (error) {
133
- console.error('[OpenAI Pricing] Error reading cache:', error);
134
- pricing = null;
135
- }
136
- }
137
- // Scrape if we don't have fresh cache
138
- if (!pricing) {
139
- try {
140
- pricing = await scrapePricingFromWeb();
141
- try {
142
- await writeCachedPricing(cacheFile, pricing);
143
- console.log('[OpenAI Pricing] Cached pricing to', cacheFile);
144
- }
145
- catch (error) {
146
- console.error('[OpenAI Pricing] Error writing cache:', error);
147
- }
148
- }
149
- catch (error) {
150
- console.error('[OpenAI Pricing] Error scraping pricing, using fallback data:', error);
151
- // Use fallback pricing
152
- for (const [model, prices] of Object.entries(FALLBACK_PRICING)) {
153
- result.set(model, {
154
- prompt: prices.input,
155
- completion: prices.output,
156
- });
157
- }
158
- console.log(`[OpenAI Pricing] Using fallback pricing for ${result.size} models`);
159
- return result;
160
- }
161
- }
162
- // Convert to ModelInfoPricing format
163
- for (const [model, prices] of Object.entries(pricing.text_models)) {
164
- if (prices.input !== undefined && prices.output !== undefined) {
165
- result.set(model, {
166
- prompt: prices.input,
167
- completion: prices.output,
168
- });
169
- }
170
- }
171
- console.log(`[OpenAI Pricing] Loaded pricing for ${result.size} models`);
172
- }
173
- catch (error) {
174
- console.error('[OpenAI Pricing] Unexpected error getting pricing, using fallback:', error);
175
- // Use fallback pricing as last resort
176
- for (const [model, prices] of Object.entries(FALLBACK_PRICING)) {
177
- result.set(model, {
178
- prompt: prices.input,
179
- completion: prices.output,
180
- });
181
- }
182
- }
183
- return result;
184
- }
185
- //# sourceMappingURL=openai-pricing-scraper.js.map