veryfront 0.0.4 → 0.0.6
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 +168 -2
- package/bin/veryfront.js +7 -0
- package/dist/ai/client.js +7 -0
- package/dist/ai/client.js.map +7 -0
- package/dist/ai/components.js +713 -0
- package/dist/ai/components.js.map +7 -0
- package/dist/ai/dev.js +839 -0
- package/dist/ai/dev.js.map +7 -0
- package/dist/ai/index.js +7398 -0
- package/dist/ai/index.js.map +7 -0
- package/dist/ai/primitives.js +314 -0
- package/dist/ai/primitives.js.map +7 -0
- package/dist/ai/production.js +934 -0
- package/dist/ai/production.js.map +7 -0
- package/dist/ai/react.js +455 -0
- package/dist/ai/react.js.map +7 -0
- package/dist/cli.js +38365 -0
- package/dist/components.js +9403 -0
- package/dist/components.js.map +7 -0
- package/dist/config.js +654 -0
- package/dist/config.js.map +7 -0
- package/dist/data.js +1131 -0
- package/dist/data.js.map +7 -0
- package/dist/index.js +11441 -0
- package/dist/index.js.map +7 -0
- package/package.json +95 -30
- package/bin/veryfront +0 -0
- package/scripts/postinstall.js +0 -109
|
@@ -0,0 +1,934 @@
|
|
|
1
|
+
// src/core/errors/veryfront-error.ts
|
|
2
|
+
function createError(error) {
|
|
3
|
+
return error;
|
|
4
|
+
}
|
|
5
|
+
function toError(veryfrontError) {
|
|
6
|
+
const error = new Error(veryfrontError.message);
|
|
7
|
+
error.name = `VeryfrontError[${veryfrontError.type}]`;
|
|
8
|
+
Object.defineProperty(error, "context", {
|
|
9
|
+
value: veryfrontError,
|
|
10
|
+
enumerable: false,
|
|
11
|
+
configurable: true
|
|
12
|
+
});
|
|
13
|
+
return error;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/ai/production/rate-limit/limiter.ts
|
|
17
|
+
var FixedWindowLimiter = class {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.requests = /* @__PURE__ */ new Map();
|
|
20
|
+
this.config = config;
|
|
21
|
+
}
|
|
22
|
+
check(identifier) {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const entry = this.requests.get(identifier);
|
|
25
|
+
if (!entry || now >= entry.resetAt) {
|
|
26
|
+
const resetAt = now + this.config.windowMs;
|
|
27
|
+
this.requests.set(identifier, {
|
|
28
|
+
count: 1,
|
|
29
|
+
resetAt
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
allowed: true,
|
|
33
|
+
remaining: this.config.maxRequests - 1,
|
|
34
|
+
resetAt
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (entry.count < this.config.maxRequests) {
|
|
38
|
+
entry.count++;
|
|
39
|
+
return {
|
|
40
|
+
allowed: true,
|
|
41
|
+
remaining: this.config.maxRequests - entry.count,
|
|
42
|
+
resetAt: entry.resetAt
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
allowed: false,
|
|
47
|
+
remaining: 0,
|
|
48
|
+
resetAt: entry.resetAt,
|
|
49
|
+
retryAfter: Math.ceil((entry.resetAt - now) / 1e3)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
reset(identifier) {
|
|
53
|
+
this.requests.delete(identifier);
|
|
54
|
+
}
|
|
55
|
+
clear() {
|
|
56
|
+
this.requests.clear();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var TokenBucketLimiter = class {
|
|
60
|
+
constructor(config) {
|
|
61
|
+
this.buckets = /* @__PURE__ */ new Map();
|
|
62
|
+
this.config = config;
|
|
63
|
+
this.refillRate = config.maxRequests / config.windowMs;
|
|
64
|
+
}
|
|
65
|
+
check(identifier) {
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
let bucket = this.buckets.get(identifier);
|
|
68
|
+
if (!bucket) {
|
|
69
|
+
bucket = {
|
|
70
|
+
tokens: this.config.maxRequests - 1,
|
|
71
|
+
lastRefill: now
|
|
72
|
+
};
|
|
73
|
+
this.buckets.set(identifier, bucket);
|
|
74
|
+
return {
|
|
75
|
+
allowed: true,
|
|
76
|
+
remaining: bucket.tokens,
|
|
77
|
+
resetAt: now + this.config.windowMs
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const timePassed = now - bucket.lastRefill;
|
|
81
|
+
const tokensToAdd = timePassed * this.refillRate;
|
|
82
|
+
bucket.tokens = Math.min(
|
|
83
|
+
this.config.maxRequests,
|
|
84
|
+
bucket.tokens + tokensToAdd
|
|
85
|
+
);
|
|
86
|
+
bucket.lastRefill = now;
|
|
87
|
+
if (bucket.tokens >= 1) {
|
|
88
|
+
bucket.tokens--;
|
|
89
|
+
return {
|
|
90
|
+
allowed: true,
|
|
91
|
+
remaining: Math.floor(bucket.tokens),
|
|
92
|
+
resetAt: now + this.config.windowMs
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const timeUntilToken = (1 - bucket.tokens) / this.refillRate;
|
|
96
|
+
return {
|
|
97
|
+
allowed: false,
|
|
98
|
+
remaining: 0,
|
|
99
|
+
resetAt: now + this.config.windowMs,
|
|
100
|
+
retryAfter: Math.ceil(timeUntilToken / 1e3)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
reset(identifier) {
|
|
104
|
+
this.buckets.delete(identifier);
|
|
105
|
+
}
|
|
106
|
+
clear() {
|
|
107
|
+
this.buckets.clear();
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
function createRateLimiter(config) {
|
|
111
|
+
let limiter;
|
|
112
|
+
switch (config.strategy) {
|
|
113
|
+
case "fixed-window":
|
|
114
|
+
limiter = new FixedWindowLimiter(config);
|
|
115
|
+
break;
|
|
116
|
+
case "token-bucket":
|
|
117
|
+
limiter = new TokenBucketLimiter(config);
|
|
118
|
+
break;
|
|
119
|
+
case "sliding-window":
|
|
120
|
+
limiter = new TokenBucketLimiter(config);
|
|
121
|
+
break;
|
|
122
|
+
default:
|
|
123
|
+
limiter = new FixedWindowLimiter(config);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
/**
|
|
127
|
+
* Check if request is allowed
|
|
128
|
+
*/
|
|
129
|
+
check(context) {
|
|
130
|
+
const identifier = config.identify ? config.identify(context) : "default";
|
|
131
|
+
return limiter.check(identifier);
|
|
132
|
+
},
|
|
133
|
+
/**
|
|
134
|
+
* Reset rate limit for identifier
|
|
135
|
+
*/
|
|
136
|
+
reset(context) {
|
|
137
|
+
const identifier = config.identify ? config.identify(context) : "default";
|
|
138
|
+
limiter.reset(identifier);
|
|
139
|
+
},
|
|
140
|
+
/**
|
|
141
|
+
* Clear all rate limits
|
|
142
|
+
*/
|
|
143
|
+
clear() {
|
|
144
|
+
limiter.clear();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function rateLimitMiddleware(config) {
|
|
149
|
+
const limiter = createRateLimiter(config);
|
|
150
|
+
return (context, next) => {
|
|
151
|
+
const result = limiter.check(context);
|
|
152
|
+
if (!result.allowed) {
|
|
153
|
+
throw toError(createError({
|
|
154
|
+
type: "agent",
|
|
155
|
+
message: config.errorMessage || `Rate limit exceeded. Try again in ${result.retryAfter} seconds.`
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
return next();
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/ai/production/cache/cache.ts
|
|
163
|
+
var MemoryCache = class {
|
|
164
|
+
constructor() {
|
|
165
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
166
|
+
}
|
|
167
|
+
set(key, response) {
|
|
168
|
+
this.cache.set(key, {
|
|
169
|
+
response,
|
|
170
|
+
cachedAt: Date.now(),
|
|
171
|
+
accessCount: 0,
|
|
172
|
+
lastAccessedAt: Date.now()
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
get(key) {
|
|
176
|
+
const entry = this.cache.get(key);
|
|
177
|
+
if (!entry)
|
|
178
|
+
return null;
|
|
179
|
+
entry.accessCount++;
|
|
180
|
+
entry.lastAccessedAt = Date.now();
|
|
181
|
+
return entry.response;
|
|
182
|
+
}
|
|
183
|
+
has(key) {
|
|
184
|
+
return this.cache.has(key);
|
|
185
|
+
}
|
|
186
|
+
delete(key) {
|
|
187
|
+
this.cache.delete(key);
|
|
188
|
+
}
|
|
189
|
+
clear() {
|
|
190
|
+
this.cache.clear();
|
|
191
|
+
}
|
|
192
|
+
size() {
|
|
193
|
+
return this.cache.size;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
var LRUCache = class {
|
|
197
|
+
constructor(maxSize = 100) {
|
|
198
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
199
|
+
this.maxSize = maxSize;
|
|
200
|
+
}
|
|
201
|
+
set(key, response) {
|
|
202
|
+
if (this.cache.has(key)) {
|
|
203
|
+
this.cache.delete(key);
|
|
204
|
+
}
|
|
205
|
+
if (this.cache.size >= this.maxSize) {
|
|
206
|
+
const firstKey = this.cache.keys().next().value;
|
|
207
|
+
if (firstKey !== void 0) {
|
|
208
|
+
this.cache.delete(firstKey);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
this.cache.set(key, {
|
|
212
|
+
response,
|
|
213
|
+
cachedAt: Date.now(),
|
|
214
|
+
accessCount: 0,
|
|
215
|
+
lastAccessedAt: Date.now()
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
get(key) {
|
|
219
|
+
const entry = this.cache.get(key);
|
|
220
|
+
if (!entry)
|
|
221
|
+
return null;
|
|
222
|
+
this.cache.delete(key);
|
|
223
|
+
entry.accessCount++;
|
|
224
|
+
entry.lastAccessedAt = Date.now();
|
|
225
|
+
this.cache.set(key, entry);
|
|
226
|
+
return entry.response;
|
|
227
|
+
}
|
|
228
|
+
has(key) {
|
|
229
|
+
return this.cache.has(key);
|
|
230
|
+
}
|
|
231
|
+
delete(key) {
|
|
232
|
+
this.cache.delete(key);
|
|
233
|
+
}
|
|
234
|
+
clear() {
|
|
235
|
+
this.cache.clear();
|
|
236
|
+
}
|
|
237
|
+
size() {
|
|
238
|
+
return this.cache.size;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
var TTLCache = class {
|
|
242
|
+
constructor(ttl = 3e5) {
|
|
243
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
244
|
+
this.cleanupInterval = null;
|
|
245
|
+
this.ttl = ttl;
|
|
246
|
+
this.startCleanup();
|
|
247
|
+
}
|
|
248
|
+
set(key, response) {
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
this.cache.set(key, {
|
|
251
|
+
response,
|
|
252
|
+
cachedAt: now,
|
|
253
|
+
expiresAt: now + this.ttl,
|
|
254
|
+
accessCount: 0,
|
|
255
|
+
lastAccessedAt: now
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
get(key) {
|
|
259
|
+
const entry = this.cache.get(key);
|
|
260
|
+
if (!entry)
|
|
261
|
+
return null;
|
|
262
|
+
if (entry.expiresAt && Date.now() >= entry.expiresAt) {
|
|
263
|
+
this.cache.delete(key);
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
entry.accessCount++;
|
|
267
|
+
entry.lastAccessedAt = Date.now();
|
|
268
|
+
return entry.response;
|
|
269
|
+
}
|
|
270
|
+
has(key) {
|
|
271
|
+
const entry = this.cache.get(key);
|
|
272
|
+
if (!entry)
|
|
273
|
+
return false;
|
|
274
|
+
if (entry.expiresAt && Date.now() >= entry.expiresAt) {
|
|
275
|
+
this.cache.delete(key);
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
delete(key) {
|
|
281
|
+
this.cache.delete(key);
|
|
282
|
+
}
|
|
283
|
+
clear() {
|
|
284
|
+
this.cache.clear();
|
|
285
|
+
}
|
|
286
|
+
size() {
|
|
287
|
+
return this.cache.size;
|
|
288
|
+
}
|
|
289
|
+
destroy() {
|
|
290
|
+
if (this.cleanupInterval) {
|
|
291
|
+
clearInterval(this.cleanupInterval);
|
|
292
|
+
this.cleanupInterval = null;
|
|
293
|
+
}
|
|
294
|
+
this.cache.clear();
|
|
295
|
+
}
|
|
296
|
+
startCleanup() {
|
|
297
|
+
this.cleanupInterval = setInterval(() => {
|
|
298
|
+
const now = Date.now();
|
|
299
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
300
|
+
if (entry.expiresAt && now >= entry.expiresAt) {
|
|
301
|
+
this.cache.delete(key);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}, 6e4);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
function createCache(config) {
|
|
308
|
+
let cache;
|
|
309
|
+
switch (config.strategy) {
|
|
310
|
+
case "memory":
|
|
311
|
+
cache = new MemoryCache();
|
|
312
|
+
break;
|
|
313
|
+
case "lru":
|
|
314
|
+
cache = new LRUCache(config.maxSize || 100);
|
|
315
|
+
break;
|
|
316
|
+
case "ttl":
|
|
317
|
+
cache = new TTLCache(config.ttl || 3e5);
|
|
318
|
+
break;
|
|
319
|
+
default:
|
|
320
|
+
cache = new MemoryCache();
|
|
321
|
+
}
|
|
322
|
+
const keyGenerator = config.keyGenerator || ((input) => `cache_${hashString(input)}`);
|
|
323
|
+
return {
|
|
324
|
+
/**
|
|
325
|
+
* Get cached response
|
|
326
|
+
*/
|
|
327
|
+
get(input, context) {
|
|
328
|
+
const key = keyGenerator(input, context);
|
|
329
|
+
return cache.get(key);
|
|
330
|
+
},
|
|
331
|
+
/**
|
|
332
|
+
* Set cached response
|
|
333
|
+
*/
|
|
334
|
+
set(input, response, context) {
|
|
335
|
+
const key = keyGenerator(input, context);
|
|
336
|
+
cache.set(key, response);
|
|
337
|
+
},
|
|
338
|
+
/**
|
|
339
|
+
* Check if cached
|
|
340
|
+
*/
|
|
341
|
+
has(input, context) {
|
|
342
|
+
const key = keyGenerator(input, context);
|
|
343
|
+
return cache.has(key);
|
|
344
|
+
},
|
|
345
|
+
/**
|
|
346
|
+
* Delete cached entry
|
|
347
|
+
*/
|
|
348
|
+
delete(input, context) {
|
|
349
|
+
const key = keyGenerator(input, context);
|
|
350
|
+
cache.delete(key);
|
|
351
|
+
},
|
|
352
|
+
/**
|
|
353
|
+
* Clear all cache
|
|
354
|
+
*/
|
|
355
|
+
clear() {
|
|
356
|
+
cache.clear();
|
|
357
|
+
},
|
|
358
|
+
/**
|
|
359
|
+
* Get cache size
|
|
360
|
+
*/
|
|
361
|
+
size() {
|
|
362
|
+
return cache.size();
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function hashString(str) {
|
|
367
|
+
let hash = 0;
|
|
368
|
+
for (let i = 0; i < str.length; i++) {
|
|
369
|
+
const char = str.charCodeAt(i);
|
|
370
|
+
hash = (hash << 5) - hash + char;
|
|
371
|
+
hash = hash & hash;
|
|
372
|
+
}
|
|
373
|
+
return Math.abs(hash).toString(36);
|
|
374
|
+
}
|
|
375
|
+
function cacheMiddleware(config) {
|
|
376
|
+
const cache = createCache(config);
|
|
377
|
+
return async (context, next) => {
|
|
378
|
+
const inputString = typeof context.input === "string" ? context.input : JSON.stringify(context.input);
|
|
379
|
+
const cached = cache.get(inputString, context);
|
|
380
|
+
if (cached) {
|
|
381
|
+
return {
|
|
382
|
+
...cached,
|
|
383
|
+
metadata: {
|
|
384
|
+
...cached.metadata,
|
|
385
|
+
fromCache: true,
|
|
386
|
+
cachedAt: Date.now()
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
const result = await next();
|
|
391
|
+
cache.set(inputString, result, context);
|
|
392
|
+
return result;
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/core/utils/runtime-guards.ts
|
|
397
|
+
function hasDenoRuntime(global) {
|
|
398
|
+
return typeof global === "object" && global !== null && "Deno" in global && typeof global.Deno?.env?.get === "function";
|
|
399
|
+
}
|
|
400
|
+
function hasNodeProcess(global) {
|
|
401
|
+
return typeof global === "object" && global !== null && "process" in global && typeof global.process?.env === "object";
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/core/utils/logger/env.ts
|
|
405
|
+
function getEnvironmentVariable(name) {
|
|
406
|
+
try {
|
|
407
|
+
if (typeof Deno !== "undefined" && hasDenoRuntime(globalThis)) {
|
|
408
|
+
const value = globalThis.Deno?.env.get(name);
|
|
409
|
+
return value === "" ? void 0 : value;
|
|
410
|
+
}
|
|
411
|
+
if (hasNodeProcess(globalThis)) {
|
|
412
|
+
const value = globalThis.process?.env[name];
|
|
413
|
+
return value === "" ? void 0 : value;
|
|
414
|
+
}
|
|
415
|
+
} catch {
|
|
416
|
+
return void 0;
|
|
417
|
+
}
|
|
418
|
+
return void 0;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/core/utils/logger/logger.ts
|
|
422
|
+
var cachedLogLevel;
|
|
423
|
+
function resolveLogLevel(force = false) {
|
|
424
|
+
if (force || cachedLogLevel === void 0) {
|
|
425
|
+
cachedLogLevel = getDefaultLevel();
|
|
426
|
+
}
|
|
427
|
+
return cachedLogLevel;
|
|
428
|
+
}
|
|
429
|
+
var ConsoleLogger = class {
|
|
430
|
+
constructor(prefix, level = resolveLogLevel()) {
|
|
431
|
+
this.prefix = prefix;
|
|
432
|
+
this.level = level;
|
|
433
|
+
}
|
|
434
|
+
setLevel(level) {
|
|
435
|
+
this.level = level;
|
|
436
|
+
}
|
|
437
|
+
getLevel() {
|
|
438
|
+
return this.level;
|
|
439
|
+
}
|
|
440
|
+
debug(message, ...args) {
|
|
441
|
+
if (this.level <= 0 /* DEBUG */) {
|
|
442
|
+
console.debug(`[${this.prefix}] DEBUG: ${message}`, ...args);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
info(message, ...args) {
|
|
446
|
+
if (this.level <= 1 /* INFO */) {
|
|
447
|
+
console.log(`[${this.prefix}] ${message}`, ...args);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
warn(message, ...args) {
|
|
451
|
+
if (this.level <= 2 /* WARN */) {
|
|
452
|
+
console.warn(`[${this.prefix}] WARN: ${message}`, ...args);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
error(message, ...args) {
|
|
456
|
+
if (this.level <= 3 /* ERROR */) {
|
|
457
|
+
console.error(`[${this.prefix}] ERROR: ${message}`, ...args);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async time(label, fn) {
|
|
461
|
+
const start = performance.now();
|
|
462
|
+
try {
|
|
463
|
+
const result = await fn();
|
|
464
|
+
const end = performance.now();
|
|
465
|
+
this.debug(`${label} completed in ${(end - start).toFixed(2)}ms`);
|
|
466
|
+
return result;
|
|
467
|
+
} catch (error) {
|
|
468
|
+
const end = performance.now();
|
|
469
|
+
this.error(`${label} failed after ${(end - start).toFixed(2)}ms`, error);
|
|
470
|
+
throw error;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
function parseLogLevel(levelString) {
|
|
475
|
+
if (!levelString)
|
|
476
|
+
return void 0;
|
|
477
|
+
const upper = levelString.toUpperCase();
|
|
478
|
+
switch (upper) {
|
|
479
|
+
case "DEBUG":
|
|
480
|
+
return 0 /* DEBUG */;
|
|
481
|
+
case "WARN":
|
|
482
|
+
return 2 /* WARN */;
|
|
483
|
+
case "ERROR":
|
|
484
|
+
return 3 /* ERROR */;
|
|
485
|
+
case "INFO":
|
|
486
|
+
return 1 /* INFO */;
|
|
487
|
+
default:
|
|
488
|
+
return void 0;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
var getDefaultLevel = () => {
|
|
492
|
+
const envLevel = getEnvironmentVariable("LOG_LEVEL");
|
|
493
|
+
const parsedLevel = parseLogLevel(envLevel);
|
|
494
|
+
if (parsedLevel !== void 0)
|
|
495
|
+
return parsedLevel;
|
|
496
|
+
const debugFlag = getEnvironmentVariable("VERYFRONT_DEBUG");
|
|
497
|
+
if (debugFlag === "1" || debugFlag === "true")
|
|
498
|
+
return 0 /* DEBUG */;
|
|
499
|
+
return 1 /* INFO */;
|
|
500
|
+
};
|
|
501
|
+
var trackedLoggers = /* @__PURE__ */ new Set();
|
|
502
|
+
function createLogger(prefix) {
|
|
503
|
+
const logger2 = new ConsoleLogger(prefix);
|
|
504
|
+
trackedLoggers.add(logger2);
|
|
505
|
+
return logger2;
|
|
506
|
+
}
|
|
507
|
+
var cliLogger = createLogger("CLI");
|
|
508
|
+
var serverLogger = createLogger("SERVER");
|
|
509
|
+
var rendererLogger = createLogger("RENDERER");
|
|
510
|
+
var bundlerLogger = createLogger("BUNDLER");
|
|
511
|
+
var agentLogger = createLogger("AGENT");
|
|
512
|
+
var logger = createLogger("VERYFRONT");
|
|
513
|
+
|
|
514
|
+
// src/ai/production/cost-tracking/tracker.ts
|
|
515
|
+
var CostTracker = class {
|
|
516
|
+
constructor(config) {
|
|
517
|
+
this.records = [];
|
|
518
|
+
this.dailyTotal = 0;
|
|
519
|
+
this.monthlyTotal = 0;
|
|
520
|
+
this.lastDayReset = Date.now();
|
|
521
|
+
this.lastMonthReset = Date.now();
|
|
522
|
+
this.resetInterval = null;
|
|
523
|
+
this.config = config;
|
|
524
|
+
this.startPeriodicReset();
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Track an agent response
|
|
528
|
+
*/
|
|
529
|
+
track(agentId, model, response, userId) {
|
|
530
|
+
if (!response.usage) {
|
|
531
|
+
agentLogger.warn("No usage data in response, cannot track costs");
|
|
532
|
+
return this.createEmptyRecord(agentId, model);
|
|
533
|
+
}
|
|
534
|
+
const provider = model.split("/")[0] || "unknown";
|
|
535
|
+
const cost = this.calculateCost(
|
|
536
|
+
provider,
|
|
537
|
+
response.usage.promptTokens,
|
|
538
|
+
response.usage.completionTokens
|
|
539
|
+
);
|
|
540
|
+
const record = {
|
|
541
|
+
timestamp: Date.now(),
|
|
542
|
+
agentId,
|
|
543
|
+
model,
|
|
544
|
+
provider,
|
|
545
|
+
tokens: {
|
|
546
|
+
prompt: response.usage.promptTokens,
|
|
547
|
+
completion: response.usage.completionTokens,
|
|
548
|
+
total: response.usage.totalTokens
|
|
549
|
+
},
|
|
550
|
+
cost,
|
|
551
|
+
userId
|
|
552
|
+
};
|
|
553
|
+
this.records.push(record);
|
|
554
|
+
this.dailyTotal += cost;
|
|
555
|
+
this.monthlyTotal += cost;
|
|
556
|
+
this.checkLimits();
|
|
557
|
+
return record;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Calculate cost based on token usage
|
|
561
|
+
*/
|
|
562
|
+
calculateCost(provider, inputTokens, outputTokens) {
|
|
563
|
+
const pricing = this.config.pricing[provider];
|
|
564
|
+
if (!pricing) {
|
|
565
|
+
agentLogger.warn(`No pricing configured for provider: ${provider}`);
|
|
566
|
+
return 0;
|
|
567
|
+
}
|
|
568
|
+
const inputCost = inputTokens / 1e6 * pricing.input;
|
|
569
|
+
const outputCost = outputTokens / 1e6 * pricing.output;
|
|
570
|
+
return inputCost + outputCost;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Get usage summary for a period
|
|
574
|
+
*/
|
|
575
|
+
getSummary(startTime, endTime) {
|
|
576
|
+
const start = startTime || 0;
|
|
577
|
+
const end = endTime || Date.now();
|
|
578
|
+
const relevantRecords = this.records.filter(
|
|
579
|
+
(r) => r.timestamp >= start && r.timestamp <= end
|
|
580
|
+
);
|
|
581
|
+
const summary = {
|
|
582
|
+
requests: relevantRecords.length,
|
|
583
|
+
tokens: {
|
|
584
|
+
prompt: 0,
|
|
585
|
+
completion: 0,
|
|
586
|
+
total: 0
|
|
587
|
+
},
|
|
588
|
+
cost: 0,
|
|
589
|
+
byProvider: {},
|
|
590
|
+
period: { start, end }
|
|
591
|
+
};
|
|
592
|
+
for (const record of relevantRecords) {
|
|
593
|
+
summary.tokens.prompt += record.tokens.prompt;
|
|
594
|
+
summary.tokens.completion += record.tokens.completion;
|
|
595
|
+
summary.tokens.total += record.tokens.total;
|
|
596
|
+
summary.cost += record.cost;
|
|
597
|
+
if (!summary.byProvider[record.provider]) {
|
|
598
|
+
summary.byProvider[record.provider] = {
|
|
599
|
+
requests: 0,
|
|
600
|
+
tokens: 0,
|
|
601
|
+
cost: 0
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
const providerStats = summary.byProvider[record.provider];
|
|
605
|
+
providerStats.requests++;
|
|
606
|
+
providerStats.tokens += record.tokens.total;
|
|
607
|
+
providerStats.cost += record.cost;
|
|
608
|
+
}
|
|
609
|
+
return summary;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Get daily summary
|
|
613
|
+
*/
|
|
614
|
+
getDailySummary() {
|
|
615
|
+
const now = Date.now();
|
|
616
|
+
const dayStart = now - 24 * 60 * 60 * 1e3;
|
|
617
|
+
return this.getSummary(dayStart, now);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Get monthly summary
|
|
621
|
+
*/
|
|
622
|
+
getMonthlySummary() {
|
|
623
|
+
const now = Date.now();
|
|
624
|
+
const monthStart = now - 30 * 24 * 60 * 60 * 1e3;
|
|
625
|
+
return this.getSummary(monthStart, now);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Check if limits are exceeded
|
|
629
|
+
*/
|
|
630
|
+
checkLimits() {
|
|
631
|
+
if (this.config.limits?.daily && this.dailyTotal > this.config.limits.daily) {
|
|
632
|
+
if (this.config.onLimitExceeded) {
|
|
633
|
+
this.config.onLimitExceeded(this.getDailySummary());
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (this.config.limits?.monthly && this.monthlyTotal > this.config.limits.monthly) {
|
|
637
|
+
if (this.config.onLimitExceeded) {
|
|
638
|
+
this.config.onLimitExceeded(this.getMonthlySummary());
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
startPeriodicReset() {
|
|
643
|
+
this.resetInterval = setInterval(() => {
|
|
644
|
+
const now = Date.now();
|
|
645
|
+
if (now - this.lastDayReset >= 24 * 60 * 60 * 1e3) {
|
|
646
|
+
this.dailyTotal = 0;
|
|
647
|
+
this.lastDayReset = now;
|
|
648
|
+
}
|
|
649
|
+
if (now - this.lastMonthReset >= 30 * 24 * 60 * 60 * 1e3) {
|
|
650
|
+
this.monthlyTotal = 0;
|
|
651
|
+
this.lastMonthReset = now;
|
|
652
|
+
}
|
|
653
|
+
}, 6e4);
|
|
654
|
+
}
|
|
655
|
+
destroy() {
|
|
656
|
+
if (this.resetInterval) {
|
|
657
|
+
clearInterval(this.resetInterval);
|
|
658
|
+
this.resetInterval = null;
|
|
659
|
+
}
|
|
660
|
+
this.records = [];
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Create empty record
|
|
664
|
+
*/
|
|
665
|
+
createEmptyRecord(agentId, model) {
|
|
666
|
+
return {
|
|
667
|
+
timestamp: Date.now(),
|
|
668
|
+
agentId,
|
|
669
|
+
model,
|
|
670
|
+
provider: model.split("/")[0] || "unknown",
|
|
671
|
+
tokens: { prompt: 0, completion: 0, total: 0 },
|
|
672
|
+
cost: 0
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Get all records
|
|
677
|
+
*/
|
|
678
|
+
getAllRecords() {
|
|
679
|
+
return [...this.records];
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Clear all records
|
|
683
|
+
*/
|
|
684
|
+
clear() {
|
|
685
|
+
this.records = [];
|
|
686
|
+
this.dailyTotal = 0;
|
|
687
|
+
this.monthlyTotal = 0;
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
function createCostTracker(config) {
|
|
691
|
+
const tracker = new CostTracker(config);
|
|
692
|
+
return {
|
|
693
|
+
/**
|
|
694
|
+
* Track agent response
|
|
695
|
+
*/
|
|
696
|
+
track(agentId, model, response, userId) {
|
|
697
|
+
return tracker.track(agentId, model, response, userId);
|
|
698
|
+
},
|
|
699
|
+
/**
|
|
700
|
+
* Get usage summary
|
|
701
|
+
*/
|
|
702
|
+
getSummary(startTime, endTime) {
|
|
703
|
+
return tracker.getSummary(startTime, endTime);
|
|
704
|
+
},
|
|
705
|
+
/**
|
|
706
|
+
* Get daily summary
|
|
707
|
+
*/
|
|
708
|
+
getDailySummary() {
|
|
709
|
+
return tracker.getDailySummary();
|
|
710
|
+
},
|
|
711
|
+
/**
|
|
712
|
+
* Get monthly summary
|
|
713
|
+
*/
|
|
714
|
+
getMonthlySummary() {
|
|
715
|
+
return tracker.getMonthlySummary();
|
|
716
|
+
},
|
|
717
|
+
/**
|
|
718
|
+
* Get all records
|
|
719
|
+
*/
|
|
720
|
+
getAllRecords() {
|
|
721
|
+
return tracker.getAllRecords();
|
|
722
|
+
},
|
|
723
|
+
/**
|
|
724
|
+
* Clear all data
|
|
725
|
+
*/
|
|
726
|
+
clear() {
|
|
727
|
+
tracker.clear();
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function costTrackingMiddleware(config) {
|
|
732
|
+
const tracker = createCostTracker(config);
|
|
733
|
+
return async (context, next) => {
|
|
734
|
+
const result = await next();
|
|
735
|
+
tracker.track(
|
|
736
|
+
context.agentId,
|
|
737
|
+
context.model || "unknown",
|
|
738
|
+
result,
|
|
739
|
+
context.data?.userId
|
|
740
|
+
);
|
|
741
|
+
return result;
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// src/ai/production/security/validator.ts
|
|
746
|
+
var COMMON_BLOCKED_PATTERNS = {
|
|
747
|
+
/** Prompt injection attempts */
|
|
748
|
+
promptInjection: [
|
|
749
|
+
/ignore\s+previous\s+instructions/i,
|
|
750
|
+
/ignore\s+all\s+previous\s+prompts/i,
|
|
751
|
+
/you\s+are\s+now\s+a/i,
|
|
752
|
+
/pretend\s+you\s+are/i,
|
|
753
|
+
/system:\s*/i,
|
|
754
|
+
/<\|im_start\|>/i,
|
|
755
|
+
/<\|im_end\|>/i
|
|
756
|
+
],
|
|
757
|
+
/** Potential data exfiltration */
|
|
758
|
+
dataExfiltration: [
|
|
759
|
+
/password/i,
|
|
760
|
+
/api[_\s-]?key/i,
|
|
761
|
+
/secret/i,
|
|
762
|
+
/token/i,
|
|
763
|
+
/credit\s+card/i
|
|
764
|
+
],
|
|
765
|
+
/** SQL injection patterns */
|
|
766
|
+
sqlInjection: [
|
|
767
|
+
/(\bUNION\b|\bSELECT\b).*\bFROM\b/i,
|
|
768
|
+
/;\s*(DROP|DELETE|UPDATE|INSERT)/i
|
|
769
|
+
],
|
|
770
|
+
/** XSS patterns */
|
|
771
|
+
xss: [
|
|
772
|
+
/<script[^>]*>.*?<\/script>/gi,
|
|
773
|
+
/javascript:/i,
|
|
774
|
+
/on\w+\s*=/i
|
|
775
|
+
// Event handlers
|
|
776
|
+
]
|
|
777
|
+
};
|
|
778
|
+
var PII_PATTERNS = {
|
|
779
|
+
email: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
|
|
780
|
+
phone: /\b(\+\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
781
|
+
ssn: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
782
|
+
creditCard: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g
|
|
783
|
+
};
|
|
784
|
+
var InputValidator = class {
|
|
785
|
+
constructor(config) {
|
|
786
|
+
this.config = config || {};
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Validate input
|
|
790
|
+
*/
|
|
791
|
+
async validate(input) {
|
|
792
|
+
const violations = [];
|
|
793
|
+
if (this.config?.maxLength && input.length > this.config.maxLength) {
|
|
794
|
+
violations.push({
|
|
795
|
+
type: "input",
|
|
796
|
+
reason: `Input exceeds maximum length of ${this.config.maxLength}`,
|
|
797
|
+
content: input.substring(0, 100) + "..."
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
if (this.config?.blockedPatterns) {
|
|
801
|
+
for (const pattern of this.config.blockedPatterns) {
|
|
802
|
+
if (pattern.test(input)) {
|
|
803
|
+
violations.push({
|
|
804
|
+
type: "input",
|
|
805
|
+
reason: "Input matches blocked pattern",
|
|
806
|
+
content: input,
|
|
807
|
+
pattern
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
if (this.config?.validate) {
|
|
813
|
+
const customValid = await this.config.validate(input);
|
|
814
|
+
if (!customValid) {
|
|
815
|
+
violations.push({
|
|
816
|
+
type: "input",
|
|
817
|
+
reason: "Custom validation failed",
|
|
818
|
+
content: input
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
let sanitized = input;
|
|
823
|
+
if (this.config?.sanitize) {
|
|
824
|
+
sanitized = this.sanitizeInput(input);
|
|
825
|
+
}
|
|
826
|
+
return {
|
|
827
|
+
valid: violations.length === 0,
|
|
828
|
+
sanitized: this.config?.sanitize ? sanitized : void 0,
|
|
829
|
+
violations
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Sanitize input (remove potentially harmful content)
|
|
834
|
+
*/
|
|
835
|
+
sanitizeInput(input) {
|
|
836
|
+
let sanitized = input;
|
|
837
|
+
sanitized = sanitized.replace(/<script[^>]*>.*?<\/script>/gi, "");
|
|
838
|
+
sanitized = sanitized.replace(/on\w+\s*=\s*["'][^"']*["']/gi, "");
|
|
839
|
+
sanitized = sanitized.replace(/javascript:/gi, "");
|
|
840
|
+
return sanitized;
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
var OutputFilter = class {
|
|
844
|
+
constructor(config) {
|
|
845
|
+
this.config = config || {};
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Filter output
|
|
849
|
+
*/
|
|
850
|
+
async filter(output) {
|
|
851
|
+
const violations = [];
|
|
852
|
+
let filtered = output;
|
|
853
|
+
if (this.config?.blockedPatterns) {
|
|
854
|
+
for (const pattern of this.config.blockedPatterns) {
|
|
855
|
+
if (pattern.test(filtered)) {
|
|
856
|
+
violations.push({
|
|
857
|
+
type: "output",
|
|
858
|
+
reason: "Output contains blocked pattern",
|
|
859
|
+
content: filtered,
|
|
860
|
+
pattern
|
|
861
|
+
});
|
|
862
|
+
filtered = filtered.replace(pattern, "[REDACTED]");
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
if (this.config?.filterPII) {
|
|
867
|
+
filtered = this.filterPII(filtered);
|
|
868
|
+
}
|
|
869
|
+
if (this.config?.filter) {
|
|
870
|
+
filtered = await this.config.filter(filtered);
|
|
871
|
+
}
|
|
872
|
+
return { filtered, violations };
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Filter PII from output
|
|
876
|
+
*/
|
|
877
|
+
filterPII(output) {
|
|
878
|
+
let filtered = output;
|
|
879
|
+
filtered = filtered.replace(PII_PATTERNS.email, "[EMAIL]");
|
|
880
|
+
filtered = filtered.replace(PII_PATTERNS.phone, "[PHONE]");
|
|
881
|
+
filtered = filtered.replace(PII_PATTERNS.ssn, "[SSN]");
|
|
882
|
+
filtered = filtered.replace(PII_PATTERNS.creditCard, "[CREDIT_CARD]");
|
|
883
|
+
return filtered;
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
function securityMiddleware(config) {
|
|
887
|
+
const inputValidator = new InputValidator(config.input);
|
|
888
|
+
const outputFilter = new OutputFilter(config.output);
|
|
889
|
+
return async (context, next) => {
|
|
890
|
+
const inputString = typeof context.input === "string" ? context.input : JSON.stringify(context.input);
|
|
891
|
+
const inputValidation = await inputValidator.validate(inputString);
|
|
892
|
+
if (!inputValidation.valid) {
|
|
893
|
+
inputValidation.violations.forEach((v) => {
|
|
894
|
+
if (config.onViolation) {
|
|
895
|
+
config.onViolation(v);
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
const firstViolation = inputValidation.violations[0];
|
|
899
|
+
throw toError(createError({
|
|
900
|
+
type: "agent",
|
|
901
|
+
message: `Input validation failed: ${firstViolation?.reason || "Unknown reason"}`
|
|
902
|
+
}));
|
|
903
|
+
}
|
|
904
|
+
if (inputValidation.sanitized) {
|
|
905
|
+
context.input = inputValidation.sanitized;
|
|
906
|
+
}
|
|
907
|
+
const result = await next();
|
|
908
|
+
const outputFiltering = await outputFilter.filter(result.text);
|
|
909
|
+
if (outputFiltering.violations.length > 0) {
|
|
910
|
+
outputFiltering.violations.forEach((v) => {
|
|
911
|
+
if (config.onViolation) {
|
|
912
|
+
config.onViolation(v);
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
return {
|
|
917
|
+
...result,
|
|
918
|
+
text: outputFiltering.filtered
|
|
919
|
+
};
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
export {
|
|
923
|
+
COMMON_BLOCKED_PATTERNS,
|
|
924
|
+
InputValidator,
|
|
925
|
+
OutputFilter,
|
|
926
|
+
cacheMiddleware,
|
|
927
|
+
costTrackingMiddleware,
|
|
928
|
+
createCache,
|
|
929
|
+
createCostTracker,
|
|
930
|
+
createRateLimiter,
|
|
931
|
+
rateLimitMiddleware,
|
|
932
|
+
securityMiddleware
|
|
933
|
+
};
|
|
934
|
+
//# sourceMappingURL=production.js.map
|