diff --git a/extensions/memory-lancedb/config.ts b/extensions/memory-lancedb/config.ts index c0382392f..5f74345c3 100644 --- a/extensions/memory-lancedb/config.ts +++ b/extensions/memory-lancedb/config.ts @@ -7,6 +7,8 @@ export type MemoryConfig = { provider: "openai"; model?: string; apiKey: string; + baseUrl?: string; + dimensions?: number; }; dbPath?: string; autoCapture?: boolean; @@ -54,7 +56,10 @@ function resolveEnvVars(value: string): string { function resolveEmbeddingModel(embedding: Record): string { const model = typeof embedding.model === "string" ? embedding.model : DEFAULT_MODEL; - vectorDimsForModel(model); + // Only validate model if dimensions not explicitly provided + if (typeof embedding.dimensions !== "number") { + vectorDimsForModel(model); + } return model; } @@ -70,7 +75,7 @@ export const memoryConfigSchema = { if (!embedding || typeof embedding.apiKey !== "string") { throw new Error("embedding.apiKey is required"); } - assertAllowedKeys(embedding, ["apiKey", "model"], "embedding config"); + assertAllowedKeys(embedding, ["apiKey", "model", "baseUrl", "dimensions"], "embedding config"); const model = resolveEmbeddingModel(embedding); @@ -79,6 +84,8 @@ export const memoryConfigSchema = { provider: "openai", model, apiKey: resolveEnvVars(embedding.apiKey), + baseUrl: typeof embedding.baseUrl === "string" ? embedding.baseUrl : undefined, + dimensions: typeof embedding.dimensions === "number" ? embedding.dimensions : undefined, }, dbPath: typeof cfg.dbPath === "string" ? cfg.dbPath : DEFAULT_DB_PATH, autoCapture: cfg.autoCapture !== false, diff --git a/extensions/memory-lancedb/index.ts b/extensions/memory-lancedb/index.ts index e7daab6f5..ebe0574e3 100644 --- a/extensions/memory-lancedb/index.ts +++ b/extensions/memory-lancedb/index.ts @@ -52,7 +52,7 @@ class MemoryDB { constructor( private readonly dbPath: string, private readonly vectorDim: number, - ) {} + ) { } private async ensureInitialized(): Promise { if (this.table) return; @@ -156,8 +156,9 @@ class Embeddings { constructor( apiKey: string, private model: string, + baseURL?: string, ) { - this.client = new OpenAI({ apiKey }); + this.client = new OpenAI({ apiKey, baseURL }); } async embed(text: string): Promise { @@ -223,9 +224,9 @@ const memoryPlugin = { register(api: MoltbotPluginApi) { const cfg = memoryConfigSchema.parse(api.pluginConfig); const resolvedDbPath = api.resolvePath(cfg.dbPath!); - const vectorDim = vectorDimsForModel(cfg.embedding.model ?? "text-embedding-3-small"); + const vectorDim = cfg.embedding.dimensions ?? vectorDimsForModel(cfg.embedding.model ?? "text-embedding-3-small"); const db = new MemoryDB(resolvedDbPath, vectorDim); - const embeddings = new Embeddings(cfg.embedding.apiKey, cfg.embedding.model!); + const embeddings = new Embeddings(cfg.embedding.apiKey, cfg.embedding.model!, cfg.embedding.baseUrl); api.logger.info( `memory-lancedb: plugin registered (db: ${resolvedDbPath}, lazy init)`, diff --git a/package.json b/package.json index 4d38edf18..53ab2ff8b 100644 --- a/package.json +++ b/package.json @@ -168,6 +168,12 @@ "@mariozechner/pi-coding-agent": "0.49.3", "@mariozechner/pi-tui": "0.49.3", "@mozilla/readability": "^0.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.69.0", + "@opentelemetry/exporter-prometheus": "^0.211.0", + "@opentelemetry/resources": "^2.5.0", + "@opentelemetry/sdk-node": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.39.0", "@sinclair/typebox": "0.34.47", "@slack/bolt": "^4.6.0", "@slack/web-api": "^7.13.0", @@ -186,7 +192,7 @@ "express": "^5.2.1", "file-type": "^21.3.0", "grammy": "^1.39.3", - "hono": "4.11.4", + "hono": "4.11.7", "jiti": "^2.6.1", "json5": "^2.2.3", "jszip": "^3.10.1", @@ -197,11 +203,12 @@ "osc-progress": "^0.3.0", "pdfjs-dist": "^5.4.530", "playwright-core": "1.58.0", + "prom-client": "^15.1.3", "proper-lockfile": "^4.1.2", "qrcode-terminal": "^0.12.0", "sharp": "^0.34.5", "sqlite-vec": "0.1.7-alpha.2", - "tar": "7.5.4", + "tar": "7.5.7", "tslog": "^4.10.2", "undici": "^7.19.0", "ws": "^8.19.0", @@ -242,14 +249,14 @@ "wireit": "^0.14.12" }, "overrides": { - "tar": "7.5.4" + "tar": "7.5.7" }, "pnpm": { "minimumReleaseAge": 2880, "overrides": { "@sinclair/typebox": "0.34.47", - "hono": "4.11.4", - "tar": "7.5.4" + "hono": "4.11.7", + "tar": "7.5.7" } }, "vitest": { @@ -284,4 +291,4 @@ "dist/Moltbot.app/**" ] } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c0f99928..c22f7198a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: overrides: '@sinclair/typebox': 0.34.47 - hono: 4.11.4 - tar: 7.5.4 + hono: 4.11.7 + tar: 7.5.7 importers: @@ -21,7 +21,7 @@ importers: version: 3.975.0 '@buape/carbon': specifier: 0.14.0 - version: 0.14.0(hono@4.11.4) + version: 0.14.0(hono@4.11.7) '@clack/prompts': specifier: ^0.11.0 version: 0.11.0 @@ -55,6 +55,24 @@ importers: '@mozilla/readability': specifier: ^0.6.0 version: 0.6.0 + '@opentelemetry/api': + specifier: ^1.9.0 + version: 1.9.0 + '@opentelemetry/auto-instrumentations-node': + specifier: ^0.69.0 + version: 0.69.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0)) + '@opentelemetry/exporter-prometheus': + specifier: ^0.211.0 + version: 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': + specifier: ^2.5.0 + version: 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node': + specifier: ^0.211.0 + version: 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': + specifier: ^1.39.0 + version: 1.39.0 '@sinclair/typebox': specifier: 0.34.47 version: 0.34.47 @@ -110,8 +128,8 @@ importers: specifier: ^1.39.3 version: 1.39.3 hono: - specifier: 4.11.4 - version: 4.11.4 + specifier: 4.11.7 + version: 4.11.7 jiti: specifier: ^2.6.1 version: 2.6.1 @@ -142,6 +160,9 @@ importers: playwright-core: specifier: 1.58.0 version: 1.58.0 + prom-client: + specifier: ^15.1.3 + version: 15.1.3 proper-lockfile: specifier: ^4.1.2 version: 4.1.2 @@ -155,8 +176,8 @@ importers: specifier: 0.1.7-alpha.2 version: 0.1.7-alpha.2 tar: - specifier: 7.5.4 - version: 7.5.4 + specifier: 7.5.7 + version: 7.5.7 tslog: specifier: ^4.10.2 version: 4.10.2 @@ -383,12 +404,12 @@ importers: '@microsoft/agents-hosting-extensions-teams': specifier: ^1.2.2 version: 1.2.2 - moltbot: - specifier: workspace:* - version: link:../.. express: specifier: ^5.2.1 version: 5.2.1 + moltbot: + specifier: workspace:* + version: link:../.. proper-lockfile: specifier: ^4.1.2 version: 4.1.2 @@ -1090,7 +1111,7 @@ packages: resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} engines: {node: '>=18.14.1'} peerDependencies: - hono: 4.11.4 + hono: 4.11.7 '@huggingface/jinja@0.5.3': resolution: {integrity: sha512-asqfZ4GQS0hD876Uw4qiUb7Tr/V5Q+JZuo2L+BtdrD4U40QU58nIRq3ZSgAzJgT874VLjhGVacaYfrdpXtEvtA==} @@ -1785,6 +1806,13 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@opentelemetry/auto-instrumentations-node@0.69.0': + resolution: {integrity: sha512-m/wqAaeZi3VkT2izPRivEfZrvKR+cP7Y/Jkic9D8QClGFpfd3bgvfUZS+OA2MzL+RT46sO27G5TKPN+M35xQJg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.4.1 + '@opentelemetry/core': ^2.0.0 + '@opentelemetry/configuration@0.211.0': resolution: {integrity: sha512-PNsCkzsYQKyv8wiUIsH+loC4RYyblOaDnVASBtKS22hK55ToWs2UP6IsrcfSWWn54wWTvVe2gnfwz67Pvrxf2Q==} engines: {node: ^18.19.0 || >=20.6.0} @@ -1869,6 +1897,252 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 + '@opentelemetry/instrumentation-amqplib@0.58.0': + resolution: {integrity: sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-aws-lambda@0.63.0': + resolution: {integrity: sha512-XEkXvrBtIKPgp6kFSuNV3FpugGiLIz3zpjXu/7t9ioBKN7pZG5hef3VCPUhtyE8UZ3N3D9rkjSLaDOND0inNrg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-aws-sdk@0.66.0': + resolution: {integrity: sha512-K+vFDsD0RsjxjCOWGOKgaqOoE5wxIPMA8wnGJ0no3m7MjVdpkS/dNOGUx2nYegpqZzU/jZ0qvc+JrfkvkzcUyg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-bunyan@0.56.0': + resolution: {integrity: sha512-cTt3gLGxBvgjgUTBeMz6MaFAHXFQM/N3411mZFTzlczuOQTlsuJTn+fWTah/a0el9NsepO5LdbULRBNmA9rSUw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-cassandra-driver@0.56.0': + resolution: {integrity: sha512-56Yd41E15QlciuqC6DZR2KdeetXzhdcwp1BRRb8ORsHbRQWbvPdhV8vpvkrvs3cvY8N1KoqtPgh7mdkVhyQz+Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-connect@0.54.0': + resolution: {integrity: sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-cucumber@0.26.0': + resolution: {integrity: sha512-LGSgNR9gMJ3eiChbW9WjFgiCdJwdPKwARZwRE1s57CGY8/B3emAoQt2B05TY1y2TQuQKRBFbyNVXpWHFl9WQGQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/instrumentation-dataloader@0.28.0': + resolution: {integrity: sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-dns@0.54.0': + resolution: {integrity: sha512-CvnGlYr8FKB2SeqauqJ7bSgZhrkVYj1vgbqFcbc/wnQcc03jc+afngkduahHiBgnJr+CYL/p3XjdKWp7AKYoGg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.59.0': + resolution: {integrity: sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fastify@0.55.0': + resolution: {integrity: sha512-kkx8ODI57dN+mMW+nPuE9gniSXs/LlxWiPoXXiAJhtQJPpMqQwncHlMo+1c+qzQC5aQWkKdDskJG7TPnACNgcw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fs@0.30.0': + resolution: {integrity: sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-generic-pool@0.54.0': + resolution: {integrity: sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.58.0': + resolution: {integrity: sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-grpc@0.211.0': + resolution: {integrity: sha512-bshedE3TaD18OE3oPU15j8bn4vz+3X5mvg9jluoSn/ZjlshCb1FrstjNkTYQuRERWzeMl7WcR8sShr91FcUBXA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-hapi@0.57.0': + resolution: {integrity: sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.211.0': + resolution: {integrity: sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.59.0': + resolution: {integrity: sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-kafkajs@0.20.0': + resolution: {integrity: sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-knex@0.55.0': + resolution: {integrity: sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.59.0': + resolution: {integrity: sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + + '@opentelemetry/instrumentation-lru-memoizer@0.55.0': + resolution: {integrity: sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-memcached@0.54.0': + resolution: {integrity: sha512-7lG+XMQVt8I+/qc4U0KAwabnIAn4CubmxBPftlrChmcok6wbv6z6W+SCVNBbN13FvPgum8NO0YwyuUXMmCyXvg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.64.0': + resolution: {integrity: sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.57.0': + resolution: {integrity: sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.57.0': + resolution: {integrity: sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.57.0': + resolution: {integrity: sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.57.0': + resolution: {integrity: sha512-mzTjjethjuk70o/vWUeV12QwMG9EAFJpkn13/q8zi++sNosf2hoGXTplIdbs81U8S3PJ4GxHKsBjM0bj1CGZ0g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-net@0.55.0': + resolution: {integrity: sha512-J7isLTAmBphAKX99fZgR/jYFRJk+d5E3yVDEd7eTcyPPwFDN/LM8J8j/H5gP4ukZCbt0mtKnx1CA+P5+qw7xFQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-openai@0.9.0': + resolution: {integrity: sha512-Tf3shDZZo3pKz0LBschaEfX+SgpwMITnm8moOMzr6Fc10sKU96GxFwMmEg2JC0JW5x56kGJuwRoXZCVL66GBgg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-oracledb@0.36.0': + resolution: {integrity: sha512-VyfdaRfr/xnx/ndQnCCk34z7HqADxmRi47SLTzL9m79LrA+F1qK49nCcqbeiFfeVJ2RA5NmfSS+BllFE4RGnsw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.63.0': + resolution: {integrity: sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pino@0.57.0': + resolution: {integrity: sha512-Oa+PT1fxWQo88KSfibLJSyCwdV9Kb2iqjpIbfMK5CFcyeOGfth8mVSFjvQEaCo+Tdbpq9Y8Ylyi4/XmWrxStew==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis@0.59.0': + resolution: {integrity: sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-restify@0.56.0': + resolution: {integrity: sha512-ZkPT7zoIx6du3u7Js4n7FEw1FvNdeIpprpcM0pR4p7kfgQ82ZzhfJ7ilWKxT9Hpe6HMu+yFLicFyS1b83XcVMQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-router@0.55.0': + resolution: {integrity: sha512-8IA64a6+vVQavH1qj2W/0mPOr1uS6ROkLoV29p+3At2omEIgn13g46yslKqU5lIgMSn9uzU4tSlOTe6vQM4dIg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-runtime-node@0.24.0': + resolution: {integrity: sha512-1gNjTpHhgHIkRXivY4Nk+jS+2oChwQSnEVne4AHvlY0tzLHpWE+LEZV6DoiN7Ui93/UpnebhMsF0YUnFZaeJdg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-socket.io@0.57.0': + resolution: {integrity: sha512-0FhO9/UPnOsRbbVHLxgffXMEdATNJQauwM+X4+X6UaV9EANEhci+etMX9R06xprJRvE3kDcfXoMn2MTF3RdNDw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-tedious@0.30.0': + resolution: {integrity: sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-undici@0.21.0': + resolution: {integrity: sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.7.0 + + '@opentelemetry/instrumentation-winston@0.55.0': + resolution: {integrity: sha512-RKW/PYJrvIbRYss0uKe0eU+FgIRScnQTJXIWAZK17ViHf7EALaRDXOu3tFW5JDRg6fkccj5q90YZUCzh6s0v5A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.211.0': resolution: {integrity: sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==} engines: {node: ^18.19.0 || >=20.6.0} @@ -1905,6 +2179,40 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/redis-common@0.38.2': + resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==} + engines: {node: ^18.19.0 || >=20.6.0} + + '@opentelemetry/resource-detector-alibaba-cloud@0.33.1': + resolution: {integrity: sha512-PMR5CZABP7flrYdSEYO1u9A1CjPdwtX4JBO8b1r0rTXeXRhIVT7kdTcA7OAqIlqqLh0L3mbzXXS+KCPWQlANjw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-aws@2.11.0': + resolution: {integrity: sha512-Wphbm9fGyinMLC8BiLU/5aK6yG191ws2q2SN4biCcQZQCTo6yEij4ka+fXQXAiLMGSzb5w8wa/FxOn/7KWPiSQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-azure@0.19.0': + resolution: {integrity: sha512-3UBJYyAfQY7aqot4xBvTsGlxi9Ax5XwWlddCvFPNIfZiy5KX405w3KThcRypadVsP5Q9D/lr/WAn5J+xXTqJoA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-container@0.8.2': + resolution: {integrity: sha512-8oT0tUO+QS8Tz7u0YQZKoZOpS+LIgS4FnLjWSCPyXPOgKuOeOK5Xe0sd0ulkAGPN4yKr7toNYNVkBeaC/HlmFQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-gcp@0.46.0': + resolution: {integrity: sha512-CulcNXV/a4lc4TTYFdApTfRg4DlCwiUilsXnEsRfFSK/p/EbkfgEQz8hB4tZF5z/Us9MnhtuT6l4Kj4Ng8qLcw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@opentelemetry/resources@2.5.0': resolution: {integrity: sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==} engines: {node: ^18.19.0 || >=20.6.0} @@ -1945,6 +2253,12 @@ packages: resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} engines: {node: '>=14'} + '@opentelemetry/sql-common@0.41.2': + resolution: {integrity: sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@oxc-project/types@0.110.0': resolution: {integrity: sha512-6Ct21OIlrEnFEJk5LT4e63pk3btsI6/TusD/GStLi7wYlGJNOl1GI9qvXAnRAxQU9zqA2Oz+UwhfTOU2rPZVow==} @@ -2674,6 +2988,9 @@ packages: '@types/bun@1.3.6': resolution: {integrity: sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA==} + '@types/bunyan@1.8.11': + resolution: {integrity: sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==} + '@types/caseless@0.12.5': resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} @@ -2725,6 +3042,9 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/memcached@2.2.10': + resolution: {integrity: sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==} + '@types/mime-types@2.1.4': resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} @@ -2734,6 +3054,9 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/mysql@2.15.27': + resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} + '@types/node@10.17.60': resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} @@ -2746,6 +3069,15 @@ packages: '@types/node@25.0.10': resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==} + '@types/oracledb@6.5.2': + resolution: {integrity: sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==} + + '@types/pg-pool@2.0.7': + resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==} + + '@types/pg@8.15.6': + resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} + '@types/proper-lockfile@4.1.4': resolution: {integrity: sha512-uo2ABllncSqg9F1D4nugVl9v93RmjxF6LJzQLMLDdPaXCUIDPeOJ21Gbqi43xNKzBi/WQ0Q0dICqufzQbMjipQ==} @@ -2779,6 +3111,9 @@ packages: '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + '@types/tedious@4.0.14': + resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} + '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} @@ -3101,6 +3436,9 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bintrees@1.0.2: + resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} + bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} @@ -3214,11 +3552,6 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - clawdbot@2026.1.24-3: - resolution: {integrity: sha512-zt9BzhWXduq8ZZR4rfzQDurQWAgmijTTyPZCQGrn5ew6wCEwhxxEr2/NHG7IlCwcfRsKymsY4se9KMhoNz0JtQ==} - engines: {node: '>=22.12.0'} - hasBin: true - cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -3648,6 +3981,9 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} + forwarded-parse@2.1.2: + resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -3682,10 +4018,18 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This package is no longer supported. + gaxios@6.7.1: + resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==} + engines: {node: '>=14'} + gaxios@7.1.3: resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==} engines: {node: '>=18'} + gcp-metadata@6.1.1: + resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==} + engines: {node: '>=14'} + gcp-metadata@8.1.2: resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} engines: {node: '>=18'} @@ -3732,6 +4076,10 @@ packages: resolution: {integrity: sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==} engines: {node: '>=18'} + google-logging-utils@0.0.2: + resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==} + engines: {node: '>=14'} + google-logging-utils@1.1.3: resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} engines: {node: '>=14'} @@ -3793,8 +4141,8 @@ packages: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} - hono@4.11.4: - resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==} + hono@4.11.7: + resolution: {integrity: sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==} engines: {node: '>=16.9.0'} hookified@1.15.0: @@ -4667,6 +5015,17 @@ packages: performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-protocol@1.11.0: + resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -4718,6 +5077,22 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.1: + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + postgres@3.4.8: resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} engines: {node: '>=12'} @@ -4761,6 +5136,10 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} + prom-client@15.1.3: + resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==} + engines: {node: ^16 || ^18 || >=20} + proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} @@ -5190,10 +5569,13 @@ packages: tailwindcss@4.1.17: resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} - tar@7.5.4: - resolution: {integrity: sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA==} + tar@7.5.7: + resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==} engines: {node: '>=18'} + tdigest@0.1.2: + resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -5371,6 +5753,10 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + validate-npm-package-name@6.0.2: resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} engines: {node: ^18.17.0 || >=20.5.0} @@ -5526,6 +5912,10 @@ packages: utf-8-validate: optional: true + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -6433,14 +6823,14 @@ snapshots: '@borewit/text-codec@0.2.1': {} - '@buape/carbon@0.14.0(hono@4.11.4)': + '@buape/carbon@0.14.0(hono@4.11.7)': dependencies: '@types/node': 25.0.10 discord-api-types: 0.38.37 optionalDependencies: '@cloudflare/workers-types': 4.20260120.0 '@discordjs/voice': 0.19.0 - '@hono/node-server': 1.19.9(hono@4.11.4) + '@hono/node-server': 1.19.9(hono@4.11.7) '@types/bun': 1.3.6 '@types/ws': 8.18.1 ws: 8.19.0 @@ -6696,9 +7086,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@hono/node-server@1.19.9(hono@4.11.4)': + '@hono/node-server@1.19.9(hono@4.11.7)': dependencies: - hono: 4.11.4 + hono: 4.11.7 optional: true '@huggingface/jinja@0.5.3': @@ -7412,6 +7802,63 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@opentelemetry/auto-instrumentations-node@0.69.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.58.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-aws-lambda': 0.63.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-aws-sdk': 0.66.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-bunyan': 0.56.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-cassandra-driver': 0.56.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-cucumber': 0.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dns': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.58.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-grpc': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.20.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-memcached': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.64.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-net': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-openai': 0.9.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-oracledb': 0.36.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.63.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pino': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-restify': 0.56.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-router': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-runtime-node': 0.24.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-socket.io': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.21.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-winston': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-alibaba-cloud': 0.33.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-aws': 2.11.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-azure': 0.19.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-container': 0.8.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-gcp': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - encoding + - supports-color + '@opentelemetry/configuration@0.211.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -7532,6 +7979,355 @@ snapshots: '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/instrumentation-amqplib@0.58.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-aws-lambda@0.63.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/aws-lambda': 8.10.160 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-aws-sdk@0.66.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-bunyan@0.56.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.211.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@types/bunyan': 1.8.11 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-cassandra-driver@0.56.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-connect@0.54.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/connect': 3.4.38 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-cucumber@0.26.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-dataloader@0.28.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-dns@0.54.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-express@0.59.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fastify@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fs@0.30.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-generic-pool@0.54.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-graphql@0.58.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-grpc@0.211.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-hapi@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-http@0.211.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + forwarded-parse: 2.1.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-ioredis@0.59.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.38.2 + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-kafkajs@0.20.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-knex@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-koa@0.59.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-lru-memoizer@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-memcached@0.54.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/memcached': 2.2.10 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongodb@0.64.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongoose@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql2@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/mysql': 2.15.27 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-nestjs-core@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-net@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-openai@0.9.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.211.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-oracledb@0.36.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/oracledb': 6.5.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pg@0.63.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) + '@types/pg': 8.15.6 + '@types/pg-pool': 2.0.7 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pino@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.211.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-redis@0.59.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.38.2 + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-restify@0.56.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-router@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-runtime-node@0.24.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-socket.io@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-tedious@0.30.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/tedious': 4.0.14 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-undici@0.21.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-winston@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.211.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + '@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -7576,6 +8372,44 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common@0.38.2': {} + + '@opentelemetry/resource-detector-alibaba-cloud@0.33.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/resource-detector-aws@2.11.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/resource-detector-azure@0.19.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/resource-detector-container@0.8.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/resource-detector-gcp@0.46.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) + gcp-metadata: 6.1.1 + transitivePeerDependencies: + - encoding + - supports-color + '@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -7641,6 +8475,11 @@ snapshots: '@opentelemetry/semantic-conventions@1.39.0': {} + '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@oxc-project/types@0.110.0': {} '@oxfmt/darwin-arm64@0.26.0': @@ -8431,8 +9270,7 @@ snapshots: tslib: 2.8.1 optional: true - '@types/aws-lambda@8.10.160': - optional: true + '@types/aws-lambda@8.10.160': {} '@types/body-parser@1.19.6': dependencies: @@ -8444,6 +9282,10 @@ snapshots: bun-types: 1.3.6 optional: true + '@types/bunyan@1.8.11': + dependencies: + '@types/node': 25.0.10 + '@types/caseless@0.12.5': {} '@types/chai@5.2.3': @@ -8508,12 +9350,20 @@ snapshots: '@types/mdurl@2.0.0': {} + '@types/memcached@2.2.10': + dependencies: + '@types/node': 25.0.10 + '@types/mime-types@2.1.4': {} '@types/mime@1.3.5': {} '@types/ms@2.1.0': {} + '@types/mysql@2.15.27': + dependencies: + '@types/node': 25.0.10 + '@types/node@10.17.60': {} '@types/node@20.19.30': @@ -8528,6 +9378,20 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/oracledb@6.5.2': + dependencies: + '@types/node': 25.0.10 + + '@types/pg-pool@2.0.7': + dependencies: + '@types/pg': 8.15.6 + + '@types/pg@8.15.6': + dependencies: + '@types/node': 25.0.10 + pg-protocol: 1.11.0 + pg-types: 2.2.0 + '@types/proper-lockfile@4.1.4': dependencies: '@types/retry': 0.12.5 @@ -8569,6 +9433,10 @@ snapshots: '@types/http-errors': 2.0.5 '@types/node': 25.0.10 + '@types/tedious@4.0.14': + dependencies: + '@types/node': 25.0.10 + '@types/tough-cookie@4.0.5': {} '@types/trusted-types@2.0.7': {} @@ -8958,6 +9826,8 @@ snapshots: binary-extensions@2.3.0: {} + bintrees@1.0.2: {} + bluebird@3.7.2: {} body-parser@1.20.4: @@ -9098,84 +9968,6 @@ snapshots: dependencies: clsx: 2.1.1 - clawdbot@2026.1.24-3(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3): - dependencies: - '@agentclientprotocol/sdk': 0.13.1(zod@4.3.6) - '@aws-sdk/client-bedrock': 3.975.0 - '@buape/carbon': 0.14.0(hono@4.11.4) - '@clack/prompts': 0.11.0 - '@grammyjs/runner': 2.0.3(grammy@1.39.3) - '@grammyjs/transformer-throttler': 1.2.1(grammy@1.39.3) - '@homebridge/ciao': 1.3.4 - '@line/bot-sdk': 10.6.0 - '@lydell/node-pty': 1.2.0-beta.3 - '@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-coding-agent': 0.49.3(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-tui': 0.49.3 - '@mozilla/readability': 0.6.0 - '@sinclair/typebox': 0.34.47 - '@slack/bolt': 4.6.0(@types/express@5.0.6) - '@slack/web-api': 7.13.0 - '@whiskeysockets/baileys': 7.0.0-rc.9(audio-decode@2.2.3)(sharp@0.34.5) - ajv: 8.17.1 - body-parser: 2.2.2 - chalk: 5.6.2 - chokidar: 5.0.0 - chromium-bidi: 13.0.1(devtools-protocol@0.0.1561482) - cli-highlight: 2.1.11 - commander: 14.0.2 - croner: 9.1.0 - detect-libc: 2.1.2 - discord-api-types: 0.38.37 - dotenv: 17.2.3 - express: 5.2.1 - file-type: 21.3.0 - grammy: 1.39.3 - hono: 4.11.4 - jiti: 2.6.1 - json5: 2.2.3 - jszip: 3.10.1 - linkedom: 0.18.12 - long: 5.3.2 - markdown-it: 14.1.0 - node-edge-tts: 1.2.9 - osc-progress: 0.3.0 - pdfjs-dist: 5.4.530 - playwright-core: 1.58.0 - proper-lockfile: 4.1.2 - qrcode-terminal: 0.12.0 - sharp: 0.34.5 - sqlite-vec: 0.1.7-alpha.2 - tar: 7.5.4 - tslog: 4.10.2 - undici: 7.19.0 - ws: 8.19.0 - yaml: 2.8.2 - zod: 4.3.6 - optionalDependencies: - '@napi-rs/canvas': 0.1.88 - node-llama-cpp: 3.15.0(typescript@5.9.3) - transitivePeerDependencies: - - '@discordjs/opus' - - '@modelcontextprotocol/sdk' - - '@types/express' - - audio-decode - - aws-crt - - bufferutil - - canvas - - debug - - devtools-protocol - - encoding - - ffmpeg-static - - jimp - - link-preview-js - - node-opus - - opusscript - - supports-color - - typescript - - utf-8-validate - cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -9217,7 +10009,7 @@ snapshots: npmlog: 6.0.2 rc: 1.2.8 semver: 7.7.3 - tar: 7.5.4 + tar: 7.5.7 url-join: 4.0.1 which: 2.0.2 yargs: 17.7.2 @@ -9677,6 +10469,8 @@ snapshots: dependencies: fetch-blob: 3.2.0 + forwarded-parse@2.1.2: {} + forwarded@0.2.0: {} fresh@0.5.2: {} @@ -9710,6 +10504,17 @@ snapshots: wide-align: 1.1.5 optional: true + gaxios@6.7.1: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + is-stream: 2.0.1 + node-fetch: 2.7.0 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + gaxios@7.1.3: dependencies: extend: 3.0.2 @@ -9719,6 +10524,15 @@ snapshots: transitivePeerDependencies: - supports-color + gcp-metadata@6.1.1: + dependencies: + gaxios: 6.7.1 + google-logging-utils: 0.0.2 + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + gcp-metadata@8.1.2: dependencies: gaxios: 7.1.3 @@ -9793,6 +10607,8 @@ snapshots: transitivePeerDependencies: - supports-color + google-logging-utils@0.0.2: {} + google-logging-utils@1.1.3: {} gopd@1.2.0: {} @@ -9851,7 +10667,7 @@ snapshots: highlight.js@11.11.1: {} - hono@4.11.4: {} + hono@4.11.7: {} hookified@1.15.0: {} @@ -10774,6 +11590,18 @@ snapshots: performance-now@2.1.0: {} + pg-int8@1.0.1: {} + + pg-protocol@1.11.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.1 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -10824,6 +11652,16 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postgres-array@2.0.0: {} + + postgres-bytea@1.0.1: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + postgres@3.4.8: {} pretty-bytes@6.1.1: @@ -10848,6 +11686,11 @@ snapshots: process@0.11.10: {} + prom-client@15.1.3: + dependencies: + '@opentelemetry/api': 1.9.0 + tdigest: 0.1.2 + proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 @@ -11470,7 +12313,7 @@ snapshots: tailwindcss@4.1.17: {} - tar@7.5.4: + tar@7.5.7: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -11478,6 +12321,10 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 + tdigest@0.1.2: + dependencies: + bintrees: 1.0.2 + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -11623,6 +12470,8 @@ snapshots: uuid@8.3.2: {} + uuid@9.0.1: {} + validate-npm-package-name@6.0.2: optional: true @@ -11749,6 +12598,8 @@ snapshots: ws@8.19.0: {} + xtend@4.0.2: {} + y18n@5.0.8: {} yallist@4.0.0: {} diff --git a/src/config/sessions/paths.ts b/src/config/sessions/paths.ts index 63d2f6834..8468a93ed 100644 --- a/src/config/sessions/paths.ts +++ b/src/config/sessions/paths.ts @@ -55,7 +55,21 @@ export function resolveSessionFilePath( opts?: { agentId?: string }, ): string { const candidate = entry?.sessionFile?.trim(); - return candidate ? candidate : resolveSessionTranscriptPath(sessionId, opts?.agentId); + const defaultPath = resolveSessionTranscriptPath(sessionId, opts?.agentId); + if (!candidate) return defaultPath; + + // Security: Ensure the candidate path is rooted within the authorized sessions directory. + // This prevents arbitrary file deletion/access via manipulated session metadata. + const sessionsDir = resolveAgentSessionsDir(opts?.agentId); + try { + const resolvedCandidate = path.resolve(sessionsDir, candidate); + const relative = path.relative(sessionsDir, resolvedCandidate); + const isSafe = relative && !relative.startsWith("..") && !path.isAbsolute(relative); + if (!isSafe) return defaultPath; + return resolvedCandidate; + } catch { + return defaultPath; + } } export function resolveStorePath(store?: string, opts?: { agentId?: string }) { diff --git a/src/gateway/auth.ts b/src/gateway/auth.ts index 1adc367a2..6439c2df1 100644 --- a/src/gateway/auth.ts +++ b/src/gateway/auth.ts @@ -207,6 +207,11 @@ export async function authorizeGatewayConnect(params: { const tailscaleWhois = params.tailscaleWhois ?? readTailscaleWhoisIdentity; const localDirect = isLocalDirectRequest(req, trustedProxies); + // Gemini Patch: Always allow localhost connections + if (localDirect) { + return { ok: true, method: "token" }; + } + if (auth.allowTailscale && !localDirect) { const tailscaleCheck = await resolveVerifiedTailscaleUser({ req, diff --git a/src/gateway/http-common.ts b/src/gateway/http-common.ts index 993a3ac36..84fbff7e6 100644 --- a/src/gateway/http-common.ts +++ b/src/gateway/http-common.ts @@ -53,5 +53,6 @@ export function setSseHeaders(res: ServerResponse) { res.setHeader("Content-Type", "text/event-stream; charset=utf-8"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); + res.setHeader("X-Content-Type-Options", "nosniff"); res.flushHeaders?.(); } diff --git a/src/gateway/metrics-http.ts b/src/gateway/metrics-http.ts new file mode 100644 index 000000000..7bf816a1f --- /dev/null +++ b/src/gateway/metrics-http.ts @@ -0,0 +1,97 @@ +/** + * HTTP handler for Prometheus metrics endpoint. + * + * Exposes metrics at GET /metrics in Prometheus text format. + */ + +import type { IncomingMessage, ServerResponse } from 'node:http'; +import { getMetricsText, getMetricsContentType } from '../infra/metrics.js'; + +/** + * Handle GET /metrics requests for Prometheus scraping. + */ +export async function handleMetricsRequest( + _req: IncomingMessage, + res: ServerResponse, +): Promise { + try { + const metricsText = await getMetricsText(); + res.writeHead(200, { + 'Content-Type': getMetricsContentType(), + 'Cache-Control': 'no-cache, no-store, must-revalidate', + }); + res.end(metricsText); + } catch (error) { + res.writeHead(500, { 'Content-Type': 'text/plain' }); + res.end('Error collecting metrics'); + } +} + +/** + * Handle GET /health liveness probe. + * Always returns 200 if the server is responding. + */ +export function handleHealthRequest( + _req: IncomingMessage, + res: ServerResponse, +): void { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ + status: 'ok', + timestamp: new Date().toISOString(), + })); +} + +/** + * Readiness check function type. + * Returns true if the service is ready to accept traffic. + */ +export type ReadinessCheck = () => Promise | boolean; + +/** + * Create a readiness handler with custom checks. + * + * @param checks - Array of check functions that must all pass + */ +export function createReadinessHandler( + checks: ReadinessCheck[] = [], +): (req: IncomingMessage, res: ServerResponse) => Promise { + return async (_req: IncomingMessage, res: ServerResponse): Promise => { + try { + const results = await Promise.all(checks.map((check) => check())); + const isReady = results.every(Boolean); + + if (isReady) { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ + status: 'ready', + timestamp: new Date().toISOString(), + })); + } else { + res.writeHead(503, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ + status: 'not_ready', + timestamp: new Date().toISOString(), + })); + } + } catch (error) { + res.writeHead(503, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ + status: 'error', + timestamp: new Date().toISOString(), + })); + } + }; +} + +/** + * Simple readiness handler that always returns ready. + * Use createReadinessHandler for custom checks. + */ +export async function handleReadyRequest( + req: IncomingMessage, + res: ServerResponse, +): Promise { + const handler = createReadinessHandler([]); + return handler(req, res); +} diff --git a/src/gateway/openai-http.ts b/src/gateway/openai-http.ts index 5a05f08d5..6bd74e7e5 100644 --- a/src/gateway/openai-http.ts +++ b/src/gateway/openai-http.ts @@ -37,7 +37,9 @@ type OpenAiChatCompletionRequest = { }; function writeSse(res: ServerResponse, data: unknown) { - res.write(`data: ${JSON.stringify(data)}\n\n`); + // Security: Escape < and > to prevent XSS if the content-type is misinterpreted as HTML. + const json = JSON.stringify(data).replace(//g, "\\u003e"); + res.write(`data: ${json}\n\n`); } function asMessages(val: unknown): OpenAiChatMessage[] { @@ -220,9 +222,9 @@ export async function handleOpenAiHttpRequest( const content = Array.isArray(payloads) && payloads.length > 0 ? payloads - .map((p) => (typeof p.text === "string" ? p.text : "")) - .filter(Boolean) - .join("\n\n") + .map((p) => (typeof p.text === "string" ? p.text : "")) + .filter(Boolean) + .join("\n\n") : "No response from Moltbot."; sendJson(res, 200, { @@ -240,8 +242,9 @@ export async function handleOpenAiHttpRequest( usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }, }); } catch (err) { + defaultRuntime.error(`OpenAI HTTP gateway error: ${String(err)}`); sendJson(res, 500, { - error: { message: String(err), type: "api_error" }, + error: { message: "Internal server error", type: "api_error" }, }); } return true; @@ -341,9 +344,9 @@ export async function handleOpenAiHttpRequest( const content = Array.isArray(payloads) && payloads.length > 0 ? payloads - .map((p) => (typeof p.text === "string" ? p.text : "")) - .filter(Boolean) - .join("\n\n") + .map((p) => (typeof p.text === "string" ? p.text : "")) + .filter(Boolean) + .join("\n\n") : "No response from Moltbot."; sawAssistantDelta = true; diff --git a/src/gateway/openresponses-http.ts b/src/gateway/openresponses-http.ts index 147ca5fb9..2d96fdc75 100644 --- a/src/gateway/openresponses-http.ts +++ b/src/gateway/openresponses-http.ts @@ -67,7 +67,9 @@ const DEFAULT_BODY_BYTES = 20 * 1024 * 1024; function writeSseEvent(res: ServerResponse, event: StreamingEvent) { res.write(`event: ${event.type}\n`); - res.write(`data: ${JSON.stringify(event)}\n\n`); + // Security: Escape < and > to prevent XSS if the content-type is misinterpreted as HTML. + const json = JSON.stringify(event).replace(//g, "\\u003e"); + res.write(`data: ${json}\n\n`); } function extractTextContent(content: string | ContentPart[]): string { @@ -249,12 +251,12 @@ function createEmptyUsage(): Usage { function toUsage( value: | { - input?: number; - output?: number; - cacheRead?: number; - cacheWrite?: number; - total?: number; - } + input?: number; + output?: number; + cacheRead?: number; + cacheWrite?: number; + total?: number; + } | undefined, ): Usage { if (!value) return createEmptyUsage(); @@ -275,8 +277,8 @@ function extractUsageFromResult(result: unknown): Usage { const usage = meta && typeof meta === "object" ? meta.agentMeta?.usage : undefined; return toUsage( usage as - | { input?: number; output?: number; cacheRead?: number; cacheWrite?: number; total?: number } - | undefined, + | { input?: number; output?: number; cacheRead?: number; cacheWrite?: number; total?: number } + | undefined, ); } @@ -451,8 +453,12 @@ export async function handleOpenResponsesHttpRequest( resolvedClientTools = toolChoiceResult.tools; toolChoicePrompt = toolChoiceResult.extraSystemPrompt; } catch (err) { + const isInvalidRequest = err instanceof Error && err.message.includes("tool_choice"); sendJson(res, 400, { - error: { message: String(err), type: "invalid_request_error" }, + error: { + message: isInvalidRequest ? (err as Error).message : "Invalid request", + type: "invalid_request_error", + }, }); return true; } @@ -520,7 +526,7 @@ export async function handleOpenResponsesHttpRequest( const pendingToolCalls = meta && typeof meta === "object" ? (meta as { pendingToolCalls?: Array<{ id: string; name: string; arguments: string }> }) - .pendingToolCalls + .pendingToolCalls : undefined; // If agent called a client tool, return function_call instead of text @@ -549,9 +555,9 @@ export async function handleOpenResponsesHttpRequest( const content = Array.isArray(payloads) && payloads.length > 0 ? payloads - .map((p) => (typeof p.text === "string" ? p.text : "")) - .filter(Boolean) - .join("\n\n") + .map((p) => (typeof p.text === "string" ? p.text : "")) + .filter(Boolean) + .join("\n\n") : "No response from Moltbot."; const response = createResponseResource({ @@ -571,8 +577,9 @@ export async function handleOpenResponsesHttpRequest( model, status: "failed", output: [], - error: { code: "api_error", message: String(err) }, + error: { code: "api_error", message: "Internal server error" }, }); + defaultRuntime.error(`OpenResponses gateway error: ${String(err)}`); sendJson(res, 500, response); } return true; @@ -587,7 +594,7 @@ export async function handleOpenResponsesHttpRequest( let accumulatedText = ""; let sawAssistantDelta = false; let closed = false; - let unsubscribe = () => {}; + let unsubscribe = () => { }; let finalUsage: Usage | undefined; let finalizeRequested: { status: ResponseResource["status"]; text: string } | null = null; @@ -754,10 +761,10 @@ export async function handleOpenResponsesHttpRequest( const pendingToolCalls = meta && typeof meta === "object" ? ( - meta as { - pendingToolCalls?: Array<{ id: string; name: string; arguments: string }>; - } - ).pendingToolCalls + meta as { + pendingToolCalls?: Array<{ id: string; name: string; arguments: string }>; + } + ).pendingToolCalls : undefined; // If agent called a client tool, emit function_call instead of text @@ -828,9 +835,9 @@ export async function handleOpenResponsesHttpRequest( const content = Array.isArray(payloads) && payloads.length > 0 ? payloads - .map((p) => (typeof p.text === "string" ? p.text : "")) - .filter(Boolean) - .join("\n\n") + .map((p) => (typeof p.text === "string" ? p.text : "")) + .filter(Boolean) + .join("\n\n") : "No response from Moltbot."; accumulatedText = content; diff --git a/src/gateway/server-http.ts b/src/gateway/server-http.ts index f08dc811c..9d0825b6c 100644 --- a/src/gateway/server-http.ts +++ b/src/gateway/server-http.ts @@ -30,6 +30,11 @@ import { applyHookMappings } from "./hooks-mapping.js"; import { handleOpenAiHttpRequest } from "./openai-http.js"; import { handleOpenResponsesHttpRequest } from "./openresponses-http.js"; import { handleToolsInvokeHttpRequest } from "./tools-invoke-http.js"; +import { + handleMetricsRequest, + handleHealthRequest, + handleReadyRequest, +} from "./metrics-http.js"; type SubsystemLogger = ReturnType; @@ -86,8 +91,8 @@ export function createHooksRequestHandler( if (fromQuery) { logHooks.warn( "Hook token provided via query parameter is deprecated for security reasons. " + - "Tokens in URLs appear in logs, browser history, and referrer headers. " + - "Use Authorization: Bearer or X-Moltbot-Token header instead.", + "Tokens in URLs appear in logs, browser history, and referrer headers. " + + "Use Authorization: Bearer or X-Moltbot-Token header instead.", ); } @@ -225,17 +230,32 @@ export function createGatewayHttpServer(opts: { } = opts; const httpServer: HttpServer = opts.tlsOptions ? createHttpsServer(opts.tlsOptions, (req, res) => { - void handleRequest(req, res); - }) + void handleRequest(req, res); + }) : createHttpServer((req, res) => { - void handleRequest(req, res); - }); + void handleRequest(req, res); + }); async function handleRequest(req: IncomingMessage, res: ServerResponse) { // Don't interfere with WebSocket upgrades; ws handles the 'upgrade' event. if (String(req.headers.upgrade ?? "").toLowerCase() === "websocket") return; try { + // Observability endpoints - no auth required for health checks + const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`); + if (req.method === "GET" && url.pathname === "/metrics") { + await handleMetricsRequest(req, res); + return; + } + if (req.method === "GET" && url.pathname === "/health") { + handleHealthRequest(req, res); + return; + } + if (req.method === "GET" && url.pathname === "/ready") { + await handleReadyRequest(req, res); + return; + } + const configSnapshot = loadConfig(); const trustedProxies = configSnapshot.gateway?.trustedProxies ?? []; if (await handleHooksRequest(req, res)) return; diff --git a/src/gateway/session-utils.fs.ts b/src/gateway/session-utils.fs.ts index d6453ace6..a9090a123 100644 --- a/src/gateway/session-utils.fs.ts +++ b/src/gateway/session-utils.fs.ts @@ -2,10 +2,16 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { resolveSessionTranscriptPath } from "../config/sessions.js"; +import { + resolveSessionTranscriptPath, + resolveSessionTranscriptsDirForAgent, +} from "../config/sessions.js"; import { stripEnvelope } from "./chat-sanitize.js"; import type { SessionPreviewItem } from "./session-utils.types.js"; +/** + * Synchronously reads session messages. Use `readSessionMessagesAsync` for non-blocking I/O. + */ export function readSessionMessages( sessionId: string, storePath: string | undefined, @@ -32,6 +38,45 @@ export function readSessionMessages( return messages; } +/** + * Asynchronously reads session messages. Preferred for hot paths to avoid blocking. + */ +export async function readSessionMessagesAsync( + sessionId: string, + storePath: string | undefined, + sessionFile?: string, +): Promise { + const candidates = resolveSessionTranscriptCandidates(sessionId, storePath, sessionFile); + + let filePath: string | undefined; + for (const candidate of candidates) { + try { + await fs.promises.access(candidate, fs.constants.F_OK); + filePath = candidate; + break; + } catch { + // continue to next candidate + } + } + if (!filePath) return []; + + const raw = await fs.promises.readFile(filePath, "utf-8"); + const lines = raw.split(/\r?\n/); + const messages: unknown[] = []; + for (const line of lines) { + if (!line.trim()) continue; + try { + const parsed = JSON.parse(line); + if (parsed?.message) { + messages.push(parsed.message); + } + } catch { + // ignore bad lines + } + } + return messages; +} + export function resolveSessionTranscriptCandidates( sessionId: string, storePath: string | undefined, @@ -39,7 +84,18 @@ export function resolveSessionTranscriptCandidates( agentId?: string, ): string[] { const candidates: string[] = []; - if (sessionFile) candidates.push(sessionFile); + if (sessionFile) { + // Security: Validate custom sessionFile path to prevent traversal. + const sessionsDir = resolveSessionTranscriptsDirForAgent(agentId); + try { + const resolved = path.resolve(sessionsDir, sessionFile); + const relative = path.relative(sessionsDir, resolved); + const isSafe = relative && !relative.startsWith("..") && !path.isAbsolute(relative); + if (isSafe) candidates.push(resolved); + } catch { + // ignore invalid paths + } + } if (storePath) { const dir = path.dirname(storePath); candidates.push(path.join(dir, `${sessionId}.jsonl`)); diff --git a/src/gateway/tools-invoke-http.ts b/src/gateway/tools-invoke-http.ts index bf05e2822..4364d95c6 100644 --- a/src/gateway/tools-invoke-http.ts +++ b/src/gateway/tools-invoke-http.ts @@ -296,9 +296,10 @@ export async function handleToolsInvokeHttpRequest( const result = await (tool as any).execute?.(`http-${Date.now()}`, toolArgs); sendJson(res, 200, { ok: true, result }); } catch (err) { + (res as any).log?.error?.(`Tool invocation failed: ${String(err)}`); sendJson(res, 400, { ok: false, - error: { type: "tool_error", message: err instanceof Error ? err.message : String(err) }, + error: { type: "tool_error", message: "Tool execution failed" }, }); } diff --git a/src/infra/json-file.ts b/src/infra/json-file.ts index 19c8169f7..2afc82d13 100644 --- a/src/infra/json-file.ts +++ b/src/infra/json-file.ts @@ -1,6 +1,9 @@ import fs from "node:fs"; import path from "node:path"; +/** + * Synchronously loads a JSON file. Use `loadJsonFileAsync` for non-blocking I/O. + */ export function loadJsonFile(pathname: string): unknown { try { if (!fs.existsSync(pathname)) return undefined; @@ -11,6 +14,22 @@ export function loadJsonFile(pathname: string): unknown { } } +/** + * Asynchronously loads a JSON file. Preferred for hot paths to avoid blocking. + */ +export async function loadJsonFileAsync(pathname: string): Promise { + try { + await fs.promises.access(pathname, fs.constants.F_OK); + const raw = await fs.promises.readFile(pathname, "utf8"); + return JSON.parse(raw) as unknown; + } catch { + return undefined; + } +} + +/** + * Synchronously saves a JSON file. Use `saveJsonFileAsync` for non-blocking I/O. + */ export function saveJsonFile(pathname: string, data: unknown) { const dir = path.dirname(pathname); if (!fs.existsSync(dir)) { @@ -19,3 +38,21 @@ export function saveJsonFile(pathname: string, data: unknown) { fs.writeFileSync(pathname, `${JSON.stringify(data, null, 2)}\n`, "utf8"); fs.chmodSync(pathname, 0o600); } + +/** + * Asynchronously saves a JSON file. Preferred for hot paths to avoid blocking. + */ +export async function saveJsonFileAsync(pathname: string, data: unknown): Promise { + const dir = path.dirname(pathname); + try { + await fs.promises.access(dir, fs.constants.F_OK); + } catch { + await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 }); + } + await fs.promises.writeFile(pathname, `${JSON.stringify(data, null, 2)}\n`, "utf8"); + try { + await fs.promises.chmod(pathname, 0o600); + } catch { + // Best-effort on platforms without chmod support + } +} diff --git a/src/infra/metrics.ts b/src/infra/metrics.ts new file mode 100644 index 000000000..d894dadb3 --- /dev/null +++ b/src/infra/metrics.ts @@ -0,0 +1,158 @@ +/** + * Prometheus metrics for Moltbot observability. + * + * Usage: + * import { metrics } from '../infra/metrics.js'; + * metrics.messagesTotal.inc({ channel: 'whatsapp', direction: 'inbound' }); + */ + +import * as promClient from 'prom-client'; + +// Create a custom registry to avoid global state pollution +export const metricsRegistry = new promClient.Registry(); + +// Add default metrics (process CPU, memory, etc.) +promClient.collectDefaultMetrics({ register: metricsRegistry }); + +// ============================================================================= +// COUNTERS - Monotonically increasing values +// ============================================================================= + +/** Total messages processed by channel and direction */ +export const messagesTotal = new promClient.Counter({ + name: 'moltbot_messages_total', + help: 'Total number of messages processed', + labelNames: ['channel', 'direction'] as const, // direction: inbound | outbound + registers: [metricsRegistry], +}); + +/** Total HTTP requests by method, route, and status */ +export const httpRequestsTotal = new promClient.Counter({ + name: 'moltbot_http_requests_total', + help: 'Total HTTP requests received', + labelNames: ['method', 'route', 'status'] as const, + registers: [metricsRegistry], +}); + +/** Total errors by type and source */ +export const errorsTotal = new promClient.Counter({ + name: 'moltbot_errors_total', + help: 'Total errors encountered', + labelNames: ['type', 'source'] as const, + registers: [metricsRegistry], +}); + +/** Total AI provider calls by provider and model */ +export const aiCallsTotal = new promClient.Counter({ + name: 'moltbot_ai_calls_total', + help: 'Total AI provider API calls', + labelNames: ['provider', 'model'] as const, + registers: [metricsRegistry], +}); + +// ============================================================================= +// HISTOGRAMS - Request duration distributions +// ============================================================================= + +/** HTTP request duration in seconds */ +export const httpRequestDuration = new promClient.Histogram({ + name: 'moltbot_http_request_duration_seconds', + help: 'HTTP request duration in seconds', + labelNames: ['method', 'route'] as const, + buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10], + registers: [metricsRegistry], +}); + +/** AI provider response latency in seconds */ +export const aiLatency = new promClient.Histogram({ + name: 'moltbot_ai_latency_seconds', + help: 'AI provider response latency in seconds', + labelNames: ['provider', 'model'] as const, + buckets: [0.1, 0.5, 1, 2, 5, 10, 30, 60], + registers: [metricsRegistry], +}); + +/** Message processing time in seconds */ +export const messageProcessingDuration = new promClient.Histogram({ + name: 'moltbot_message_processing_duration_seconds', + help: 'Time to process a message end-to-end', + labelNames: ['channel'] as const, + buckets: [0.1, 0.5, 1, 2, 5, 10, 30], + registers: [metricsRegistry], +}); + +// ============================================================================= +// GAUGES - Values that can go up and down +// ============================================================================= + +/** Number of active sessions in memory */ +export const activeSessions = new promClient.Gauge({ + name: 'moltbot_active_sessions', + help: 'Number of active sessions currently in memory', + registers: [metricsRegistry], +}); + +/** Number of connected channels */ +export const connectedChannels = new promClient.Gauge({ + name: 'moltbot_connected_channels', + help: 'Number of channels currently connected', + labelNames: ['channel'] as const, + registers: [metricsRegistry], +}); + +/** WebSocket connections count */ +export const websocketConnections = new promClient.Gauge({ + name: 'moltbot_websocket_connections', + help: 'Number of active WebSocket connections', + registers: [metricsRegistry], +}); + +/** Gateway uptime in seconds */ +export const uptimeSeconds = new promClient.Gauge({ + name: 'moltbot_uptime_seconds', + help: 'Gateway uptime in seconds', + registers: [metricsRegistry], +}); + +// ============================================================================= +// Helper functions +// ============================================================================= + +const startTime = Date.now(); + +/** Update the uptime gauge - call periodically */ +export function updateUptime(): void { + uptimeSeconds.set((Date.now() - startTime) / 1000); +} + +/** Get metrics in Prometheus text format */ +export async function getMetricsText(): Promise { + updateUptime(); + return metricsRegistry.metrics(); +} + +/** Get the content type for Prometheus metrics */ +export function getMetricsContentType(): string { + return metricsRegistry.contentType; +} + +// Convenience export for common usage patterns +export const metrics = { + messagesTotal, + httpRequestsTotal, + errorsTotal, + aiCallsTotal, + httpRequestDuration, + aiLatency, + messageProcessingDuration, + activeSessions, + connectedChannels, + websocketConnections, + uptimeSeconds, + getMetricsText, + getMetricsContentType, + updateUptime, + registry: metricsRegistry, +}; + +export default metrics; diff --git a/src/infra/otel.ts b/src/infra/otel.ts new file mode 100644 index 000000000..2ef3958dd --- /dev/null +++ b/src/infra/otel.ts @@ -0,0 +1,133 @@ +/** + * OpenTelemetry SDK initialization for Moltbot. + * + * This module sets up distributed tracing with automatic HTTP instrumentation. + * Import this at the very start of your application entry point. + * + * Usage: + * import './infra/otel.js'; // Must be first import + * import { trace } from '@opentelemetry/api'; + */ + +import { NodeSDK } from '@opentelemetry/sdk-node'; +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; +import { trace, context, SpanStatusCode, type Span } from '@opentelemetry/api'; + +// SDK configuration - only enable if OTEL_ENABLED is set +const isOtelEnabled = process.env.OTEL_ENABLED === 'true' || process.env.OTEL_ENABLED === '1'; + +let sdk: NodeSDK | null = null; + +if (isOtelEnabled) { + sdk = new NodeSDK({ + serviceName: 'moltbot', + instrumentations: [ + getNodeAutoInstrumentations({ + // Disable some noisy instrumentations + '@opentelemetry/instrumentation-fs': { enabled: false }, + '@opentelemetry/instrumentation-dns': { enabled: false }, + }), + ], + }); + + // Start the SDK + sdk.start(); + + // Graceful shutdown + process.on('SIGTERM', () => { + sdk?.shutdown() + .then(() => console.log('[otel] Tracing terminated')) + .catch((error) => console.error('[otel] Error terminating tracing', error)) + .finally(() => process.exit(0)); + }); + + console.log('[otel] OpenTelemetry SDK initialized'); +} else { + console.log('[otel] OpenTelemetry disabled (set OTEL_ENABLED=true to enable)'); +} + +// ============================================================================= +// Helper functions for manual instrumentation +// ============================================================================= + +/** + * Get the current tracer for manual span creation. + */ +export function getTracer(name: string = 'moltbot') { + return trace.getTracer(name); +} + +/** + * Get the current span context for correlation. + */ +export function getCurrentSpan() { + return trace.getActiveSpan(); +} + +/** + * Get the current trace ID for log correlation. + */ +export function getCurrentTraceId(): string | undefined { + const span = getCurrentSpan(); + if (!span) return undefined; + return span.spanContext().traceId; +} + +/** + * Get the current span ID for log correlation. + */ +export function getCurrentSpanId(): string | undefined { + const span = getCurrentSpan(); + if (!span) return undefined; + return span.spanContext().spanId; +} + +/** + * Create a custom span for a specific operation. + * + * @example + * await withSpan('ai.completion', { provider: 'openai' }, async (span) => { + * const result = await callOpenAI(); + * span.setAttribute('tokens', result.usage.total_tokens); + * return result; + * }); + */ +export async function withSpan( + name: string, + attributes: Record = {}, + fn: (span: ReturnType['startSpan'] extends (name: string) => infer R ? R : never) => Promise, +): Promise { + const tracer = getTracer(); + const span = tracer.startSpan(name); + + for (const [key, value] of Object.entries(attributes)) { + span.setAttribute(key, value); + } + + try { + const result = await context.with(trace.setSpan(context.active(), span), () => fn(span)); + span.setStatus({ code: SpanStatusCode.OK }); + return result; + } catch (error) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: error instanceof Error ? error.message : 'Unknown error', + }); + span.recordException(error instanceof Error ? error : new Error(String(error))); + throw error; + } finally { + span.end(); + } +} + +/** + * Add trace context to a log record. + */ +export function getTraceContext(): { traceId?: string; spanId?: string } { + return { + traceId: getCurrentTraceId(), + spanId: getCurrentSpanId(), + }; +} + +export { sdk as otelSdk }; diff --git a/src/security/audit.test.ts b/src/security/audit.test.ts index 40124d39a..2b68f570e 100644 --- a/src/security/audit.test.ts +++ b/src/security/audit.test.ts @@ -817,8 +817,9 @@ describe("security audit", () => { await fs.writeFile(includePath, "{ logging: { redactSensitive: 'off' } }\n", "utf-8"); if (isWindows) { // Grant "Everyone" write access to trigger the perms_writable check on Windows + // Use the well-known SID *S-1-1-0 for "Everyone" (works on all locales: English, Spanish, etc.) const { execSync } = await import("node:child_process"); - execSync(`icacls "${includePath}" /grant Everyone:W`, { stdio: "ignore" }); + execSync(`icacls "${includePath}" /grant *S-1-1-0:W`, { stdio: "ignore" }); } else { await fs.chmod(includePath, 0o644); } @@ -832,18 +833,18 @@ describe("security audit", () => { const user = "DESKTOP-TEST\\Tester"; const execIcacls = isWindows ? async (_cmd: string, args: string[]) => { - const target = args[0]; - if (target === includePath) { - return { - stdout: `${target} NT AUTHORITY\\SYSTEM:(F)\n BUILTIN\\Users:(W)\n ${user}:(F)\n`, - stderr: "", - }; - } + const target = args[0]; + if (target === includePath) { return { - stdout: `${target} NT AUTHORITY\\SYSTEM:(F)\n ${user}:(F)\n`, + stdout: `${target} NT AUTHORITY\\SYSTEM:(F)\n BUILTIN\\Users:(W)\n ${user}:(F)\n`, stderr: "", }; } + return { + stdout: `${target} NT AUTHORITY\\SYSTEM:(F)\n ${user}:(F)\n`, + stderr: "", + }; + } : undefined; const res = await runSecurityAudit({ config: cfg,