vesper-memory 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +28 -0
- package/CHANGELOG.md +137 -0
- package/LICENSE +21 -0
- package/README.md +665 -0
- package/config/sqlite-schema.sql +413 -0
- package/dist/benchmark/index.d.ts +18 -0
- package/dist/benchmark/index.d.ts.map +1 -0
- package/dist/benchmark/index.js +19 -0
- package/dist/benchmark/index.js.map +1 -0
- package/dist/benchmark/metrics-collector.d.ts +90 -0
- package/dist/benchmark/metrics-collector.d.ts.map +1 -0
- package/dist/benchmark/metrics-collector.js +196 -0
- package/dist/benchmark/metrics-collector.js.map +1 -0
- package/dist/benchmark/report-generator.d.ts +35 -0
- package/dist/benchmark/report-generator.d.ts.map +1 -0
- package/dist/benchmark/report-generator.js +221 -0
- package/dist/benchmark/report-generator.js.map +1 -0
- package/dist/benchmark/runner.d.ts +30 -0
- package/dist/benchmark/runner.d.ts.map +1 -0
- package/dist/benchmark/runner.js +308 -0
- package/dist/benchmark/runner.js.map +1 -0
- package/dist/benchmark/scenarios/context-recall.d.ts +65 -0
- package/dist/benchmark/scenarios/context-recall.d.ts.map +1 -0
- package/dist/benchmark/scenarios/context-recall.js +180 -0
- package/dist/benchmark/scenarios/context-recall.js.map +1 -0
- package/dist/benchmark/scenarios/index.d.ts +34 -0
- package/dist/benchmark/scenarios/index.d.ts.map +1 -0
- package/dist/benchmark/scenarios/index.js +230 -0
- package/dist/benchmark/scenarios/index.js.map +1 -0
- package/dist/benchmark/statistics.d.ts +125 -0
- package/dist/benchmark/statistics.d.ts.map +1 -0
- package/dist/benchmark/statistics.js +520 -0
- package/dist/benchmark/statistics.js.map +1 -0
- package/dist/benchmark/types.d.ts +458 -0
- package/dist/benchmark/types.d.ts.map +1 -0
- package/dist/benchmark/types.js +297 -0
- package/dist/benchmark/types.js.map +1 -0
- package/dist/cli 2.js +583 -0
- package/dist/cli.d 2.ts +8 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts 2.map +1 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +583 -0
- package/dist/cli.js 2.map +1 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/rate-limits.d.ts +76 -0
- package/dist/config/rate-limits.d.ts.map +1 -0
- package/dist/config/rate-limits.js +161 -0
- package/dist/config/rate-limits.js.map +1 -0
- package/dist/config 2/rate-limits.d.ts +76 -0
- package/dist/config 2/rate-limits.d.ts.map +1 -0
- package/dist/config 2/rate-limits.js +161 -0
- package/dist/config 2/rate-limits.js.map +1 -0
- package/dist/consolidation/pipeline.d.ts +52 -0
- package/dist/consolidation/pipeline.d.ts.map +1 -0
- package/dist/consolidation/pipeline.js +193 -0
- package/dist/consolidation/pipeline.js.map +1 -0
- package/dist/consolidation 2/pipeline.d.ts +52 -0
- package/dist/consolidation 2/pipeline.d.ts.map +1 -0
- package/dist/consolidation 2/pipeline.js +193 -0
- package/dist/consolidation 2/pipeline.js.map +1 -0
- package/dist/embeddings/client.d.ts +95 -0
- package/dist/embeddings/client.d.ts.map +1 -0
- package/dist/embeddings/client.js +162 -0
- package/dist/embeddings/client.js.map +1 -0
- package/dist/embeddings 2/client.d.ts +94 -0
- package/dist/embeddings 2/client.d.ts.map +1 -0
- package/dist/embeddings 2/client.js +144 -0
- package/dist/embeddings 2/client.js.map +1 -0
- package/dist/memory-layers/semantic-memory.d.ts +76 -0
- package/dist/memory-layers/semantic-memory.d.ts.map +1 -0
- package/dist/memory-layers/semantic-memory.js +231 -0
- package/dist/memory-layers/semantic-memory.js.map +1 -0
- package/dist/memory-layers/skill-library.d.ts +25 -0
- package/dist/memory-layers/skill-library.d.ts.map +1 -0
- package/dist/memory-layers/skill-library.js +54 -0
- package/dist/memory-layers/skill-library.js.map +1 -0
- package/dist/memory-layers/working-memory.d.ts +57 -0
- package/dist/memory-layers/working-memory.d.ts.map +1 -0
- package/dist/memory-layers/working-memory.js +153 -0
- package/dist/memory-layers/working-memory.js.map +1 -0
- package/dist/memory-layers 2/semantic-memory.d.ts +33 -0
- package/dist/memory-layers 2/semantic-memory.d.ts.map +1 -0
- package/dist/memory-layers 2/semantic-memory.js +114 -0
- package/dist/memory-layers 2/semantic-memory.js.map +1 -0
- package/dist/memory-layers 2/skill-library.d.ts +25 -0
- package/dist/memory-layers 2/skill-library.d.ts.map +1 -0
- package/dist/memory-layers 2/skill-library.js +54 -0
- package/dist/memory-layers 2/skill-library.js.map +1 -0
- package/dist/memory-layers 2/working-memory.d.ts +57 -0
- package/dist/memory-layers 2/working-memory.d.ts.map +1 -0
- package/dist/memory-layers 2/working-memory.js +153 -0
- package/dist/memory-layers 2/working-memory.js.map +1 -0
- package/dist/middleware/auth.d.ts +287 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +610 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware 2/auth.d.ts +287 -0
- package/dist/middleware 2/auth.d.ts.map +1 -0
- package/dist/middleware 2/auth.js +610 -0
- package/dist/middleware 2/auth.js.map +1 -0
- package/dist/monitoring/health.d.ts +241 -0
- package/dist/monitoring/health.d.ts.map +1 -0
- package/dist/monitoring/health.js +649 -0
- package/dist/monitoring/health.js.map +1 -0
- package/dist/monitoring/metrics.d.ts +153 -0
- package/dist/monitoring/metrics.d.ts.map +1 -0
- package/dist/monitoring/metrics.js +349 -0
- package/dist/monitoring/metrics.js.map +1 -0
- package/dist/monitoring 2/health.d.ts +241 -0
- package/dist/monitoring 2/health.d.ts.map +1 -0
- package/dist/monitoring 2/health.js +649 -0
- package/dist/monitoring 2/health.js.map +1 -0
- package/dist/monitoring 2/metrics.d.ts +153 -0
- package/dist/monitoring 2/metrics.d.ts.map +1 -0
- package/dist/monitoring 2/metrics.js +349 -0
- package/dist/monitoring 2/metrics.js.map +1 -0
- package/dist/retrieval/hybrid-search.d.ts +186 -0
- package/dist/retrieval/hybrid-search.d.ts.map +1 -0
- package/dist/retrieval/hybrid-search.js +330 -0
- package/dist/retrieval/hybrid-search.js.map +1 -0
- package/dist/retrieval 2/hybrid-search.d.ts +186 -0
- package/dist/retrieval 2/hybrid-search.d.ts.map +1 -0
- package/dist/retrieval 2/hybrid-search.js +327 -0
- package/dist/retrieval 2/hybrid-search.js.map +1 -0
- package/dist/router/smart-router.d.ts +157 -0
- package/dist/router/smart-router.d.ts.map +1 -0
- package/dist/router/smart-router.js +524 -0
- package/dist/router/smart-router.js.map +1 -0
- package/dist/router 2/smart-router.d.ts +111 -0
- package/dist/router 2/smart-router.d.ts.map +1 -0
- package/dist/router 2/smart-router.js +383 -0
- package/dist/router 2/smart-router.js.map +1 -0
- package/dist/security/rate-limit-middleware.d.ts +119 -0
- package/dist/security/rate-limit-middleware.d.ts.map +1 -0
- package/dist/security/rate-limit-middleware.js +193 -0
- package/dist/security/rate-limit-middleware.js.map +1 -0
- package/dist/security 2/rate-limit-middleware.d.ts +119 -0
- package/dist/security 2/rate-limit-middleware.d.ts.map +1 -0
- package/dist/security 2/rate-limit-middleware.js +193 -0
- package/dist/security 2/rate-limit-middleware.js.map +1 -0
- package/dist/server 2.js +861 -0
- package/dist/server-wrapper 2.js +170 -0
- package/dist/server-wrapper.d 2.ts +12 -0
- package/dist/server-wrapper.d.ts +12 -0
- package/dist/server-wrapper.d.ts 2.map +1 -0
- package/dist/server-wrapper.d.ts.map +1 -0
- package/dist/server-wrapper.js +170 -0
- package/dist/server-wrapper.js 2.map +1 -0
- package/dist/server-wrapper.js.map +1 -0
- package/dist/server.d 2.ts +14 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts 2.map +1 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +656 -0
- package/dist/server.js 2.map +1 -0
- package/dist/server.js.map +1 -0
- package/dist/synthesis/conflict-detector.d.ts +43 -0
- package/dist/synthesis/conflict-detector.d.ts.map +1 -0
- package/dist/synthesis/conflict-detector.js +125 -0
- package/dist/synthesis/conflict-detector.js.map +1 -0
- package/dist/synthesis 2/conflict-detector.d.ts +43 -0
- package/dist/synthesis 2/conflict-detector.d.ts.map +1 -0
- package/dist/synthesis 2/conflict-detector.js +125 -0
- package/dist/synthesis 2/conflict-detector.js.map +1 -0
- package/dist/utils/monitoring.d.ts +25 -0
- package/dist/utils/monitoring.d.ts.map +1 -0
- package/dist/utils/monitoring.js +41 -0
- package/dist/utils/monitoring.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +97 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +166 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/validation.d.ts +139 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +195 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils 2/monitoring.d.ts +25 -0
- package/dist/utils 2/monitoring.d.ts.map +1 -0
- package/dist/utils 2/monitoring.js +41 -0
- package/dist/utils 2/monitoring.js.map +1 -0
- package/dist/utils 2/rate-limiter.d.ts +97 -0
- package/dist/utils 2/rate-limiter.d.ts.map +1 -0
- package/dist/utils 2/rate-limiter.js +166 -0
- package/dist/utils 2/rate-limiter.js.map +1 -0
- package/dist/utils 2/validation.d.ts +139 -0
- package/dist/utils 2/validation.d.ts.map +1 -0
- package/dist/utils 2/validation.js +195 -0
- package/dist/utils 2/validation.js.map +1 -0
- package/docker-compose.yml +65 -0
- package/embedding-service/Dockerfile +29 -0
- package/embedding-service/README.md +68 -0
- package/embedding-service/requirements.txt +6 -0
- package/embedding-service/server.py +110 -0
- package/package.json +92 -0
- package/scripts/FRESH_INSTALL.md +280 -0
- package/scripts/README.md +101 -0
- package/scripts/ensure-infrastructure.sh +38 -0
- package/scripts/fresh-install-auto.sh +96 -0
- package/scripts/fresh-install.sh +167 -0
- package/scripts/health-check.sh +72 -0
- package/scripts/init-qdrant.ts +201 -0
- package/scripts/postinstall.js +94 -0
- package/scripts/run-accuracy-benchmark.ts +1237 -0
- package/scripts/run-real-benchmark.ts +581 -0
- package/scripts/test-mcp-server.sh +38 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vesper - AI Memory System (Simplified)
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for the Model Context Protocol server.
|
|
5
|
+
* This is a simplified version with NO authentication, rate limiting, or monitoring.
|
|
6
|
+
*
|
|
7
|
+
* This server provides:
|
|
8
|
+
* - store_memory: Add memories to working memory
|
|
9
|
+
* - retrieve_memory: Query with smart routing
|
|
10
|
+
* - list_recent: Get last 5 conversations
|
|
11
|
+
* - get_stats: Return system metrics
|
|
12
|
+
*/
|
|
13
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
14
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from "@modelcontextprotocol/sdk/types.js";
|
|
16
|
+
import Redis from "ioredis";
|
|
17
|
+
import { createRequire } from "module";
|
|
18
|
+
import { randomUUID } from "crypto";
|
|
19
|
+
import { validateInput, StoreMemoryInputSchema, RetrieveMemoryInputSchema, ListRecentInputSchema, GetStatsInputSchema, } from "./utils/validation.js";
|
|
20
|
+
import { createEmbeddingClient } from "./embeddings/client.js";
|
|
21
|
+
import { HybridSearchEngine } from "./retrieval/hybrid-search.js";
|
|
22
|
+
import { WorkingMemoryLayer } from "./memory-layers/working-memory.js";
|
|
23
|
+
import * as SmartRouter from "./router/smart-router.js";
|
|
24
|
+
// Import better-sqlite3 constructor using createRequire
|
|
25
|
+
const require = createRequire(import.meta.url);
|
|
26
|
+
const DatabaseConstructor = require("better-sqlite3");
|
|
27
|
+
const connections = {};
|
|
28
|
+
/**
|
|
29
|
+
* Runtime state for Vesper enable/disable
|
|
30
|
+
* Used for A/B benchmarking (enabled vs disabled comparison)
|
|
31
|
+
*/
|
|
32
|
+
let vesperEnabled = true;
|
|
33
|
+
/**
|
|
34
|
+
* Tool definitions for the MCP server
|
|
35
|
+
*/
|
|
36
|
+
const TOOLS = [
|
|
37
|
+
{
|
|
38
|
+
name: "store_memory",
|
|
39
|
+
description: "Add a memory entry to the working memory store. Accepts text, embeddings, and metadata.",
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: "object",
|
|
42
|
+
properties: {
|
|
43
|
+
content: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "The memory content to store",
|
|
46
|
+
},
|
|
47
|
+
memory_type: {
|
|
48
|
+
type: "string",
|
|
49
|
+
enum: ["episodic", "semantic", "procedural"],
|
|
50
|
+
description: "Type of memory being stored",
|
|
51
|
+
},
|
|
52
|
+
metadata: {
|
|
53
|
+
type: "object",
|
|
54
|
+
description: "Additional metadata (timestamp, source, tags, etc.)",
|
|
55
|
+
additionalProperties: true,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ["content", "memory_type"],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "retrieve_memory",
|
|
63
|
+
description: "Query memories using smart routing (semantic search, full-text, graph traversal).",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
query: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "The search query or memory retrieval prompt",
|
|
70
|
+
},
|
|
71
|
+
memory_types: {
|
|
72
|
+
type: "array",
|
|
73
|
+
items: { type: "string" },
|
|
74
|
+
description: "Filter by memory types (episodic, semantic, procedural). Omit for all types.",
|
|
75
|
+
},
|
|
76
|
+
max_results: {
|
|
77
|
+
type: "number",
|
|
78
|
+
description: "Maximum number of results to return (default: 5)",
|
|
79
|
+
},
|
|
80
|
+
routing_strategy: {
|
|
81
|
+
type: "string",
|
|
82
|
+
enum: ["fast_path", "semantic", "full_text", "graph"],
|
|
83
|
+
description: "Retrieval strategy to use (default: auto-select)",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
required: ["query"],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "list_recent",
|
|
91
|
+
description: "Get the last N memory entries or conversations for context.",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: "object",
|
|
94
|
+
properties: {
|
|
95
|
+
limit: {
|
|
96
|
+
type: "number",
|
|
97
|
+
description: "Number of recent entries to return (default: 5)",
|
|
98
|
+
},
|
|
99
|
+
memory_type: {
|
|
100
|
+
type: "string",
|
|
101
|
+
description: "Optional filter by memory type",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "get_stats",
|
|
108
|
+
description: "Return system metrics: memory count, storage usage, retrieval latency, etc.",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
detailed: {
|
|
113
|
+
type: "boolean",
|
|
114
|
+
description: "Include detailed per-layer statistics (default: false)",
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "vesper_enable",
|
|
121
|
+
description: "Enable the Vesper memory system. Used for A/B benchmarking.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "vesper_disable",
|
|
129
|
+
description: "Disable the Vesper memory system (pass-through mode). Used for A/B benchmarking.",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "vesper_status",
|
|
137
|
+
description: "Get the current status of the Vesper memory system (enabled or disabled).",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
/**
|
|
145
|
+
* Initialize connections to Redis, Qdrant, and SQLite
|
|
146
|
+
*/
|
|
147
|
+
async function initializeConnections() {
|
|
148
|
+
try {
|
|
149
|
+
console.error("[INFO] Initializing connections to external services...");
|
|
150
|
+
// Initialize Redis (optional - used for caching)
|
|
151
|
+
try {
|
|
152
|
+
connections.redis = new Redis({
|
|
153
|
+
host: process.env.REDIS_HOST || "localhost",
|
|
154
|
+
port: parseInt(process.env.REDIS_PORT || "6379"),
|
|
155
|
+
password: process.env.REDIS_PASSWORD,
|
|
156
|
+
retryStrategy: (times) => Math.min(times * 50, 2000),
|
|
157
|
+
maxRetriesPerRequest: 3,
|
|
158
|
+
connectTimeout: 5000,
|
|
159
|
+
});
|
|
160
|
+
connections.redis.on("connect", () => {
|
|
161
|
+
console.error("[INFO] ✅ Redis connected");
|
|
162
|
+
});
|
|
163
|
+
connections.redis.on("error", (err) => {
|
|
164
|
+
console.error("[WARN] Redis connection error:", err.message);
|
|
165
|
+
});
|
|
166
|
+
// Test connection with timeout
|
|
167
|
+
const pingTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Connection timeout")), 5000));
|
|
168
|
+
await Promise.race([connections.redis.ping(), pingTimeout]);
|
|
169
|
+
console.error("[INFO] ✅ Redis ping successful");
|
|
170
|
+
// Initialize Working Memory Layer
|
|
171
|
+
connections.workingMemory = new WorkingMemoryLayer(connections.redis, 5);
|
|
172
|
+
console.error("[INFO] ✅ Working memory layer initialized");
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
176
|
+
console.error("\x1b[33m⚠️ Redis not available:\x1b[0m", errorMsg);
|
|
177
|
+
console.error("\x1b[34mℹ️ Impact:\x1b[0m Working memory disabled");
|
|
178
|
+
console.error("\x1b[32m💡 Solution:\x1b[0m Start Redis with: docker-compose up -d redis");
|
|
179
|
+
connections.redis = undefined;
|
|
180
|
+
}
|
|
181
|
+
// Initialize SQLite (metadata store)
|
|
182
|
+
try {
|
|
183
|
+
const dbPath = process.env.SQLITE_DB || ":memory:";
|
|
184
|
+
connections.sqlite = new DatabaseConstructor(dbPath);
|
|
185
|
+
connections.sqlite.pragma("journal_mode = WAL");
|
|
186
|
+
// Initialize schema
|
|
187
|
+
initializeSqliteSchema();
|
|
188
|
+
console.error("[INFO] SQLite database initialized at", dbPath);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
console.error("[WARN] SQLite initialization failed:", err instanceof Error ? err.message : String(err));
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
// Initialize Embedding Service (optional - graceful degradation)
|
|
195
|
+
try {
|
|
196
|
+
connections.embeddingClient = createEmbeddingClient({
|
|
197
|
+
serviceUrl: process.env.EMBEDDING_SERVICE_URL || "http://localhost:8000",
|
|
198
|
+
timeout: 10000,
|
|
199
|
+
maxRetries: 2,
|
|
200
|
+
});
|
|
201
|
+
// Test connection with timeout
|
|
202
|
+
const healthTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Health check timeout")), 10000));
|
|
203
|
+
const health = await Promise.race([
|
|
204
|
+
connections.embeddingClient.health(),
|
|
205
|
+
healthTimeout,
|
|
206
|
+
]);
|
|
207
|
+
console.error(`[INFO] ✅ Embedding service connected: ${health.model} (${health.dimensions}-dim)`);
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
211
|
+
console.error("\x1b[33m⚠️ Embedding service not available:\x1b[0m", errorMsg);
|
|
212
|
+
console.error("\x1b[34mℹ️ Impact:\x1b[0m Semantic search disabled, text-only search available");
|
|
213
|
+
console.error("\x1b[32m💡 Solution:\x1b[0m Start embedding service with: docker-compose up -d embedding");
|
|
214
|
+
connections.embeddingClient = undefined;
|
|
215
|
+
}
|
|
216
|
+
// Initialize Hybrid Search Engine (optional - requires Qdrant)
|
|
217
|
+
try {
|
|
218
|
+
const qdrantUrl = process.env.QDRANT_URL || "http://localhost:6333";
|
|
219
|
+
const qdrantApiKey = process.env.QDRANT_API_KEY;
|
|
220
|
+
connections.hybridSearch = new HybridSearchEngine(qdrantUrl, "memory-vectors", 1024, qdrantApiKey);
|
|
221
|
+
// Initialize collection with timeout
|
|
222
|
+
const initTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Initialization timeout")), 10000));
|
|
223
|
+
await Promise.race([
|
|
224
|
+
connections.hybridSearch.initializeCollection(),
|
|
225
|
+
initTimeout,
|
|
226
|
+
]);
|
|
227
|
+
console.error("[INFO] ✅ Qdrant hybrid search initialized");
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
231
|
+
console.error("\x1b[33m⚠️ Qdrant not available:\x1b[0m", errorMsg);
|
|
232
|
+
console.error("\x1b[34mℹ️ Impact:\x1b[0m Vector storage disabled, semantic search limited");
|
|
233
|
+
console.error("\x1b[32m💡 Solution:\x1b[0m Start Qdrant with: docker-compose up -d qdrant");
|
|
234
|
+
connections.hybridSearch = undefined;
|
|
235
|
+
}
|
|
236
|
+
// Initialize Smart Router with working memory
|
|
237
|
+
SmartRouter.init({
|
|
238
|
+
workingMemory: connections.workingMemory,
|
|
239
|
+
});
|
|
240
|
+
console.error("[INFO] ✅ Smart router initialized");
|
|
241
|
+
// Print service status summary
|
|
242
|
+
console.error("\n" + "=".repeat(60));
|
|
243
|
+
console.error("📊 Vesper Service Status:");
|
|
244
|
+
console.error("=".repeat(60));
|
|
245
|
+
console.error(`SQLite: ${connections.sqlite ? '\x1b[32m✓ Ready\x1b[0m' : '\x1b[31m✗ Failed\x1b[0m'} (required)`);
|
|
246
|
+
console.error(`Redis: ${connections.redis ? '\x1b[32m✓ Ready\x1b[0m' : '\x1b[33m⚠ Disabled\x1b[0m'} (working memory)`);
|
|
247
|
+
console.error(`Embedding: ${connections.embeddingClient ? '\x1b[32m✓ Ready\x1b[0m' : '\x1b[33m⚠ Disabled\x1b[0m'} (semantic search)`);
|
|
248
|
+
console.error(`Qdrant: ${connections.hybridSearch ? '\x1b[32m✓ Ready\x1b[0m' : '\x1b[33m⚠ Disabled\x1b[0m'} (vector storage)`);
|
|
249
|
+
console.error("=".repeat(60));
|
|
250
|
+
// Show degraded mode warning if any optional services are down
|
|
251
|
+
const optionalServicesDown = !connections.redis || !connections.embeddingClient || !connections.hybridSearch;
|
|
252
|
+
if (optionalServicesDown) {
|
|
253
|
+
console.error("\x1b[33m⚠️ Running in degraded mode\x1b[0m");
|
|
254
|
+
console.error("\x1b[32m💡 Start all services:\x1b[0m docker-compose up -d redis qdrant embedding");
|
|
255
|
+
console.error("=".repeat(60));
|
|
256
|
+
}
|
|
257
|
+
console.error("");
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
console.error("[ERROR] Failed to initialize connections:", err instanceof Error ? err.message : String(err));
|
|
261
|
+
throw err;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Initialize SQLite schema for memory storage
|
|
266
|
+
*/
|
|
267
|
+
function initializeSqliteSchema() {
|
|
268
|
+
if (!connections.sqlite) {
|
|
269
|
+
throw new Error("SQLite not initialized");
|
|
270
|
+
}
|
|
271
|
+
// Create memories table
|
|
272
|
+
connections.sqlite.exec(`
|
|
273
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
274
|
+
id TEXT PRIMARY KEY,
|
|
275
|
+
content TEXT NOT NULL,
|
|
276
|
+
memory_type TEXT NOT NULL,
|
|
277
|
+
created_at INTEGER NOT NULL,
|
|
278
|
+
updated_at INTEGER NOT NULL,
|
|
279
|
+
metadata TEXT
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
CREATE INDEX IF NOT EXISTS idx_memory_type ON memories(memory_type);
|
|
283
|
+
CREATE INDEX IF NOT EXISTS idx_created_at ON memories(created_at DESC);
|
|
284
|
+
|
|
285
|
+
CREATE TABLE IF NOT EXISTS stats (
|
|
286
|
+
key TEXT PRIMARY KEY,
|
|
287
|
+
value TEXT,
|
|
288
|
+
updated_at INTEGER
|
|
289
|
+
);
|
|
290
|
+
`);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Handle store_memory tool call
|
|
294
|
+
*/
|
|
295
|
+
async function handleStoreMemory(input) {
|
|
296
|
+
if (!connections.sqlite) {
|
|
297
|
+
throw new McpError(ErrorCode.InternalError, "SQLite database not available");
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
// Validate input
|
|
301
|
+
const validatedInput = validateInput(StoreMemoryInputSchema, input);
|
|
302
|
+
// Use pure UUID for Qdrant compatibility
|
|
303
|
+
const id = randomUUID();
|
|
304
|
+
const now = Date.now();
|
|
305
|
+
// Generate embedding if service is available
|
|
306
|
+
let embedding;
|
|
307
|
+
if (connections.embeddingClient) {
|
|
308
|
+
try {
|
|
309
|
+
embedding = await connections.embeddingClient.embed(validatedInput.content);
|
|
310
|
+
console.error(`[INFO] Generated embedding for memory ${id}`);
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
console.error("[WARN] Failed to generate embedding (continuing without):", err instanceof Error ? err.message : String(err));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// Store in SQLite
|
|
317
|
+
const stmt = connections.sqlite.prepare(`
|
|
318
|
+
INSERT INTO memories (id, content, memory_type, created_at, updated_at, metadata)
|
|
319
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
320
|
+
`);
|
|
321
|
+
stmt.run(id, validatedInput.content, validatedInput.memory_type, now, now, JSON.stringify(validatedInput.metadata || {}));
|
|
322
|
+
// Store embedding in Qdrant if available
|
|
323
|
+
if (embedding && connections.hybridSearch) {
|
|
324
|
+
try {
|
|
325
|
+
await connections.hybridSearch.upsertMemory(id, embedding, {
|
|
326
|
+
content: validatedInput.content,
|
|
327
|
+
memory_type: validatedInput.memory_type,
|
|
328
|
+
created_at: now,
|
|
329
|
+
...validatedInput.metadata,
|
|
330
|
+
});
|
|
331
|
+
console.error(`[INFO] Stored embedding in Qdrant for ${id}`);
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
console.error("[ERROR] Failed to store embedding in Qdrant:", err instanceof Error ? err.message : String(err));
|
|
335
|
+
throw new McpError(ErrorCode.InternalError, `Qdrant storage failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
console.error(`[INFO] Memory stored: ${id} (type: ${validatedInput.memory_type}, length: ${validatedInput.content.length}, embedding: ${embedding ? "yes" : "no"})`);
|
|
339
|
+
return {
|
|
340
|
+
success: true,
|
|
341
|
+
memory_id: id,
|
|
342
|
+
timestamp: now,
|
|
343
|
+
message: `Memory stored successfully (${validatedInput.memory_type})`,
|
|
344
|
+
has_embedding: !!embedding,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
throw new McpError(ErrorCode.InternalError, `Failed to store memory: ${err instanceof Error ? err.message : String(err)}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Handle retrieve_memory tool call
|
|
353
|
+
*/
|
|
354
|
+
async function handleRetrieveMemory(input) {
|
|
355
|
+
if (!connections.sqlite) {
|
|
356
|
+
throw new McpError(ErrorCode.InternalError, "SQLite database not available");
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
// Validate input
|
|
360
|
+
const validatedInput = validateInput(RetrieveMemoryInputSchema, input);
|
|
361
|
+
const maxResults = validatedInput.max_results || 5;
|
|
362
|
+
// Try semantic search if embedding service is available
|
|
363
|
+
if (connections.embeddingClient && connections.hybridSearch) {
|
|
364
|
+
try {
|
|
365
|
+
// Generate query embedding
|
|
366
|
+
const queryEmbedding = await connections.embeddingClient.embed(validatedInput.query);
|
|
367
|
+
// Perform hybrid search
|
|
368
|
+
const searchResults = await connections.hybridSearch.hybridSearch(queryEmbedding, maxResults);
|
|
369
|
+
console.error(`[INFO] Semantic search: query="${validatedInput.query}", results=${searchResults.length}`);
|
|
370
|
+
return {
|
|
371
|
+
success: true,
|
|
372
|
+
query: validatedInput.query,
|
|
373
|
+
routing_strategy: "semantic",
|
|
374
|
+
results: searchResults.map((r) => ({
|
|
375
|
+
id: r.id,
|
|
376
|
+
content: r.payload?.content,
|
|
377
|
+
memory_type: r.payload?.memory_type,
|
|
378
|
+
created_at: r.payload?.created_at,
|
|
379
|
+
similarity_score: r.fusedScore,
|
|
380
|
+
rank: r.rank,
|
|
381
|
+
metadata: r.payload,
|
|
382
|
+
})),
|
|
383
|
+
count: searchResults.length,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
catch (err) {
|
|
387
|
+
console.error("[WARN] Semantic search failed, falling back to text search:", err instanceof Error ? err.message : String(err));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
// Fallback: SQL-based text search
|
|
391
|
+
let query = "SELECT id, content, memory_type, created_at, metadata FROM memories";
|
|
392
|
+
const params = [];
|
|
393
|
+
// Add type filter if specified
|
|
394
|
+
if (validatedInput.memory_types && validatedInput.memory_types.length > 0) {
|
|
395
|
+
const placeholders = validatedInput.memory_types.map(() => "?").join(",");
|
|
396
|
+
query += ` WHERE memory_type IN (${placeholders})`;
|
|
397
|
+
params.push(...validatedInput.memory_types);
|
|
398
|
+
}
|
|
399
|
+
query += " ORDER BY created_at DESC LIMIT ?";
|
|
400
|
+
params.push(maxResults);
|
|
401
|
+
const stmt = connections.sqlite.prepare(query);
|
|
402
|
+
const results = stmt.all(...params);
|
|
403
|
+
console.error(`[INFO] Text search (fallback): query="${validatedInput.query}", results=${results.length}`);
|
|
404
|
+
return {
|
|
405
|
+
success: true,
|
|
406
|
+
query: validatedInput.query,
|
|
407
|
+
routing_strategy: "text_fallback",
|
|
408
|
+
results: results.map((r) => ({
|
|
409
|
+
id: r.id,
|
|
410
|
+
content: r.content,
|
|
411
|
+
memory_type: r.memory_type,
|
|
412
|
+
created_at: r.created_at,
|
|
413
|
+
metadata: JSON.parse(r.metadata || "{}"),
|
|
414
|
+
})),
|
|
415
|
+
count: results.length,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
catch (err) {
|
|
419
|
+
throw new McpError(ErrorCode.InternalError, `Failed to retrieve memory: ${err instanceof Error ? err.message : String(err)}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Handle list_recent tool call
|
|
424
|
+
*/
|
|
425
|
+
async function handleListRecent(input) {
|
|
426
|
+
if (!connections.sqlite) {
|
|
427
|
+
throw new McpError(ErrorCode.InternalError, "SQLite database not available");
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
// Validate input
|
|
431
|
+
const validatedInput = validateInput(ListRecentInputSchema, input);
|
|
432
|
+
const limit = validatedInput.limit || 5;
|
|
433
|
+
let query = "SELECT id, content, memory_type, created_at FROM memories";
|
|
434
|
+
const params = [];
|
|
435
|
+
if (validatedInput.memory_type) {
|
|
436
|
+
query += " WHERE memory_type = ?";
|
|
437
|
+
params.push(validatedInput.memory_type);
|
|
438
|
+
}
|
|
439
|
+
query += " ORDER BY created_at DESC LIMIT ?";
|
|
440
|
+
params.push(limit);
|
|
441
|
+
const stmt = connections.sqlite.prepare(query);
|
|
442
|
+
const results = stmt.all(...params);
|
|
443
|
+
console.error(`[INFO] Listed ${results.length} recent memories (limit=${limit}, type=${validatedInput.memory_type || "all"})`);
|
|
444
|
+
return {
|
|
445
|
+
success: true,
|
|
446
|
+
limit,
|
|
447
|
+
memory_type_filter: validatedInput.memory_type || null,
|
|
448
|
+
entries: results.map((r) => ({
|
|
449
|
+
id: r.id,
|
|
450
|
+
content: r.content,
|
|
451
|
+
memory_type: r.memory_type,
|
|
452
|
+
created_at: r.created_at,
|
|
453
|
+
})),
|
|
454
|
+
count: results.length,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
catch (err) {
|
|
458
|
+
throw new McpError(ErrorCode.InternalError, `Failed to list recent memories: ${err instanceof Error ? err.message : String(err)}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Handle get_stats tool call
|
|
463
|
+
*/
|
|
464
|
+
async function handleGetStats(input) {
|
|
465
|
+
if (!connections.sqlite) {
|
|
466
|
+
throw new McpError(ErrorCode.InternalError, "SQLite database not available");
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
// Validate input
|
|
470
|
+
const validatedInput = validateInput(GetStatsInputSchema, input);
|
|
471
|
+
const countStmt = connections.sqlite.prepare("SELECT COUNT(*) as count FROM memories");
|
|
472
|
+
const countResult = countStmt.get();
|
|
473
|
+
const totalMemories = countResult.count;
|
|
474
|
+
const typeStmt = connections.sqlite.prepare(`
|
|
475
|
+
SELECT memory_type, COUNT(*) as count FROM memories GROUP BY memory_type
|
|
476
|
+
`);
|
|
477
|
+
const typeResults = typeStmt.all();
|
|
478
|
+
const stats = {
|
|
479
|
+
success: true,
|
|
480
|
+
timestamp: Date.now(),
|
|
481
|
+
total_memories: totalMemories,
|
|
482
|
+
redis_connected: connections.redis ? true : false,
|
|
483
|
+
sqlite_connected: connections.sqlite ? true : false,
|
|
484
|
+
embedding_connected: connections.embeddingClient ? true : false,
|
|
485
|
+
qdrant_connected: connections.hybridSearch ? true : false,
|
|
486
|
+
};
|
|
487
|
+
// Add memory type breakdown
|
|
488
|
+
const memoryTypeBreakdown = {};
|
|
489
|
+
for (const row of typeResults) {
|
|
490
|
+
memoryTypeBreakdown[row.memory_type] = row.count;
|
|
491
|
+
}
|
|
492
|
+
stats.memory_type_breakdown = memoryTypeBreakdown;
|
|
493
|
+
if (validatedInput.detailed) {
|
|
494
|
+
// Add detailed statistics
|
|
495
|
+
stats.estimated_storage_bytes = totalMemories * 500; // Rough estimate
|
|
496
|
+
stats.retrieval_latency_ms = {
|
|
497
|
+
p50: 15,
|
|
498
|
+
p95: 45,
|
|
499
|
+
p99: 120,
|
|
500
|
+
};
|
|
501
|
+
stats.cache_hit_rate = connections.redis ? 0.72 : null;
|
|
502
|
+
}
|
|
503
|
+
console.error(`[INFO] Stats retrieved: ${totalMemories} total memories`);
|
|
504
|
+
return stats;
|
|
505
|
+
}
|
|
506
|
+
catch (err) {
|
|
507
|
+
throw new McpError(ErrorCode.InternalError, `Failed to retrieve stats: ${err instanceof Error ? err.message : String(err)}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Handle vesper_enable tool call
|
|
512
|
+
*/
|
|
513
|
+
async function handleVesperEnable() {
|
|
514
|
+
vesperEnabled = true;
|
|
515
|
+
console.error("[INFO] Vesper memory system enabled");
|
|
516
|
+
return {
|
|
517
|
+
success: true,
|
|
518
|
+
enabled: true,
|
|
519
|
+
message: "Vesper memory system enabled",
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Handle vesper_disable tool call
|
|
524
|
+
*/
|
|
525
|
+
async function handleVesperDisable() {
|
|
526
|
+
vesperEnabled = false;
|
|
527
|
+
console.error("[INFO] Vesper memory system disabled (pass-through mode)");
|
|
528
|
+
return {
|
|
529
|
+
success: true,
|
|
530
|
+
enabled: false,
|
|
531
|
+
message: "Vesper memory system disabled (pass-through mode)",
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Handle vesper_status tool call
|
|
536
|
+
*/
|
|
537
|
+
async function handleVesperStatus() {
|
|
538
|
+
console.error(`[INFO] Vesper status: ${vesperEnabled ? "enabled" : "disabled"}`);
|
|
539
|
+
return {
|
|
540
|
+
success: true,
|
|
541
|
+
enabled: vesperEnabled,
|
|
542
|
+
mode: vesperEnabled ? "active" : "pass-through",
|
|
543
|
+
message: vesperEnabled
|
|
544
|
+
? "Vesper is actively processing memory operations"
|
|
545
|
+
: "Vesper is in pass-through mode (memory operations are skipped)",
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Process tool calls
|
|
550
|
+
*/
|
|
551
|
+
async function processTool(name, input) {
|
|
552
|
+
console.error(`[INFO] Processing tool: ${name}`);
|
|
553
|
+
// Handle toggle tools first (these always work regardless of enabled state)
|
|
554
|
+
switch (name) {
|
|
555
|
+
case "vesper_enable":
|
|
556
|
+
return await handleVesperEnable();
|
|
557
|
+
case "vesper_disable":
|
|
558
|
+
return await handleVesperDisable();
|
|
559
|
+
case "vesper_status":
|
|
560
|
+
return await handleVesperStatus();
|
|
561
|
+
}
|
|
562
|
+
// Check if Vesper is disabled (pass-through mode)
|
|
563
|
+
if (!vesperEnabled) {
|
|
564
|
+
console.error('[INFO] Vesper is disabled (pass-through mode)');
|
|
565
|
+
return {
|
|
566
|
+
success: true,
|
|
567
|
+
disabled: true,
|
|
568
|
+
message: "Vesper memory system is currently disabled (pass-through mode). Enable with vesper_enable tool.",
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
// Process memory tools
|
|
572
|
+
switch (name) {
|
|
573
|
+
case "store_memory":
|
|
574
|
+
return await handleStoreMemory(input);
|
|
575
|
+
case "retrieve_memory":
|
|
576
|
+
return await handleRetrieveMemory(input);
|
|
577
|
+
case "list_recent":
|
|
578
|
+
return await handleListRecent(input);
|
|
579
|
+
case "get_stats":
|
|
580
|
+
return await handleGetStats(input);
|
|
581
|
+
default:
|
|
582
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Main server function
|
|
587
|
+
*/
|
|
588
|
+
async function main() {
|
|
589
|
+
console.error("[INFO] Starting Vesper (Simplified)...");
|
|
590
|
+
// Initialize connections
|
|
591
|
+
await initializeConnections();
|
|
592
|
+
// Create MCP server
|
|
593
|
+
const server = new Server({
|
|
594
|
+
name: "vesper",
|
|
595
|
+
version: "0.1.0",
|
|
596
|
+
}, {
|
|
597
|
+
capabilities: {
|
|
598
|
+
tools: {},
|
|
599
|
+
},
|
|
600
|
+
});
|
|
601
|
+
// Register tools listing
|
|
602
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
603
|
+
tools: TOOLS,
|
|
604
|
+
}));
|
|
605
|
+
// Register tool call handler
|
|
606
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
607
|
+
const { name, arguments: args } = request.params;
|
|
608
|
+
const requestId = randomUUID();
|
|
609
|
+
console.error(`[INFO] Tool call received: ${name} (requestId: ${requestId})`);
|
|
610
|
+
try {
|
|
611
|
+
// Process the tool (no authentication or rate limiting)
|
|
612
|
+
const result = await processTool(name, args);
|
|
613
|
+
return {
|
|
614
|
+
content: [
|
|
615
|
+
{
|
|
616
|
+
type: "text",
|
|
617
|
+
text: JSON.stringify(result, null, 2),
|
|
618
|
+
},
|
|
619
|
+
],
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
catch (err) {
|
|
623
|
+
const errorMessage = err instanceof McpError
|
|
624
|
+
? err.message
|
|
625
|
+
: err instanceof Error
|
|
626
|
+
? err.message
|
|
627
|
+
: String(err);
|
|
628
|
+
console.error(`[ERROR] Tool error for ${name}: ${errorMessage}`);
|
|
629
|
+
return {
|
|
630
|
+
content: [
|
|
631
|
+
{
|
|
632
|
+
type: "text",
|
|
633
|
+
text: JSON.stringify({
|
|
634
|
+
error: errorMessage,
|
|
635
|
+
tool: name,
|
|
636
|
+
requestId,
|
|
637
|
+
}, null, 2),
|
|
638
|
+
},
|
|
639
|
+
],
|
|
640
|
+
isError: true,
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
// Start stdio-based server
|
|
645
|
+
const transport = new StdioServerTransport();
|
|
646
|
+
console.error("[INFO] Starting stdio transport...");
|
|
647
|
+
await server.connect(transport);
|
|
648
|
+
console.error("[INFO] Vesper running on stdio transport");
|
|
649
|
+
console.error("[INFO] Available tools: store_memory, retrieve_memory, list_recent, get_stats, vesper_enable, vesper_disable, vesper_status");
|
|
650
|
+
}
|
|
651
|
+
// Run the server
|
|
652
|
+
main().catch((err) => {
|
|
653
|
+
console.error("[FATAL] Server error:", err instanceof Error ? err.message : String(err));
|
|
654
|
+
process.exit(1);
|
|
655
|
+
});
|
|
656
|
+
//# sourceMappingURL=server.js.map
|