@relayplane/proxy 1.8.10 → 1.8.12

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.
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ /**
3
+ * Osmosis Phase 1 — KnowledgeAtom capture
4
+ *
5
+ * Stores per-request atoms in ~/.relayplane/osmosis.db (SQLite via better-sqlite3).
6
+ * Falls back to ~/.relayplane/osmosis.jsonl if SQLite is unavailable.
7
+ *
8
+ * All writes are fire-and-forget; errors are silently swallowed.
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.captureAtom = captureAtom;
45
+ exports._resetStore = _resetStore;
46
+ const fs = __importStar(require("node:fs"));
47
+ const os = __importStar(require("node:os"));
48
+ const path = __importStar(require("node:path"));
49
+ const SCHEMA_SQL = `
50
+ CREATE TABLE IF NOT EXISTS knowledge_atoms (
51
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
52
+ type TEXT NOT NULL,
53
+ model TEXT,
54
+ task_type TEXT,
55
+ latency_ms INTEGER,
56
+ input_tokens INTEGER,
57
+ output_tokens INTEGER,
58
+ error_type TEXT,
59
+ fallback_taken INTEGER,
60
+ timestamp INTEGER NOT NULL
61
+ );
62
+ `;
63
+ /** Lazy-initialised SQLite database handle, or null if unavailable. */
64
+ let _db = undefined;
65
+ let _jsonlPath = null;
66
+ let _insertStmt = null;
67
+ function getRelayplaneDir() {
68
+ // RELAYPLANE_HOME_OVERRIDE is used in tests to avoid writing to ~/.relayplane
69
+ const override = process.env['RELAYPLANE_HOME_OVERRIDE'];
70
+ const base = override ?? os.homedir();
71
+ return path.join(base, '.relayplane');
72
+ }
73
+ function ensureDir(dir) {
74
+ fs.mkdirSync(dir, { recursive: true });
75
+ }
76
+ function initDb() {
77
+ try {
78
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
79
+ const Database = require('better-sqlite3');
80
+ const dir = getRelayplaneDir();
81
+ ensureDir(dir);
82
+ const dbPath = path.join(dir, 'osmosis.db');
83
+ const db = new Database(dbPath);
84
+ db.pragma('journal_mode = WAL');
85
+ db.exec(SCHEMA_SQL);
86
+ return db;
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ }
92
+ function getDb() {
93
+ if (_db !== undefined)
94
+ return _db;
95
+ _db = initDb();
96
+ if (_db) {
97
+ _insertStmt = _db.prepare(`
98
+ INSERT INTO knowledge_atoms
99
+ (type, model, task_type, latency_ms, input_tokens, output_tokens, error_type, fallback_taken, timestamp)
100
+ VALUES
101
+ (@type, @model, @task_type, @latency_ms, @input_tokens, @output_tokens, @error_type, @fallback_taken, @timestamp)
102
+ `);
103
+ }
104
+ return _db;
105
+ }
106
+ function getJsonlPath() {
107
+ if (_jsonlPath)
108
+ return _jsonlPath;
109
+ const dir = getRelayplaneDir();
110
+ ensureDir(dir);
111
+ _jsonlPath = path.join(dir, 'osmosis.jsonl');
112
+ return _jsonlPath;
113
+ }
114
+ function writeToJsonl(atom) {
115
+ try {
116
+ fs.appendFileSync(getJsonlPath(), JSON.stringify(atom) + '\n', 'utf-8');
117
+ }
118
+ catch {
119
+ // best-effort
120
+ }
121
+ }
122
+ /**
123
+ * Capture a KnowledgeAtom (fire-and-forget).
124
+ * Never throws. Writes to SQLite; falls back to JSONL.
125
+ */
126
+ function captureAtom(atom) {
127
+ try {
128
+ const db = getDb();
129
+ if (db && _insertStmt) {
130
+ if (atom.type === 'success') {
131
+ _insertStmt.run({
132
+ type: atom.type,
133
+ model: atom.model ?? null,
134
+ task_type: atom.taskType ?? null,
135
+ latency_ms: atom.latencyMs,
136
+ input_tokens: atom.inputTokens,
137
+ output_tokens: atom.outputTokens,
138
+ error_type: null,
139
+ fallback_taken: null,
140
+ timestamp: atom.timestamp,
141
+ });
142
+ }
143
+ else {
144
+ _insertStmt.run({
145
+ type: atom.type,
146
+ model: atom.model ?? null,
147
+ task_type: null,
148
+ latency_ms: null,
149
+ input_tokens: null,
150
+ output_tokens: null,
151
+ error_type: atom.errorType ?? null,
152
+ fallback_taken: atom.fallbackTaken ? 1 : 0,
153
+ timestamp: atom.timestamp,
154
+ });
155
+ }
156
+ return;
157
+ }
158
+ // SQLite unavailable — fall back to JSONL
159
+ writeToJsonl(atom);
160
+ }
161
+ catch {
162
+ // best-effort fallback
163
+ try {
164
+ writeToJsonl(atom);
165
+ }
166
+ catch { /* ignore */ }
167
+ }
168
+ }
169
+ /** Exposed for testing — reset singleton state. */
170
+ function _resetStore() {
171
+ if (_db) {
172
+ try {
173
+ _db.close();
174
+ }
175
+ catch { /* ignore */ }
176
+ }
177
+ _db = undefined;
178
+ _insertStmt = null;
179
+ _jsonlPath = null;
180
+ }
181
+ //# sourceMappingURL=osmosis-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"osmosis-store.js","sourceRoot":"","sources":["../src/osmosis-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Mesh server routes for recovery atom sharing.
3
+ *
4
+ * Provides HTTP endpoints for proxy instances to contribute,
5
+ * query, and confirm recovery patterns via the mesh.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import { type Server } from 'node:http';
10
+ import { MeshRecoveryAtomStore } from './recovery-mesh.js';
11
+ export interface RecoveryMeshServerConfig {
12
+ /** Port to listen on */
13
+ port: number;
14
+ /** API keys that can write (empty = deny all writes unless allowUnauthenticated is true) */
15
+ writeKeys: string[];
16
+ /** API keys that can read (empty = no auth required) */
17
+ readKeys: string[];
18
+ /** Rate limit per key/IP per hour */
19
+ rateLimitPerHour: number;
20
+ /** Pattern expiry in days */
21
+ expiryDays: number;
22
+ /**
23
+ * When true and writeKeys is empty, allow unauthenticated writes.
24
+ * Requires explicit opt-in — empty writeKeys alone no longer grants open write access.
25
+ * /confirm always requires a writeKey regardless of this flag.
26
+ */
27
+ allowUnauthenticated?: boolean;
28
+ }
29
+ export declare const DEFAULT_RECOVERY_MESH_CONFIG: RecoveryMeshServerConfig;
30
+ export interface RecoveryMeshServerHandle {
31
+ store: MeshRecoveryAtomStore;
32
+ server: Server;
33
+ stop(): void;
34
+ }
35
+ /**
36
+ * Create an HTTP server that handles recovery atom mesh endpoints.
37
+ *
38
+ * Routes:
39
+ * POST /mesh/recovery/contribute — submit recovery atoms
40
+ * GET /mesh/recovery/atoms?since= — get recovery atoms (incremental)
41
+ * POST /mesh/recovery/confirm — report confirmation/denial (always requires writeKey)
42
+ * GET /mesh/recovery/stats — mesh recovery statistics
43
+ */
44
+ export declare function createRecoveryMeshServer(store: MeshRecoveryAtomStore, config: RecoveryMeshServerConfig): Server;
45
+ /**
46
+ * Start a recovery mesh server with a fresh store.
47
+ */
48
+ export declare function startRecoveryMeshServer(config?: Partial<RecoveryMeshServerConfig>): RecoveryMeshServerHandle;
49
+ //# sourceMappingURL=recovery-mesh-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-mesh-server.d.ts","sourceRoot":"","sources":["../src/recovery-mesh-server.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAA+E,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACrH,OAAO,EAEL,qBAAqB,EAGtB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,wBAAwB;IACvC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,qCAAqC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,eAAO,MAAM,4BAA4B,EAAE,wBAO1C,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,qBAAqB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,IAAI,CAAC;CACd;AAiGD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,wBAAwB,GAC/B,MAAM,CAiOR;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,GACzC,wBAAwB,CAY1B"}
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ /**
3
+ * Mesh server routes for recovery atom sharing.
4
+ *
5
+ * Provides HTTP endpoints for proxy instances to contribute,
6
+ * query, and confirm recovery patterns via the mesh.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.DEFAULT_RECOVERY_MESH_CONFIG = void 0;
12
+ exports.createRecoveryMeshServer = createRecoveryMeshServer;
13
+ exports.startRecoveryMeshServer = startRecoveryMeshServer;
14
+ const node_http_1 = require("node:http");
15
+ const recovery_mesh_js_1 = require("./recovery-mesh.js");
16
+ exports.DEFAULT_RECOVERY_MESH_CONFIG = {
17
+ port: 19600,
18
+ writeKeys: [],
19
+ readKeys: [],
20
+ rateLimitPerHour: 100,
21
+ expiryDays: 30,
22
+ allowUnauthenticated: false,
23
+ };
24
+ // ─── Constants ────────────────────────────────────────────────────────────────
25
+ /** Maximum request body size (1 MB) */
26
+ const MAX_BODY_BYTES = 1_048_576;
27
+ /** Maximum atoms allowed in a single /contribute request */
28
+ const MAX_ATOMS_PER_REQUEST = 100;
29
+ /** Maximum allowed value for count fields */
30
+ const MAX_COUNT = 10_000;
31
+ /** Valid atom type values */
32
+ const VALID_ATOM_TYPES = new Set([
33
+ 'auth-header',
34
+ 'model-rename',
35
+ 'timeout-tune',
36
+ 'provider-fallback',
37
+ ]);
38
+ /** Valid authHeader values */
39
+ const VALID_AUTH_HEADERS = new Set(['Authorization', 'x-api-key']);
40
+ /** Known provider names */
41
+ const KNOWN_PROVIDERS = new Set([
42
+ 'anthropic',
43
+ 'openai',
44
+ 'google',
45
+ 'azure',
46
+ 'openrouter',
47
+ 'cohere',
48
+ 'mistral',
49
+ 'bedrock',
50
+ 'vertex',
51
+ 'groq',
52
+ 'together',
53
+ 'fireworks',
54
+ 'perplexity',
55
+ 'deepseek',
56
+ 'xai',
57
+ 'meta',
58
+ 'ollama',
59
+ ]);
60
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
61
+ function json(res, status, data) {
62
+ res.writeHead(status, { 'Content-Type': 'application/json' });
63
+ res.end(JSON.stringify(data));
64
+ }
65
+ function extractKey(req, url) {
66
+ const authHeader = req.headers['authorization'];
67
+ if (authHeader?.startsWith('Bearer '))
68
+ return authHeader.slice(7);
69
+ return url.searchParams.get('key');
70
+ }
71
+ function makeRateLimiter() {
72
+ const buckets = new Map();
73
+ return function checkRateLimit(key, limit) {
74
+ const now = Date.now();
75
+ const bucket = buckets.get(key);
76
+ if (!bucket || now > bucket.resetAt) {
77
+ buckets.set(key, { count: 1, resetAt: now + 3600_000 });
78
+ return true;
79
+ }
80
+ if (bucket.count >= limit)
81
+ return false;
82
+ bucket.count++;
83
+ return true;
84
+ };
85
+ }
86
+ function readBody(req) {
87
+ return new Promise((resolve, reject) => {
88
+ const chunks = [];
89
+ let total = 0;
90
+ let oversized = false;
91
+ req.on('data', (c) => {
92
+ if (oversized)
93
+ return; // discard additional data without buffering
94
+ total += c.length;
95
+ if (total > MAX_BODY_BYTES) {
96
+ oversized = true;
97
+ chunks.length = 0; // free already-buffered chunks
98
+ return reject(new Error('Payload too large'));
99
+ }
100
+ chunks.push(c);
101
+ });
102
+ req.on('end', () => {
103
+ if (!oversized)
104
+ resolve(Buffer.concat(chunks).toString());
105
+ });
106
+ req.on('error', reject);
107
+ });
108
+ }
109
+ // ─── Server ───────────────────────────────────────────────────────────────────
110
+ /**
111
+ * Create an HTTP server that handles recovery atom mesh endpoints.
112
+ *
113
+ * Routes:
114
+ * POST /mesh/recovery/contribute — submit recovery atoms
115
+ * GET /mesh/recovery/atoms?since= — get recovery atoms (incremental)
116
+ * POST /mesh/recovery/confirm — report confirmation/denial (always requires writeKey)
117
+ * GET /mesh/recovery/stats — mesh recovery statistics
118
+ */
119
+ function createRecoveryMeshServer(store, config) {
120
+ // Per-server rate limiter (not module-level) to avoid cross-test interference
121
+ const checkRateLimit = makeRateLimiter();
122
+ const server = (0, node_http_1.createServer)(async (req, res) => {
123
+ const url = new URL(req.url ?? '/', `http://localhost:${config.port}`);
124
+ const path = url.pathname;
125
+ const method = req.method ?? 'GET';
126
+ try {
127
+ const apiKey = extractKey(req, url);
128
+ // ── Auth: /confirm always requires a valid writeKey (H3)
129
+ // Not exempted by allowUnauthenticated — prevents unauthenticated confidence flooding.
130
+ if (method === 'POST' && path === '/mesh/recovery/confirm') {
131
+ if (!apiKey || config.writeKeys.length === 0 || !config.writeKeys.includes(apiKey)) {
132
+ return json(res, 401, { error: 'Authentication required for confirmations' });
133
+ }
134
+ }
135
+ // ── Auth: all other POSTs (/contribute, etc.) (H1)
136
+ // allowUnauthenticated: true → skip key check (even if writeKeys is set)
137
+ else if (method === 'POST') {
138
+ if (config.allowUnauthenticated) {
139
+ // Explicit opt-in: open write access (e.g., private cluster deployments)
140
+ // /confirm is still always authenticated above
141
+ }
142
+ else if (config.writeKeys.length > 0) {
143
+ // writeKeys configured — validate the key
144
+ if (!apiKey || !config.writeKeys.includes(apiKey)) {
145
+ return json(res, 401, { error: 'Invalid or missing API key' });
146
+ }
147
+ }
148
+ else {
149
+ // No writeKeys and no explicit opt-in → deny-by-default (H1)
150
+ return json(res, 401, { error: 'Write access requires authentication' });
151
+ }
152
+ }
153
+ // ── Auth: reads (C2 rate limit applies before this for GETs? No — reads are separate)
154
+ if (method === 'GET' && config.readKeys.length > 0 && path !== '/mesh/recovery/stats') {
155
+ if (!apiKey || !config.readKeys.includes(apiKey)) {
156
+ return json(res, 401, { error: 'Invalid or missing API key' });
157
+ }
158
+ }
159
+ // ── Rate limiting for writes (C2: use IP when no API key)
160
+ if (method === 'POST') {
161
+ const rateLimitKey = apiKey ?? req.socket.remoteAddress ?? 'unknown';
162
+ if (!checkRateLimit(rateLimitKey, config.rateLimitPerHour)) {
163
+ return json(res, 429, { error: 'Rate limit exceeded' });
164
+ }
165
+ }
166
+ // POST /mesh/recovery/contribute — accept recovery atoms
167
+ if (method === 'POST' && path === '/mesh/recovery/contribute') {
168
+ let body;
169
+ try {
170
+ body = JSON.parse(await readBody(req));
171
+ }
172
+ catch (err) {
173
+ if (err.message === 'Payload too large') {
174
+ return json(res, 413, { error: 'Payload too large' });
175
+ }
176
+ return json(res, 400, { error: 'Invalid JSON' });
177
+ }
178
+ const rawAtoms = Array.isArray(body) ? body : [body];
179
+ // M3: Limit atoms per request
180
+ if (rawAtoms.length > MAX_ATOMS_PER_REQUEST) {
181
+ return json(res, 400, { error: `Too many atoms: max ${MAX_ATOMS_PER_REQUEST} per request` });
182
+ }
183
+ const results = [];
184
+ for (const atom of rawAtoms) {
185
+ // Validate required fields
186
+ if (!atom.type || !atom.provider || !atom.trigger || !atom.fix) {
187
+ results.push({ id: atom.id ?? 'unknown', status: 'rejected' });
188
+ continue;
189
+ }
190
+ // M3: Validate atom type against enum
191
+ if (!VALID_ATOM_TYPES.has(atom.type)) {
192
+ results.push({ id: atom.id ?? 'unknown', status: 'rejected' });
193
+ continue;
194
+ }
195
+ // M3: Validate errorCode is a number in valid HTTP range
196
+ const errorCode = atom.trigger?.errorCode;
197
+ if (typeof errorCode !== 'number' || errorCode < 100 || errorCode > 599) {
198
+ results.push({ id: atom.id ?? 'unknown', status: 'rejected' });
199
+ continue;
200
+ }
201
+ // M3: Validate authHeader if present
202
+ if (atom.fix?.authHeader && !VALID_AUTH_HEADERS.has(atom.fix.authHeader)) {
203
+ results.push({ id: atom.id ?? 'unknown', status: 'rejected' });
204
+ continue;
205
+ }
206
+ // M2: Strip unknown fix.provider
207
+ if (atom.fix?.provider && !KNOWN_PROVIDERS.has(atom.fix.provider)) {
208
+ delete atom.fix.provider;
209
+ }
210
+ // H2: Cap numeric fields
211
+ if (typeof atom.confirmCount === 'number') {
212
+ atom.confirmCount = Math.min(atom.confirmCount, MAX_COUNT);
213
+ }
214
+ if (typeof atom.denyCount === 'number') {
215
+ atom.denyCount = Math.min(atom.denyCount, MAX_COUNT);
216
+ }
217
+ if (typeof atom.reportCount === 'number') {
218
+ atom.reportCount = Math.min(atom.reportCount, MAX_COUNT);
219
+ }
220
+ // H2: Recalculate confidence from counts (don't trust client-provided value)
221
+ const totalAttempts = (atom.confirmCount ?? 0) + (atom.denyCount ?? 0);
222
+ atom.confidence = totalAttempts > 0 ? (atom.confirmCount ?? 0) / totalAttempts : 0;
223
+ // Ensure atom has an ID
224
+ if (!atom.id) {
225
+ atom.id = (0, recovery_mesh_js_1.recoveryAtomId)(atom.type, atom.provider, atom.trigger);
226
+ }
227
+ // Force recovery atom type
228
+ atom.atomType = 'recovery';
229
+ const existing = store.get(atom.id);
230
+ store.upsert(atom);
231
+ results.push({
232
+ id: atom.id,
233
+ status: existing ? 'merged' : 'created',
234
+ });
235
+ }
236
+ const accepted = results.filter(r => r.status !== 'rejected').length;
237
+ return json(res, 200, { accepted, results });
238
+ }
239
+ // GET /mesh/recovery/atoms?since=<ISO>
240
+ if (method === 'GET' && path === '/mesh/recovery/atoms') {
241
+ const since = url.searchParams.get('since');
242
+ const atoms = since ? store.getSince(since) : store.getAll();
243
+ return json(res, 200, atoms);
244
+ }
245
+ // POST /mesh/recovery/confirm — report confirmation or denial
246
+ if (method === 'POST' && path === '/mesh/recovery/confirm') {
247
+ let body;
248
+ try {
249
+ body = JSON.parse(await readBody(req));
250
+ }
251
+ catch (err) {
252
+ if (err.message === 'Payload too large') {
253
+ return json(res, 413, { error: 'Payload too large' });
254
+ }
255
+ return json(res, 400, { error: 'Invalid JSON' });
256
+ }
257
+ const { patternId, instanceHash, success } = body;
258
+ if (!patternId) {
259
+ return json(res, 400, { error: 'patternId is required' });
260
+ }
261
+ const atom = store.get(patternId);
262
+ if (!atom) {
263
+ return json(res, 404, { error: 'Pattern not found' });
264
+ }
265
+ if (success) {
266
+ store.recordConfirmation(patternId);
267
+ }
268
+ else {
269
+ store.recordDenial(patternId);
270
+ }
271
+ return json(res, 200, {
272
+ id: patternId,
273
+ confidence: store.get(patternId).confidence,
274
+ confirmCount: store.get(patternId).confirmCount,
275
+ denyCount: store.get(patternId).denyCount,
276
+ });
277
+ }
278
+ // GET /mesh/recovery/stats
279
+ if (method === 'GET' && path === '/mesh/recovery/stats') {
280
+ const stats = store.stats();
281
+ const all = store.getAll();
282
+ // Group by type
283
+ const byType = {};
284
+ const byProvider = {};
285
+ for (const atom of all) {
286
+ byType[atom.type] = (byType[atom.type] ?? 0) + 1;
287
+ byProvider[atom.provider] = (byProvider[atom.provider] ?? 0) + 1;
288
+ }
289
+ return json(res, 200, {
290
+ ...stats,
291
+ byType,
292
+ byProvider,
293
+ topPatterns: all
294
+ .sort((a, b) => b.reportCount - a.reportCount)
295
+ .slice(0, 10)
296
+ .map(a => ({
297
+ id: a.id,
298
+ type: a.type,
299
+ provider: a.provider,
300
+ confidence: a.confidence,
301
+ reportCount: a.reportCount,
302
+ confirmCount: a.confirmCount,
303
+ })),
304
+ });
305
+ }
306
+ json(res, 404, { error: 'Not found' });
307
+ }
308
+ catch (err) {
309
+ if (err.message === 'Payload too large') {
310
+ return json(res, 413, { error: 'Payload too large' });
311
+ }
312
+ json(res, 500, { error: err.message ?? 'Internal error' });
313
+ }
314
+ });
315
+ server.listen(config.port);
316
+ return server;
317
+ }
318
+ /**
319
+ * Start a recovery mesh server with a fresh store.
320
+ */
321
+ function startRecoveryMeshServer(config) {
322
+ const fullConfig = { ...exports.DEFAULT_RECOVERY_MESH_CONFIG, ...config };
323
+ const store = new recovery_mesh_js_1.MeshRecoveryAtomStore(fullConfig.expiryDays);
324
+ const server = createRecoveryMeshServer(store, fullConfig);
325
+ return {
326
+ store,
327
+ server,
328
+ stop() {
329
+ server.close();
330
+ },
331
+ };
332
+ }
333
+ //# sourceMappingURL=recovery-mesh-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-mesh-server.js","sourceRoot":"","sources":["../src/recovery-mesh-server.ts"],"names":[],"mappings":"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"}