milens 0.6.2 → 0.6.3
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 +157 -14
- package/dist/analyzer/engine.d.ts +1 -0
- package/dist/analyzer/engine.d.ts.map +1 -1
- package/dist/analyzer/engine.js +27 -8
- package/dist/analyzer/engine.js.map +1 -1
- package/dist/analyzer/review.d.ts +23 -0
- package/dist/analyzer/review.d.ts.map +1 -0
- package/dist/analyzer/review.js +143 -0
- package/dist/analyzer/review.js.map +1 -0
- package/dist/analyzer/testplan.d.ts +59 -0
- package/dist/analyzer/testplan.d.ts.map +1 -0
- package/dist/analyzer/testplan.js +218 -0
- package/dist/analyzer/testplan.js.map +1 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/gateway/analyzer.d.ts +6 -0
- package/dist/gateway/analyzer.d.ts.map +1 -0
- package/dist/gateway/analyzer.js +218 -0
- package/dist/gateway/analyzer.js.map +1 -0
- package/dist/gateway/cache.d.ts +35 -0
- package/dist/gateway/cache.d.ts.map +1 -0
- package/dist/gateway/cache.js +175 -0
- package/dist/gateway/cache.js.map +1 -0
- package/dist/gateway/config.d.ts +10 -0
- package/dist/gateway/config.d.ts.map +1 -0
- package/dist/gateway/config.js +167 -0
- package/dist/gateway/config.js.map +1 -0
- package/dist/gateway/context-memory.d.ts +68 -0
- package/dist/gateway/context-memory.d.ts.map +1 -0
- package/dist/gateway/context-memory.js +157 -0
- package/dist/gateway/context-memory.js.map +1 -0
- package/dist/gateway/observability.d.ts +83 -0
- package/dist/gateway/observability.d.ts.map +1 -0
- package/dist/gateway/observability.js +152 -0
- package/dist/gateway/observability.js.map +1 -0
- package/dist/gateway/privacy.d.ts +27 -0
- package/dist/gateway/privacy.d.ts.map +1 -0
- package/dist/gateway/privacy.js +139 -0
- package/dist/gateway/privacy.js.map +1 -0
- package/dist/gateway/providers.d.ts +66 -0
- package/dist/gateway/providers.d.ts.map +1 -0
- package/dist/gateway/providers.js +377 -0
- package/dist/gateway/providers.js.map +1 -0
- package/dist/gateway/router.d.ts +18 -0
- package/dist/gateway/router.d.ts.map +1 -0
- package/dist/gateway/router.js +102 -0
- package/dist/gateway/router.js.map +1 -0
- package/dist/gateway/server.d.ts +20 -0
- package/dist/gateway/server.d.ts.map +1 -0
- package/dist/gateway/server.js +387 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/gateway/translator.d.ts +19 -0
- package/dist/gateway/translator.d.ts.map +1 -0
- package/dist/gateway/translator.js +340 -0
- package/dist/gateway/translator.js.map +1 -0
- package/dist/gateway/types.d.ts +215 -0
- package/dist/gateway/types.d.ts.map +1 -0
- package/dist/gateway/types.js +3 -0
- package/dist/gateway/types.js.map +1 -0
- package/dist/parser/extract.d.ts +1 -0
- package/dist/parser/extract.d.ts.map +1 -1
- package/dist/parser/extract.js +8 -0
- package/dist/parser/extract.js.map +1 -1
- package/dist/parser/lang-go.d.ts.map +1 -1
- package/dist/parser/lang-go.js +41 -5
- package/dist/parser/lang-go.js.map +1 -1
- package/dist/parser/lang-java.d.ts.map +1 -1
- package/dist/parser/lang-java.js +1 -0
- package/dist/parser/lang-java.js.map +1 -1
- package/dist/parser/lang-py.d.ts.map +1 -1
- package/dist/parser/lang-py.js +22 -0
- package/dist/parser/lang-py.js.map +1 -1
- package/dist/parser/lang-ruby.d.ts.map +1 -1
- package/dist/parser/lang-ruby.js +1 -0
- package/dist/parser/lang-ruby.js.map +1 -1
- package/dist/server/mcp.d.ts.map +1 -1
- package/dist/server/mcp.js +615 -106
- package/dist/server/mcp.js.map +1 -1
- package/dist/skills.js +32 -0
- package/dist/skills.js.map +1 -1
- package/dist/store/db.d.ts +44 -0
- package/dist/store/db.d.ts.map +1 -1
- package/dist/store/db.js +142 -25
- package/dist/store/db.js.map +1 -1
- package/dist/store/gateway-schema.sql +53 -0
- package/dist/store/schema.sql +33 -0
- package/dist/store/vectors.d.ts +65 -0
- package/dist/store/vectors.d.ts.map +1 -0
- package/dist/store/vectors.js +212 -0
- package/dist/store/vectors.js.map +1 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +9 -0
- package/dist/utils.js.map +1 -0
- package/docs/diagram2.svg +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { CostRecord, Intent, Tier } from './types.js';
|
|
2
|
+
import type BetterSqlite3 from 'better-sqlite3';
|
|
3
|
+
export declare class CostTracker {
|
|
4
|
+
private rawDb;
|
|
5
|
+
private stmts;
|
|
6
|
+
constructor(rawDb: BetterSqlite3.Database);
|
|
7
|
+
private prepareStatements;
|
|
8
|
+
/** Record a completed request */
|
|
9
|
+
record(data: {
|
|
10
|
+
provider: string;
|
|
11
|
+
model: string;
|
|
12
|
+
tokensIn: number;
|
|
13
|
+
tokensOut: number;
|
|
14
|
+
costEstimate: number;
|
|
15
|
+
durationMs: number;
|
|
16
|
+
symbolIds: string[];
|
|
17
|
+
filePaths: string[];
|
|
18
|
+
intent: Intent;
|
|
19
|
+
complexityScore: number;
|
|
20
|
+
tier: Tier;
|
|
21
|
+
cacheHit: boolean;
|
|
22
|
+
}): string;
|
|
23
|
+
/** Cost breakdown by provider + model */
|
|
24
|
+
getCostByProvider(windowSql?: string): {
|
|
25
|
+
provider: string;
|
|
26
|
+
model: string;
|
|
27
|
+
request_count: number;
|
|
28
|
+
total_tokens_in: number;
|
|
29
|
+
total_tokens_out: number;
|
|
30
|
+
total_cost: number;
|
|
31
|
+
avg_duration: number;
|
|
32
|
+
}[];
|
|
33
|
+
/** Cost breakdown by file path */
|
|
34
|
+
getCostByFile(limit?: number, windowSql?: string): {
|
|
35
|
+
file_path: string;
|
|
36
|
+
request_count: number;
|
|
37
|
+
total_tokens: number;
|
|
38
|
+
total_cost: number;
|
|
39
|
+
}[];
|
|
40
|
+
/** Cost breakdown by intent */
|
|
41
|
+
getCostByIntent(windowSql?: string): {
|
|
42
|
+
intent: string;
|
|
43
|
+
request_count: number;
|
|
44
|
+
total_tokens: number;
|
|
45
|
+
total_cost: number;
|
|
46
|
+
avg_complexity: number;
|
|
47
|
+
}[];
|
|
48
|
+
/** Cost breakdown by tier */
|
|
49
|
+
getCostByTier(windowSql?: string): {
|
|
50
|
+
tier: string;
|
|
51
|
+
request_count: number;
|
|
52
|
+
total_cost: number;
|
|
53
|
+
avg_duration: number;
|
|
54
|
+
}[];
|
|
55
|
+
/** Cache hit/miss stats and savings */
|
|
56
|
+
getCacheSavings(windowSql?: string): {
|
|
57
|
+
cacheHits: any;
|
|
58
|
+
cacheMisses: any;
|
|
59
|
+
costSaved: any;
|
|
60
|
+
tokensSaved: any;
|
|
61
|
+
hitRate: number;
|
|
62
|
+
};
|
|
63
|
+
/** Most expensive symbols */
|
|
64
|
+
getTopExpensiveSymbols(limit?: number, windowSql?: string): {
|
|
65
|
+
symbol_id: string;
|
|
66
|
+
request_count: number;
|
|
67
|
+
total_cost: number;
|
|
68
|
+
total_tokens: number;
|
|
69
|
+
}[];
|
|
70
|
+
/** Overall summary */
|
|
71
|
+
getSummary(windowSql?: string): {
|
|
72
|
+
totalRequests: any;
|
|
73
|
+
totalTokensIn: any;
|
|
74
|
+
totalTokensOut: any;
|
|
75
|
+
totalCost: any;
|
|
76
|
+
avgDuration: any;
|
|
77
|
+
firstRequest: any;
|
|
78
|
+
lastRequest: any;
|
|
79
|
+
};
|
|
80
|
+
/** Get recent requests for debugging */
|
|
81
|
+
getRecentRequests(limit?: number): CostRecord[];
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=observability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../../src/gateway/observability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAEhD,qBAAa,WAAW;IAGV,OAAO,CAAC,KAAK;IAFzB,OAAO,CAAC,KAAK,CAA+C;gBAExC,KAAK,EAAE,aAAa,CAAC,QAAQ;IAIjD,OAAO,CAAC,iBAAiB;IAyFzB,iCAAiC;IACjC,MAAM,CAAC,IAAI,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,IAAI,CAAC;QACX,QAAQ,EAAE,OAAO,CAAC;KACnB,GAAG,MAAM;IAiBV,yCAAyC;IACzC,iBAAiB,CAAC,SAAS,SAAa,GACS;QAC7C,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAChC,aAAa,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QACzE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;KAC1C,EAAE;IAGL,kCAAkC;IAClC,aAAa,CAAC,KAAK,SAAK,EAAE,SAAS,SAAa,GACI;QAChD,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;KACpF,EAAE;IAGL,+BAA+B;IAC/B,eAAe,CAAC,SAAS,SAAa,GACS;QAC3C,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;KACzG,EAAE;IAGL,6BAA6B;IAC7B,aAAa,CAAC,SAAS,SAAa,GACS;QACzC,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;KAC/E,EAAE;IAGL,uCAAuC;IACvC,eAAe,CAAC,SAAS,SAAa;;;;;;;IAatC,6BAA6B;IAC7B,sBAAsB,CAAC,KAAK,SAAK,EAAE,SAAS,SAAa,GACD;QACpD,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;KACpF,EAAE;IAGL,sBAAsB;IACtB,UAAU,CAAC,SAAS,SAAa;;;;;;;;;IAajC,wCAAwC;IACxC,iBAAiB,CAAC,KAAK,SAAK,GAAG,UAAU,EAAE;CAkB5C"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// ── Observability — per-function / per-file cost tracking ──
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
export class CostTracker {
|
|
4
|
+
rawDb;
|
|
5
|
+
stmts;
|
|
6
|
+
constructor(rawDb) {
|
|
7
|
+
this.rawDb = rawDb;
|
|
8
|
+
this.stmts = this.prepareStatements();
|
|
9
|
+
}
|
|
10
|
+
prepareStatements() {
|
|
11
|
+
return {
|
|
12
|
+
insert: this.rawDb.prepare(`INSERT INTO gateway_cost_log
|
|
13
|
+
(request_id, provider, model, tokens_in, tokens_out, cost_estimate, duration_ms,
|
|
14
|
+
symbol_ids, file_paths, intent, complexity_score, tier, cache_hit)
|
|
15
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
|
16
|
+
byProvider: this.rawDb.prepare(`SELECT provider, model,
|
|
17
|
+
COUNT(*) as request_count,
|
|
18
|
+
SUM(tokens_in) as total_tokens_in,
|
|
19
|
+
SUM(tokens_out) as total_tokens_out,
|
|
20
|
+
SUM(cost_estimate) as total_cost,
|
|
21
|
+
AVG(duration_ms) as avg_duration
|
|
22
|
+
FROM gateway_cost_log
|
|
23
|
+
WHERE timestamp >= datetime('now', ?)
|
|
24
|
+
GROUP BY provider, model
|
|
25
|
+
ORDER BY total_cost DESC`),
|
|
26
|
+
byFile: this.rawDb.prepare(`SELECT jf.value as file_path,
|
|
27
|
+
COUNT(*) as request_count,
|
|
28
|
+
SUM(cl.tokens_in + cl.tokens_out) as total_tokens,
|
|
29
|
+
SUM(cl.cost_estimate) as total_cost
|
|
30
|
+
FROM gateway_cost_log cl, json_each(cl.file_paths) jf
|
|
31
|
+
WHERE cl.timestamp >= datetime('now', ?)
|
|
32
|
+
GROUP BY jf.value
|
|
33
|
+
ORDER BY total_cost DESC
|
|
34
|
+
LIMIT ?`),
|
|
35
|
+
byIntent: this.rawDb.prepare(`SELECT intent,
|
|
36
|
+
COUNT(*) as request_count,
|
|
37
|
+
SUM(tokens_in + tokens_out) as total_tokens,
|
|
38
|
+
SUM(cost_estimate) as total_cost,
|
|
39
|
+
AVG(complexity_score) as avg_complexity
|
|
40
|
+
FROM gateway_cost_log
|
|
41
|
+
WHERE timestamp >= datetime('now', ?)
|
|
42
|
+
GROUP BY intent
|
|
43
|
+
ORDER BY total_cost DESC`),
|
|
44
|
+
byTier: this.rawDb.prepare(`SELECT tier,
|
|
45
|
+
COUNT(*) as request_count,
|
|
46
|
+
SUM(cost_estimate) as total_cost,
|
|
47
|
+
AVG(duration_ms) as avg_duration
|
|
48
|
+
FROM gateway_cost_log
|
|
49
|
+
WHERE timestamp >= datetime('now', ?)
|
|
50
|
+
GROUP BY tier`),
|
|
51
|
+
cacheSavings: this.rawDb.prepare(`SELECT
|
|
52
|
+
SUM(CASE WHEN cache_hit = 1 THEN 1 ELSE 0 END) as cache_hits,
|
|
53
|
+
SUM(CASE WHEN cache_hit = 0 THEN 1 ELSE 0 END) as cache_misses,
|
|
54
|
+
SUM(CASE WHEN cache_hit = 1 THEN cost_estimate ELSE 0 END) as cost_saved,
|
|
55
|
+
SUM(CASE WHEN cache_hit = 1 THEN tokens_in + tokens_out ELSE 0 END) as tokens_saved
|
|
56
|
+
FROM gateway_cost_log
|
|
57
|
+
WHERE timestamp >= datetime('now', ?)`),
|
|
58
|
+
topSymbols: this.rawDb.prepare(`SELECT js.value as symbol_id,
|
|
59
|
+
COUNT(*) as request_count,
|
|
60
|
+
SUM(cl.cost_estimate) as total_cost,
|
|
61
|
+
SUM(cl.tokens_in + cl.tokens_out) as total_tokens
|
|
62
|
+
FROM gateway_cost_log cl, json_each(cl.symbol_ids) js
|
|
63
|
+
WHERE cl.timestamp >= datetime('now', ?)
|
|
64
|
+
GROUP BY js.value
|
|
65
|
+
ORDER BY total_cost DESC
|
|
66
|
+
LIMIT ?`),
|
|
67
|
+
summary: this.rawDb.prepare(`SELECT
|
|
68
|
+
COUNT(*) as total_requests,
|
|
69
|
+
SUM(tokens_in) as total_tokens_in,
|
|
70
|
+
SUM(tokens_out) as total_tokens_out,
|
|
71
|
+
SUM(cost_estimate) as total_cost,
|
|
72
|
+
AVG(duration_ms) as avg_duration,
|
|
73
|
+
MIN(timestamp) as first_request,
|
|
74
|
+
MAX(timestamp) as last_request
|
|
75
|
+
FROM gateway_cost_log
|
|
76
|
+
WHERE timestamp >= datetime('now', ?)`),
|
|
77
|
+
recentRequests: this.rawDb.prepare(`SELECT * FROM gateway_cost_log ORDER BY timestamp DESC LIMIT ?`),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Record a completed request */
|
|
81
|
+
record(data) {
|
|
82
|
+
const requestId = randomUUID();
|
|
83
|
+
this.stmts.insert.run(requestId, data.provider, data.model, data.tokensIn, data.tokensOut, data.costEstimate, data.durationMs, JSON.stringify(data.symbolIds), JSON.stringify(data.filePaths), data.intent, data.complexityScore, data.tier, data.cacheHit ? 1 : 0);
|
|
84
|
+
return requestId;
|
|
85
|
+
}
|
|
86
|
+
/** Cost breakdown by provider + model */
|
|
87
|
+
getCostByProvider(windowSql = '-30 days') {
|
|
88
|
+
return this.stmts.byProvider.all(windowSql);
|
|
89
|
+
}
|
|
90
|
+
/** Cost breakdown by file path */
|
|
91
|
+
getCostByFile(limit = 20, windowSql = '-30 days') {
|
|
92
|
+
return this.stmts.byFile.all(windowSql, limit);
|
|
93
|
+
}
|
|
94
|
+
/** Cost breakdown by intent */
|
|
95
|
+
getCostByIntent(windowSql = '-30 days') {
|
|
96
|
+
return this.stmts.byIntent.all(windowSql);
|
|
97
|
+
}
|
|
98
|
+
/** Cost breakdown by tier */
|
|
99
|
+
getCostByTier(windowSql = '-30 days') {
|
|
100
|
+
return this.stmts.byTier.all(windowSql);
|
|
101
|
+
}
|
|
102
|
+
/** Cache hit/miss stats and savings */
|
|
103
|
+
getCacheSavings(windowSql = '-30 days') {
|
|
104
|
+
const row = this.stmts.cacheSavings.get(windowSql);
|
|
105
|
+
return {
|
|
106
|
+
cacheHits: row?.cache_hits ?? 0,
|
|
107
|
+
cacheMisses: row?.cache_misses ?? 0,
|
|
108
|
+
costSaved: row?.cost_saved ?? 0,
|
|
109
|
+
tokensSaved: row?.tokens_saved ?? 0,
|
|
110
|
+
hitRate: row?.cache_hits
|
|
111
|
+
? row.cache_hits / (row.cache_hits + row.cache_misses)
|
|
112
|
+
: 0,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** Most expensive symbols */
|
|
116
|
+
getTopExpensiveSymbols(limit = 10, windowSql = '-30 days') {
|
|
117
|
+
return this.stmts.topSymbols.all(windowSql, limit);
|
|
118
|
+
}
|
|
119
|
+
/** Overall summary */
|
|
120
|
+
getSummary(windowSql = '-30 days') {
|
|
121
|
+
const row = this.stmts.summary.get(windowSql);
|
|
122
|
+
return {
|
|
123
|
+
totalRequests: row?.total_requests ?? 0,
|
|
124
|
+
totalTokensIn: row?.total_tokens_in ?? 0,
|
|
125
|
+
totalTokensOut: row?.total_tokens_out ?? 0,
|
|
126
|
+
totalCost: row?.total_cost ?? 0,
|
|
127
|
+
avgDuration: row?.avg_duration ?? 0,
|
|
128
|
+
firstRequest: row?.first_request ?? null,
|
|
129
|
+
lastRequest: row?.last_request ?? null,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** Get recent requests for debugging */
|
|
133
|
+
getRecentRequests(limit = 10) {
|
|
134
|
+
return this.stmts.recentRequests.all(limit).map(r => ({
|
|
135
|
+
requestId: r.request_id,
|
|
136
|
+
timestamp: r.timestamp,
|
|
137
|
+
provider: r.provider,
|
|
138
|
+
model: r.model,
|
|
139
|
+
tokensIn: r.tokens_in,
|
|
140
|
+
tokensOut: r.tokens_out,
|
|
141
|
+
costEstimate: r.cost_estimate,
|
|
142
|
+
durationMs: r.duration_ms,
|
|
143
|
+
symbolIds: JSON.parse(r.symbol_ids || '[]'),
|
|
144
|
+
filePaths: JSON.parse(r.file_paths || '[]'),
|
|
145
|
+
intent: r.intent,
|
|
146
|
+
complexityScore: r.complexity_score,
|
|
147
|
+
tier: r.tier,
|
|
148
|
+
cacheHit: !!r.cache_hit,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=observability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observability.js","sourceRoot":"","sources":["../../src/gateway/observability.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAE9D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,MAAM,OAAO,WAAW;IAGF;IAFZ,KAAK,CAA+C;IAE5D,YAAoB,KAA6B;QAA7B,UAAK,GAAL,KAAK,CAAwB;QAC/C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;IACxC,CAAC;IAEO,iBAAiB;QACvB,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CACxB;;;wDAGgD,CACjD;YACD,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAC5B;;;;;;;;;kCAS0B,CAC3B;YACD,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CACxB;;;;;;;;iBAQS,CACV;YACD,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAC1B;;;;;;;;kCAQ0B,CAC3B;YACD,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CACxB;;;;;;uBAMe,CAChB;YACD,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAC9B;;;;;;+CAMuC,CACxC;YACD,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAC5B;;;;;;;;iBAQS,CACV;YACD,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CACzB;;;;;;;;;+CASuC,CACxC;YACD,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAChC,gEAAgE,CACjE;SACF,CAAC;IACJ,CAAC;IAED,iCAAiC;IACjC,MAAM,CAAC,IAaN;QACC,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CACnB,SAAS,EACT,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,EACzB,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,EAChD,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAC9B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAC9B,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACtB,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,yCAAyC;IACzC,iBAAiB,CAAC,SAAS,GAAG,UAAU;QACtC,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAIvC,CAAC;IACN,CAAC;IAED,kCAAkC;IAClC,aAAa,CAAC,KAAK,GAAG,EAAE,EAAE,SAAS,GAAG,UAAU;QAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAE1C,CAAC;IACN,CAAC;IAED,+BAA+B;IAC/B,eAAe,CAAC,SAAS,GAAG,UAAU;QACpC,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAErC,CAAC;IACN,CAAC;IAED,6BAA6B;IAC7B,aAAa,CAAC,SAAS,GAAG,UAAU;QAClC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAEnC,CAAC;IACN,CAAC;IAED,uCAAuC;IACvC,eAAe,CAAC,SAAS,GAAG,UAAU;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAQ,CAAC;QAC1D,OAAO;YACL,SAAS,EAAE,GAAG,EAAE,UAAU,IAAI,CAAC;YAC/B,WAAW,EAAE,GAAG,EAAE,YAAY,IAAI,CAAC;YACnC,SAAS,EAAE,GAAG,EAAE,UAAU,IAAI,CAAC;YAC/B,WAAW,EAAE,GAAG,EAAE,YAAY,IAAI,CAAC;YACnC,OAAO,EAAE,GAAG,EAAE,UAAU;gBACtB,CAAC,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC;gBACtD,CAAC,CAAC,CAAC;SACN,CAAC;IACJ,CAAC;IAED,6BAA6B;IAC7B,sBAAsB,CAAC,KAAK,GAAG,EAAE,EAAE,SAAS,GAAG,UAAU;QACvD,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAE9C,CAAC;IACN,CAAC;IAED,sBAAsB;IACtB,UAAU,CAAC,SAAS,GAAG,UAAU;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAQ,CAAC;QACrD,OAAO;YACL,aAAa,EAAE,GAAG,EAAE,cAAc,IAAI,CAAC;YACvC,aAAa,EAAE,GAAG,EAAE,eAAe,IAAI,CAAC;YACxC,cAAc,EAAE,GAAG,EAAE,gBAAgB,IAAI,CAAC;YAC1C,SAAS,EAAE,GAAG,EAAE,UAAU,IAAI,CAAC;YAC/B,WAAW,EAAE,GAAG,EAAE,YAAY,IAAI,CAAC;YACnC,YAAY,EAAE,GAAG,EAAE,aAAa,IAAI,IAAI;YACxC,WAAW,EAAE,GAAG,EAAE,YAAY,IAAI,IAAI;SACvC,CAAC;IACJ,CAAC;IAED,wCAAwC;IACxC,iBAAiB,CAAC,KAAK,GAAG,EAAE;QAC1B,OAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/D,SAAS,EAAE,CAAC,CAAC,UAAU;YACvB,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,QAAQ,EAAE,CAAC,CAAC,SAAS;YACrB,SAAS,EAAE,CAAC,CAAC,UAAU;YACvB,YAAY,EAAE,CAAC,CAAC,aAAa;YAC7B,UAAU,EAAE,CAAC,CAAC,WAAW;YACzB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC;YAC3C,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC;YAC3C,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,eAAe,EAAE,CAAC,CAAC,gBAAgB;YACnC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;SACxB,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ChatMessage, PrivacyRule, PrivacyDecision, MessageAnalysis } from './types.js';
|
|
2
|
+
import type BetterSqlite3 from 'better-sqlite3';
|
|
3
|
+
export declare class PrivacyFilter {
|
|
4
|
+
private rawDb;
|
|
5
|
+
private defaultRules;
|
|
6
|
+
private rulesCache;
|
|
7
|
+
private stmts;
|
|
8
|
+
constructor(rawDb: BetterSqlite3.Database, defaultRules?: PrivacyRule[]);
|
|
9
|
+
private prepareStatements;
|
|
10
|
+
/** Evaluate privacy decision for a request based on referenced files */
|
|
11
|
+
evaluate(analysis: MessageAnalysis): PrivacyDecision;
|
|
12
|
+
/** Mask sensitive content in messages — returns masked messages + unmask map */
|
|
13
|
+
maskMessages(messages: ChatMessage[], rules: PrivacyRule[]): {
|
|
14
|
+
masked: ChatMessage[];
|
|
15
|
+
unmaskMap: Map<string, string>;
|
|
16
|
+
};
|
|
17
|
+
/** Restore original content from masked tokens */
|
|
18
|
+
unmask(text: string, unmaskMap: Map<string, string>): string;
|
|
19
|
+
/** Add a privacy rule (persisted to DB) */
|
|
20
|
+
addRule(rule: PrivacyRule): void;
|
|
21
|
+
/** Remove a privacy rule */
|
|
22
|
+
removeRule(pathPattern: string): boolean;
|
|
23
|
+
/** List all rules */
|
|
24
|
+
listRules(): PrivacyRule[];
|
|
25
|
+
private loadRules;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=privacy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/gateway/privacy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAiB,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC5G,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAEhD,qBAAa,aAAa;IAKtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,YAAY;IALtB,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,KAAK,CAAiD;gBAGpD,KAAK,EAAE,aAAa,CAAC,QAAQ,EAC7B,YAAY,GAAE,WAAW,EAAO;IAK1C,OAAO,CAAC,iBAAiB;IAUzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe;IA6BpD,gFAAgF;IAChF,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG;QAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;IAsBtH,kDAAkD;IAClD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM;IAQ5D,2CAA2C;IAC3C,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAKhC,4BAA4B;IAC5B,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAMxC,qBAAqB;IACrB,SAAS,IAAI,WAAW,EAAE;IAI1B,OAAO,CAAC,SAAS;CAUlB"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// ── Privacy filter — path-based sensitivity rules with masking ──
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
export class PrivacyFilter {
|
|
4
|
+
rawDb;
|
|
5
|
+
defaultRules;
|
|
6
|
+
rulesCache = null;
|
|
7
|
+
stmts;
|
|
8
|
+
constructor(rawDb, defaultRules = []) {
|
|
9
|
+
this.rawDb = rawDb;
|
|
10
|
+
this.defaultRules = defaultRules;
|
|
11
|
+
this.stmts = this.prepareStatements();
|
|
12
|
+
}
|
|
13
|
+
prepareStatements() {
|
|
14
|
+
return {
|
|
15
|
+
listRules: this.rawDb.prepare('SELECT * FROM gateway_privacy_rules ORDER BY path_pattern'),
|
|
16
|
+
addRule: this.rawDb.prepare(`INSERT OR REPLACE INTO gateway_privacy_rules (path_pattern, action, reason) VALUES (?, ?, ?)`),
|
|
17
|
+
removeRule: this.rawDb.prepare('DELETE FROM gateway_privacy_rules WHERE path_pattern = ?'),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Evaluate privacy decision for a request based on referenced files */
|
|
21
|
+
evaluate(analysis) {
|
|
22
|
+
const rules = this.loadRules();
|
|
23
|
+
const matched = [];
|
|
24
|
+
let highestAction = 'allow';
|
|
25
|
+
for (const file of analysis.files) {
|
|
26
|
+
for (const rule of rules) {
|
|
27
|
+
if (matchGlob(file, rule.pathPattern)) {
|
|
28
|
+
matched.push(rule);
|
|
29
|
+
highestAction = promoteAction(highestAction, rule.action);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Also check symbol file paths
|
|
34
|
+
for (const sym of analysis.symbols) {
|
|
35
|
+
for (const rule of rules) {
|
|
36
|
+
if (matchGlob(sym.filePath, rule.pathPattern)) {
|
|
37
|
+
if (!matched.some(m => m.pathPattern === rule.pathPattern)) {
|
|
38
|
+
matched.push(rule);
|
|
39
|
+
highestAction = promoteAction(highestAction, rule.action);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { action: highestAction, matchedRules: matched };
|
|
45
|
+
}
|
|
46
|
+
/** Mask sensitive content in messages — returns masked messages + unmask map */
|
|
47
|
+
maskMessages(messages, rules) {
|
|
48
|
+
const unmaskMap = new Map();
|
|
49
|
+
const masked = messages.map(msg => {
|
|
50
|
+
const content = typeof msg.content === 'string' ? msg.content : '';
|
|
51
|
+
if (!content)
|
|
52
|
+
return msg;
|
|
53
|
+
let result = content;
|
|
54
|
+
for (const rule of rules) {
|
|
55
|
+
if (rule.action !== 'mask')
|
|
56
|
+
continue;
|
|
57
|
+
result = maskPatternOccurrences(result, rule.pathPattern, unmaskMap);
|
|
58
|
+
}
|
|
59
|
+
// Mask inline @sensitive annotations
|
|
60
|
+
result = maskSensitiveAnnotations(result, unmaskMap);
|
|
61
|
+
return { ...msg, content: result };
|
|
62
|
+
});
|
|
63
|
+
return { masked, unmaskMap };
|
|
64
|
+
}
|
|
65
|
+
/** Restore original content from masked tokens */
|
|
66
|
+
unmask(text, unmaskMap) {
|
|
67
|
+
let result = text;
|
|
68
|
+
for (const [token, original] of unmaskMap) {
|
|
69
|
+
result = result.replaceAll(token, original);
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
/** Add a privacy rule (persisted to DB) */
|
|
74
|
+
addRule(rule) {
|
|
75
|
+
this.stmts.addRule.run(rule.pathPattern, rule.action, rule.reason ?? null);
|
|
76
|
+
this.rulesCache = null;
|
|
77
|
+
}
|
|
78
|
+
/** Remove a privacy rule */
|
|
79
|
+
removeRule(pathPattern) {
|
|
80
|
+
const r = this.stmts.removeRule.run(pathPattern);
|
|
81
|
+
this.rulesCache = null;
|
|
82
|
+
return r.changes > 0;
|
|
83
|
+
}
|
|
84
|
+
/** List all rules */
|
|
85
|
+
listRules() {
|
|
86
|
+
return this.loadRules();
|
|
87
|
+
}
|
|
88
|
+
loadRules() {
|
|
89
|
+
if (this.rulesCache)
|
|
90
|
+
return this.rulesCache;
|
|
91
|
+
const dbRules = this.stmts.listRules.all().map(r => ({
|
|
92
|
+
pathPattern: r.path_pattern,
|
|
93
|
+
action: r.action,
|
|
94
|
+
reason: r.reason ?? undefined,
|
|
95
|
+
}));
|
|
96
|
+
this.rulesCache = [...this.defaultRules, ...dbRules];
|
|
97
|
+
return this.rulesCache;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// ── Helpers ──
|
|
101
|
+
/** Simple glob matching: supports * and ** */
|
|
102
|
+
function matchGlob(filePath, pattern) {
|
|
103
|
+
const regex = pattern
|
|
104
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars
|
|
105
|
+
.replace(/\*\*/g, '{{GLOBSTAR}}') // Temp placeholder
|
|
106
|
+
.replace(/\*/g, '[^/]*') // Single glob
|
|
107
|
+
.replace(/{{GLOBSTAR}}/g, '.*'); // Double glob
|
|
108
|
+
return new RegExp(`^${regex}$`).test(filePath);
|
|
109
|
+
}
|
|
110
|
+
/** Promote to higher severity: block > local-only > mask > allow */
|
|
111
|
+
function promoteAction(current, incoming) {
|
|
112
|
+
const priority = { allow: 0, mask: 1, 'local-only': 2, block: 3 };
|
|
113
|
+
return (priority[incoming] ?? 0) > (priority[current] ?? 0) ? incoming : current;
|
|
114
|
+
}
|
|
115
|
+
/** Replace occurrences matching a glob pattern in text with masked tokens */
|
|
116
|
+
function maskPatternOccurrences(text, pathPattern, unmaskMap) {
|
|
117
|
+
// Extract a searchable pattern from the glob (e.g., "secrets/**" → "secrets/")
|
|
118
|
+
const literalPrefix = pathPattern.replace(/\*.*$/, '');
|
|
119
|
+
if (!literalPrefix || !text.includes(literalPrefix))
|
|
120
|
+
return text;
|
|
121
|
+
// Find all file-path-like substrings starting with the literal prefix
|
|
122
|
+
const filePathRe = new RegExp(literalPrefix.replace(/[.+^${}()|[\]\\]/g, '\\$&') + '[\\w/._-]*', 'g');
|
|
123
|
+
return text.replace(filePathRe, match => {
|
|
124
|
+
if (!matchGlob(match, pathPattern))
|
|
125
|
+
return match;
|
|
126
|
+
const token = `[REDACTED_${randomBytes(4).toString('hex')}]`;
|
|
127
|
+
unmaskMap.set(token, match);
|
|
128
|
+
return token;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** Mask content wrapped in @sensitive{...} annotations */
|
|
132
|
+
function maskSensitiveAnnotations(text, unmaskMap) {
|
|
133
|
+
return text.replace(/@sensitive\{([^}]+)\}/g, (_match, inner) => {
|
|
134
|
+
const token = `[REDACTED_${randomBytes(4).toString('hex')}]`;
|
|
135
|
+
unmaskMap.set(token, inner);
|
|
136
|
+
return token;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=privacy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/gateway/privacy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ProviderConfig, ProviderState, ProviderStatus, Tier, ChatRequest, SSEChunk, SSEChoiceDelta, ChatResponse } from './types.js';
|
|
2
|
+
export interface ChatResult {
|
|
3
|
+
response: ChatResponse;
|
|
4
|
+
durationMs: number;
|
|
5
|
+
}
|
|
6
|
+
export interface StreamChunk {
|
|
7
|
+
chunk: SSEChunk;
|
|
8
|
+
raw?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare abstract class BaseAdapter {
|
|
11
|
+
protected config: ProviderConfig;
|
|
12
|
+
constructor(config: ProviderConfig);
|
|
13
|
+
abstract chat(req: ChatRequest): Promise<ChatResult>;
|
|
14
|
+
abstract chatStream(req: ChatRequest): AsyncGenerator<StreamChunk>;
|
|
15
|
+
protected fetchWithTimeout(url: string, init: RequestInit): Promise<Response>;
|
|
16
|
+
protected makeSSEChunk(delta: SSEChoiceDelta, model: string): SSEChunk;
|
|
17
|
+
}
|
|
18
|
+
export declare class OpenAIAdapter extends BaseAdapter {
|
|
19
|
+
chat(req: ChatRequest): Promise<ChatResult>;
|
|
20
|
+
chatStream(req: ChatRequest): AsyncGenerator<StreamChunk>;
|
|
21
|
+
private headers;
|
|
22
|
+
private parseSSEStream;
|
|
23
|
+
}
|
|
24
|
+
export declare class AnthropicAdapter extends BaseAdapter {
|
|
25
|
+
chat(req: ChatRequest): Promise<ChatResult>;
|
|
26
|
+
chatStream(req: ChatRequest): AsyncGenerator<StreamChunk>;
|
|
27
|
+
private headers;
|
|
28
|
+
}
|
|
29
|
+
export declare class GeminiAdapter extends BaseAdapter {
|
|
30
|
+
chat(req: ChatRequest): Promise<ChatResult>;
|
|
31
|
+
chatStream(req: ChatRequest): AsyncGenerator<StreamChunk>;
|
|
32
|
+
private buildUrl;
|
|
33
|
+
}
|
|
34
|
+
export declare class ProviderRegistry {
|
|
35
|
+
private providers;
|
|
36
|
+
private adapters;
|
|
37
|
+
private roundRobinIndex;
|
|
38
|
+
addProvider(config: ProviderConfig): void;
|
|
39
|
+
removeProvider(id: string): void;
|
|
40
|
+
getAdapter(id: string): BaseAdapter | undefined;
|
|
41
|
+
getState(id: string): ProviderState | undefined;
|
|
42
|
+
/** Get all providers for a tier, ordered by round-robin */
|
|
43
|
+
getByTier(tier: Tier): ProviderConfig[];
|
|
44
|
+
/** Build a fallback chain: requested tier → lower tiers */
|
|
45
|
+
buildFallbackChain(startTier: Tier): ProviderConfig[];
|
|
46
|
+
/** Mark a provider as used, update stats */
|
|
47
|
+
recordUse(id: string, tokens: number): void;
|
|
48
|
+
/** Mark a provider failure */
|
|
49
|
+
recordFailure(id: string, error: string): void;
|
|
50
|
+
/** List all model IDs across all providers */
|
|
51
|
+
listModels(): Array<{
|
|
52
|
+
id: string;
|
|
53
|
+
provider: string;
|
|
54
|
+
tier: Tier;
|
|
55
|
+
}>;
|
|
56
|
+
/** Find which provider has a specific model */
|
|
57
|
+
findByModel(model: string): ProviderConfig | undefined;
|
|
58
|
+
/** Get health status for all providers */
|
|
59
|
+
getHealthStatus(): Array<{
|
|
60
|
+
id: string;
|
|
61
|
+
status: ProviderStatus;
|
|
62
|
+
tier: Tier;
|
|
63
|
+
requests: number;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/gateway/providers.ts"],"names":[],"mappings":"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"}
|