From 76b946b16da53c95d7aff9e0af4924869be14ccb Mon Sep 17 00:00:00 2001 From: javicasper Date: Wed, 28 Jan 2026 17:01:20 +0100 Subject: [PATCH 1/2] feat: add embeddings system with semantic search and MCP server - Semantic search over CLAUDE.md, AGENTS.md, and SKILL.md files - Local embeddings with Transformers.js (all-MiniLM-L6-v2, 22MB) - SQLite storage with memory retention system (LRU + importance) - MCP Server for Claude Code integration - CLI commands: index, query, sync, memory, forget - 34 tests with Vitest - CI/CD with GitHub Actions for automated npm releases Install with: claude mcp add memory-forge -- npx -y @memory-forge/embeddings Co-Authored-By: Claude --- .../memory-forge/resources/skill-template.md | 1 + .github/workflows/ci.yml | 72 + .github/workflows/release.yml | 97 + .gitignore | 25 + .../memory-forge/resources/skill-template.md | 1 + CLAUDE.md | 54 + tools/embeddings/.mcp.json.example | 8 + tools/embeddings/README.md | 372 ++ tools/embeddings/package-lock.json | 3709 +++++++++++++++++ tools/embeddings/package.json | 49 + tools/embeddings/src/chunker.ts | 390 ++ tools/embeddings/src/db.ts | 519 +++ tools/embeddings/src/embeddings.ts | 131 + tools/embeddings/src/index.ts | 343 ++ tools/embeddings/src/mcp-server.ts | 310 ++ tools/embeddings/src/search.ts | 202 + tools/embeddings/src/sync.ts | 254 ++ tools/embeddings/src/types.ts | 70 + tools/embeddings/tests/chunker.test.ts | 106 + tools/embeddings/tests/db.test.ts | 207 + tools/embeddings/tests/embeddings.test.ts | 115 + .../tests/fixtures/sample-claude.md | 26 + .../embeddings/tests/fixtures/sample-skill.md | 52 + tools/embeddings/tests/search.test.ts | 174 + tools/embeddings/tsconfig.json | 20 + tools/embeddings/vitest.config.ts | 16 + 26 files changed, 7323 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 tools/embeddings/.mcp.json.example create mode 100644 tools/embeddings/README.md create mode 100644 tools/embeddings/package-lock.json create mode 100644 tools/embeddings/package.json create mode 100644 tools/embeddings/src/chunker.ts create mode 100644 tools/embeddings/src/db.ts create mode 100644 tools/embeddings/src/embeddings.ts create mode 100644 tools/embeddings/src/index.ts create mode 100644 tools/embeddings/src/mcp-server.ts create mode 100644 tools/embeddings/src/search.ts create mode 100644 tools/embeddings/src/sync.ts create mode 100644 tools/embeddings/src/types.ts create mode 100644 tools/embeddings/tests/chunker.test.ts create mode 100644 tools/embeddings/tests/db.test.ts create mode 100644 tools/embeddings/tests/embeddings.test.ts create mode 100644 tools/embeddings/tests/fixtures/sample-claude.md create mode 100644 tools/embeddings/tests/fixtures/sample-skill.md create mode 100644 tools/embeddings/tests/search.test.ts create mode 100644 tools/embeddings/tsconfig.json create mode 100644 tools/embeddings/vitest.config.ts diff --git a/.claude/skills/memory-forge/resources/skill-template.md b/.claude/skills/memory-forge/resources/skill-template.md index 631facd..f362a18 100644 --- a/.claude/skills/memory-forge/resources/skill-template.md +++ b/.claude/skills/memory-forge/resources/skill-template.md @@ -9,6 +9,7 @@ description: | author: Memory Forge version: 1.0.0 date: YYYY-MM-DD +importance: [1-10, where 10=critical knowledge that should never be forgotten, 5=useful but forgettable, 1=ephemeral/temporary] --- # [Human-Readable Title] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f2f9b55 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI + +on: + push: + branches: [main, master] + paths: + - 'tools/embeddings/**' + - '.github/workflows/ci.yml' + pull_request: + branches: [main, master] + paths: + - 'tools/embeddings/**' + - '.github/workflows/ci.yml' + +jobs: + test: + name: Test + runs-on: ubuntu-latest + + defaults: + run: + working-directory: tools/embeddings + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: tools/embeddings/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Run tests + run: npm test + + build: + name: Build Check + runs-on: ubuntu-latest + + defaults: + run: + working-directory: tools/embeddings + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: tools/embeddings/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Verify MCP server starts + run: | + timeout 5 node dist/mcp-server.js < /dev/null || true + echo "MCP server binary OK" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ece7b07 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,97 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + test: + name: Test before release + runs-on: ubuntu-latest + + defaults: + run: + working-directory: tools/embeddings + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: tools/embeddings/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Run tests + run: npm test + + publish: + name: Publish to npm + needs: test + runs-on: ubuntu-latest + + defaults: + run: + working-directory: tools/embeddings + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + cache: 'npm' + cache-dependency-path: tools/embeddings/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Update package.json version + run: npm version ${{ steps.version.outputs.VERSION }} --no-git-tag-version + + - name: Publish to npm + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + github-release: + name: Create GitHub Release + needs: publish + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + name: v${{ steps.version.outputs.VERSION }} + generate_release_notes: true + draft: false + prerelease: ${{ contains(steps.version.outputs.VERSION, '-') }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8828078 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Memory Forge index +.memory-forge/ + +# Dependencies +node_modules/ + +# Build output +dist/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Test coverage +coverage/ + +# Logs +*.log +npm-debug.log* diff --git a/.opencode/skill/memory-forge/resources/skill-template.md b/.opencode/skill/memory-forge/resources/skill-template.md index 631facd..f362a18 100644 --- a/.opencode/skill/memory-forge/resources/skill-template.md +++ b/.opencode/skill/memory-forge/resources/skill-template.md @@ -9,6 +9,7 @@ description: | author: Memory Forge version: 1.0.0 date: YYYY-MM-DD +importance: [1-10, where 10=critical knowledge that should never be forgotten, 5=useful but forgettable, 1=ephemeral/temporary] --- # [Human-Readable Title] diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f51f49b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,54 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Memory Forge is a **knowledge system**, not a tool. It's a skill (set of instructions in SKILL.md) that teaches AI coding agents how to: +- Recognize when valuable knowledge has been discovered during work sessions +- Decide where knowledge belongs (CLAUDE.md, AGENTS.md, or a new skill) +- Route knowledge correctly in monorepos +- Format knowledge for maximum future retrieval + +## Repository Structure + +``` +.claude/skills/memory-forge/ +├── SKILL.md # The main skill - core decision framework +└── resources/ + └── skill-template.md # Template for creating new skills + +.opencode/skill/memory-forge/ # Mirror for OpenCode compatibility + +examples/ +├── monorepo/ # Example monorepo with distributed CLAUDE.md files +└── single-service/ # Example single-service setup +``` + +## Key Files + +- **`.claude/skills/memory-forge/SKILL.md`**: The core skill containing the decision framework, activation triggers, and knowledge extraction process +- **`.opencode/skill/memory-forge/SKILL.md`**: Identical copy for OpenCode compatibility (keep in sync) + +## Development Guidelines + +This project is documentation-focused. Contributions should improve: +- Decision heuristics for skills vs docs +- Routing logic for monorepos +- Examples of knowledge extraction +- The skill template clarity + +Avoid adding complex tooling or CLI-specific features. + +## CLI Compatibility + +Memory Forge supports multiple AI coding tools through the Agent Skills standard: + +| Tool | Context File | Skills Location | +|------|--------------|-----------------| +| Claude Code | CLAUDE.md | .claude/skills/ | +| OpenCode | AGENTS.md (priority) | .opencode/skill/ | +| Codex | AGENTS.md | .codex/skills/ | +| Cursor, Copilot | AGENTS.md | Agent Skills standard | + +When updating the skill, update both `.claude/skills/` and `.opencode/skill/` to maintain sync. diff --git a/tools/embeddings/.mcp.json.example b/tools/embeddings/.mcp.json.example new file mode 100644 index 0000000..98832a2 --- /dev/null +++ b/tools/embeddings/.mcp.json.example @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "memory-forge": { + "command": "npx", + "args": ["-y", "@memory-forge/embeddings"] + } + } +} diff --git a/tools/embeddings/README.md b/tools/embeddings/README.md new file mode 100644 index 0000000..dc57c25 --- /dev/null +++ b/tools/embeddings/README.md @@ -0,0 +1,372 @@ +# Memory Forge Embeddings + +Sistema de búsqueda semántica para el conocimiento almacenado en CLAUDE.md, AGENTS.md y SKILL.md. + +## ¿Por qué embeddings? + +### El problema: Context Bloat + +Sin embeddings, **todo** el conocimiento se carga en cada sesión: + +``` +Sesión típica SIN embeddings: +┌─────────────────────────────────────────────────────────────┐ +│ CLAUDE.md completo → ~2,000 tokens │ +│ 30 skills cargados → ~15,000 tokens │ +│ Skills de otros módulos → ~8,000 tokens │ +├─────────────────────────────────────────────────────────────┤ +│ TOTAL por sesión → ~25,000 tokens │ +│ × 50 sesiones/día → 1,250,000 tokens/día │ +└─────────────────────────────────────────────────────────────┘ +``` + +**Problemas:** +- 💸 Tokens desperdiciados en contexto irrelevante +- 🐌 Respuestas más lentas por contexto grande +- 🔍 Sin búsqueda semántica: "webhook falla" no encuentra "signature verification failed" +- 📚 Límite práctico de ~50 skills antes de que sea inmanejable + +### La solución: Búsqueda semántica on-demand + +Con embeddings, solo se carga lo **relevante**: + +``` +Sesión típica CON embeddings: +┌─────────────────────────────────────────────────────────────┐ +│ Usuario: "el webhook de stripe falla" │ +│ │ +│ 1. Claude detecta que necesita contexto │ +│ 2. Llama a search_knowledge("webhook stripe falla") │ +│ 3. Sistema encuentra chunks relevantes (~500 tokens) │ +│ 4. Solo esos chunks se inyectan en contexto │ +├─────────────────────────────────────────────────────────────┤ +│ TOTAL por sesión → ~500-2,000 tokens │ +│ Reducción → 90-95% │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Beneficios + +| Métrica | Sin Embeddings | Con Embeddings | Mejora | +|---------|----------------|----------------|--------| +| Tokens/sesión | ~25,000 | ~2,000 | **92%** menos | +| Búsqueda | Exacta (keywords) | Semántica | Encuentra sinónimos | +| Skills soportados | ~50 | **Ilimitados** | Sin límite práctico | +| Latencia | Alta (contexto grande) | Baja | Respuestas más rápidas | + +### Búsqueda semántica vs exacta + +``` +Búsqueda EXACTA (sin embeddings): + Query: "webhook validation error" + ❌ No encuentra: "Stripe signature verification failed" + ❌ No encuentra: "Invalid webhook signature" + +Búsqueda SEMÁNTICA (con embeddings): + Query: "webhook validation error" + ✅ Encuentra: "Stripe signature verification failed" (similitud 0.85) + ✅ Encuentra: "Invalid webhook signature" (similitud 0.78) + ✅ Encuentra: "HTTP 400 on webhook endpoint" (similitud 0.72) +``` + +## Arquitectura + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ CLAUDE.md │ │ Chunker │ │ SQLite DB │ │ Search │ +│ SKILL.md │ ──► │ (parseo │ ──► │ (chunks + │ ──► │ (cosine │ +│ AGENTS.md │ │ semántico) │ │ embeddings)│ │ similarity)│ +└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ + │ + ▼ + ┌─────────────┐ + │Transformers │ + │.js (local) │ + │ 22MB modelo │ + └─────────────┘ +``` + +**Componentes:** +- **Chunker**: Divide archivos en chunks semánticos (frontmatter, triggers, solution, etc.) +- **Embeddings**: Genera vectores con Transformers.js (all-MiniLM-L6-v2, 384 dimensiones) +- **SQLite**: Almacena chunks y embeddings localmente +- **Search**: Búsqueda por similitud coseno con ranking por prioridad + +**Características:** +- 🔒 **100% local** - Sin API keys, sin enviar datos a terceros +- 📦 **Zero dependencies externas** - Solo Node.js +- 🧠 **Memoria con olvido** - Sistema LRU que olvida lo que no se usa +- ⚡ **Modelo pequeño** - 22MB, se descarga una vez + +## Instalación + +### Opción 1: Instalación rápida con npm (recomendada) + +```bash +# Instalar globalmente +npm install -g @memory-forge/embeddings + +# Añadir a Claude Code (en el directorio de tu proyecto) +claude mcp add memory-forge -- npx -y @memory-forge/embeddings +``` + +¡Listo! El MCP server se configura automáticamente y usa el directorio actual como raíz del proyecto. + +### Opción 2: Instalación desde source + +```bash +# 1. Clonar e instalar +cd /ruta/a/memory-forge/tools/embeddings +npm install +npm run build + +# 2. Añadir a Claude Code +claude mcp add memory-forge -- node /ruta/completa/a/memory-forge/tools/embeddings/dist/mcp-server.js +``` + +### Opción 3: Configuración manual + +Si prefieres editar la configuración manualmente, añade a `~/.claude.json`: + +```json +{ + "mcpServers": { + "memory-forge": { + "command": "npx", + "args": ["-y", "@memory-forge/embeddings"] + } + } +} +``` + +O para instalación local: + +```json +{ + "mcpServers": { + "memory-forge": { + "command": "node", + "args": ["/ruta/completa/a/memory-forge/tools/embeddings/dist/mcp-server.js"] + } + } +} +``` + +### Configuración por proyecto + +Para compartir la configuración con tu equipo, usa scope de proyecto: + +```bash +claude mcp add --scope project memory-forge -- npx -y @memory-forge/embeddings +``` + +Esto crea `.mcp.json` en la raíz del proyecto (añádelo a git). + +### Verificar instalación + +```bash +# Ver MCPs instalados +claude mcp list + +# En Claude Code, deberías tener estos tools: +# - search_knowledge +# - index_knowledge +# - knowledge_stats +# - forget_knowledge +``` + +## Uso + +### MCP Server (recomendado para Claude Code) + +Claude usa automáticamente los tools cuando detecta que necesita contexto: + +``` +Usuario: "El webhook de Stripe da error 400" + +Claude: [Internamente llama a search_knowledge] + "Encontré un skill relevante. El problema común es que + el body se parsea antes de verificar la firma..." +``` + +### CLI (para otros CLIs o uso manual) + +```bash +# Indexar conocimiento del proyecto +memory-forge index + +# Buscar +memory-forge query "webhook signature error" +memory-forge query "testing patterns" --limit 3 --json + +# Ver estadísticas de memoria +memory-forge memory + +# Olvidar memorias antiguas (no usadas en 30 días) +memory-forge forget --max-age 30 + +# Mantener máximo 100 archivos +memory-forge forget --max-files 100 --dry-run +``` + +## Sistema de memoria con olvido + +El sistema simula memoria humana: lo que no se usa se olvida. + +### Importancia (1-10) + +Cuando Memory Forge crea un skill, el LLM decide su importancia: + +```yaml +--- +name: critical-auth-pattern +importance: 9 # Crítico, nunca olvidar +--- +``` + +| Valor | Significado | Se borra | +|-------|-------------|----------| +| 9-10 | Crítico | ❌ Nunca (protegido) | +| 6-8 | Muy importante | ❌ Nunca (protegido) | +| 4-5 | Útil (default) | ✅ Si no se usa | +| 1-3 | Efímero | ✅ Primero en borrarse | + +### Algoritmo de olvido + +``` +Al decidir qué olvidar: +1. Archivos con importance >= 8 → NUNCA se borran +2. Del resto, ordenar por: + a. Menor importancia primero + b. Menor access_count (uso) + c. Más antiguo last_accessed +3. Borrar según política (max_age o max_files) +``` + +### Ejemplo + +``` +Estado de memoria: +├── CLAUDE.md (importance: 10, accesos: 50) → PROTEGIDO +├── auth-pattern.md (importance: 8, accesos: 30) → PROTEGIDO +├── stripe-webhook.md (importance: 5, accesos: 10) → Candidato +├── temp-fix.md (importance: 2, accesos: 1) → Se borra primero +└── old-workaround.md (importance: 4, accesos: 0) → Se borra segundo +``` + +## Chunking Strategy + +### SKILL.md → Chunks semánticos + +| Chunk | Prioridad | Contenido | +|-------|-----------|-----------| +| frontmatter | 10 | Nombre, descripción, triggers | +| trigger | 9 | Condiciones de activación, errores | +| problem | 8 | Descripción del problema | +| solution | 7 | Pasos de solución | +| verification | 5 | Cómo verificar | +| notes | 4 | Notas adicionales | + +### CLAUDE.md → Por secciones + +- Divide por H2 (`## Sección`) +- Si sección > 500 tokens, subdivide por H3 +- Preserva contexto de jerarquía + +## Tests + +```bash +npm test # Ejecutar tests +npm run test:watch # Watch mode +npm run test:coverage # Con coverage +``` + +## Estructura del proyecto + +``` +tools/embeddings/ +├── src/ +│ ├── index.ts # CLI entry point +│ ├── mcp-server.ts # MCP Server para Claude Code +│ ├── chunker.ts # Parseo de archivos → chunks +│ ├── embeddings.ts # Generación con Transformers.js +│ ├── db.ts # SQLite + operaciones de memoria +│ ├── search.ts # Búsqueda semántica +│ ├── sync.ts # Detección de cambios +│ └── types.ts # Tipos TypeScript +├── tests/ +│ ├── chunker.test.ts +│ ├── db.test.ts +│ ├── embeddings.test.ts +│ └── search.test.ts +├── package.json +└── tsconfig.json +``` + +## Release y publicación (para mantenedores) + +El release es **automático** vía GitHub Actions cuando se crea un tag. + +### Crear un nuevo release + +```bash +# 1. Asegúrate de estar en main con todo commiteado +git checkout main +git pull + +# 2. Crear tag de versión +git tag v1.0.0 +git push origin v1.0.0 +``` + +**GitHub Actions automáticamente:** +1. ✅ Ejecuta tests +2. ✅ Compila el proyecto +3. ✅ Publica en npm +4. ✅ Crea GitHub Release con changelog + +### Configuración requerida (una vez) + +1. **NPM Token**: En GitHub repo → Settings → Secrets → `NPM_TOKEN` + - Crear en npmjs.com → Access Tokens → Generate New Token (Automation) + +2. **Scope de npm**: Crear organización `@memory-forge` en npmjs.com + - O cambiar el nombre del paquete en `package.json` + +### Versiones + +Seguimos [SemVer](https://semver.org/): +- `v1.0.0` → Release estable +- `v1.1.0` → Nueva funcionalidad (backward compatible) +- `v1.0.1` → Bug fix +- `v2.0.0-beta.1` → Pre-release (no se marca como latest en npm) + +## Troubleshooting + +### "No index found" + +```bash +memory-forge index # Crear índice primero +``` + +### MCP server no aparece en Claude Code + +1. Verifica rutas absolutas en la configuración +2. Reinicia Claude Code completamente +3. Revisa logs: `~/.claude/logs/` + +### Modelo tarda en cargar + +La primera vez descarga el modelo (~22MB). Después usa caché local. + +```bash +memory-forge preload # Pre-descargar modelo +``` + +### Error de permisos en SQLite + +El directorio `.memory-forge/` debe ser escribible: + +```bash +chmod 755 .memory-forge +``` diff --git a/tools/embeddings/package-lock.json b/tools/embeddings/package-lock.json new file mode 100644 index 0000000..ebb4a65 --- /dev/null +++ b/tools/embeddings/package-lock.json @@ -0,0 +1,3709 @@ +{ + "name": "@memory-forge/embeddings", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@memory-forge/embeddings", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.25.3", + "@xenova/transformers": "^2.17.0", + "better-sqlite3": "^11.7.0", + "commander": "^12.1.0", + "gray-matter": "^4.0.3", + "zod": "^4.3.6" + }, + "bin": { + "memory-forge": "dist/index.js" + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.12", + "@types/node": "^22.10.0", + "typescript": "^5.7.0", + "vitest": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@huggingface/jinja": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.2.2.tgz", + "integrity": "sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.25.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz", + "integrity": "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz", + "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz", + "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz", + "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz", + "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz", + "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz", + "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz", + "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz", + "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz", + "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz", + "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz", + "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz", + "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz", + "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz", + "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz", + "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz", + "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz", + "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz", + "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz", + "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz", + "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz", + "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz", + "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz", + "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz", + "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz", + "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", + "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xenova/transformers": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@xenova/transformers/-/transformers-2.17.2.tgz", + "integrity": "sha512-lZmHqzrVIkSvZdKZEx7IYY51TK0WDrC8eR0c5IMnBsO8di8are1zzw8BlLhyO2TklZKLN5UffNGs1IJwT6oOqQ==", + "license": "Apache-2.0", + "dependencies": { + "@huggingface/jinja": "^0.2.2", + "onnxruntime-web": "1.14.0", + "sharp": "^0.32.0" + }, + "optionalDependencies": { + "onnxruntime-node": "1.14.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.3.tgz", + "integrity": "sha512-9+kwVx8QYvt3hPWnmb19tPnh38c6Nihz8Lx3t0g9+4GoIf3/fTgYwM4Z6NxgI+B9elLQA7mLE9PpqcWtOMRDiQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/flatbuffers": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz", + "integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==", + "license": "SEE LICENSE IN LICENSE.txt" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.87.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onnx-proto": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/onnx-proto/-/onnx-proto-4.0.4.tgz", + "integrity": "sha512-aldMOB3HRoo6q/phyB6QRQxSt895HNNw82BNyZ2CMh4bjeKv7g/c+VpAFtJuEMVfYLMbRx61hbuqnKceLeDcDA==", + "license": "MIT", + "dependencies": { + "protobufjs": "^6.8.8" + } + }, + "node_modules/onnxruntime-common": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.14.0.tgz", + "integrity": "sha512-3LJpegM2iMNRX2wUmtYfeX/ytfOzNwAWKSq1HbRrKc9+uqG/FsEA0bbKZl1btQeZaXhC26l44NWpNUeXPII7Ew==", + "license": "MIT" + }, + "node_modules/onnxruntime-node": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.14.0.tgz", + "integrity": "sha512-5ba7TWomIV/9b6NH/1x/8QEeowsb+jBEvFzU6z0T4mNsFwdPqXeFUM7uxC6QeSRkEbWu3qEB0VMjrvzN/0S9+w==", + "license": "MIT", + "optional": true, + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "onnxruntime-common": "~1.14.0" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.14.0.tgz", + "integrity": "sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw==", + "license": "MIT", + "dependencies": { + "flatbuffers": "^1.12.0", + "guid-typescript": "^1.0.9", + "long": "^4.0.0", + "onnx-proto": "^4.0.4", + "onnxruntime-common": "~1.14.0", + "platform": "^1.3.6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protobufjs": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", + "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.0", + "@rollup/rollup-android-arm64": "4.57.0", + "@rollup/rollup-darwin-arm64": "4.57.0", + "@rollup/rollup-darwin-x64": "4.57.0", + "@rollup/rollup-freebsd-arm64": "4.57.0", + "@rollup/rollup-freebsd-x64": "4.57.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", + "@rollup/rollup-linux-arm-musleabihf": "4.57.0", + "@rollup/rollup-linux-arm64-gnu": "4.57.0", + "@rollup/rollup-linux-arm64-musl": "4.57.0", + "@rollup/rollup-linux-loong64-gnu": "4.57.0", + "@rollup/rollup-linux-loong64-musl": "4.57.0", + "@rollup/rollup-linux-ppc64-gnu": "4.57.0", + "@rollup/rollup-linux-ppc64-musl": "4.57.0", + "@rollup/rollup-linux-riscv64-gnu": "4.57.0", + "@rollup/rollup-linux-riscv64-musl": "4.57.0", + "@rollup/rollup-linux-s390x-gnu": "4.57.0", + "@rollup/rollup-linux-x64-gnu": "4.57.0", + "@rollup/rollup-linux-x64-musl": "4.57.0", + "@rollup/rollup-openbsd-x64": "4.57.0", + "@rollup/rollup-openharmony-arm64": "4.57.0", + "@rollup/rollup-win32-arm64-msvc": "4.57.0", + "@rollup/rollup-win32-ia32-msvc": "4.57.0", + "@rollup/rollup-win32-x64-gnu": "4.57.0", + "@rollup/rollup-win32-x64-msvc": "4.57.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/sharp/node_modules/tar-fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/sharp/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + } + } +} diff --git a/tools/embeddings/package.json b/tools/embeddings/package.json new file mode 100644 index 0000000..b3a62c0 --- /dev/null +++ b/tools/embeddings/package.json @@ -0,0 +1,49 @@ +{ + "name": "@memory-forge/embeddings", + "version": "1.0.0", + "description": "Knowledge system with semantic search for AI coding agents", + "type": "module", + "main": "dist/mcp-server.js", + "bin": { + "@memory-forge/embeddings": "dist/mcp-server.js", + "memory-forge": "dist/index.js", + "memory-forge-mcp": "dist/mcp-server.js" + }, + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "start": "node dist/index.js", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "lint": "eslint src/", + "prepare": "npm run build" + }, + "keywords": [ + "ai", + "embeddings", + "knowledge-base", + "semantic-search", + "claude-code", + "opencode" + ], + "author": "Memory Forge", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.25.3", + "@xenova/transformers": "^2.17.0", + "better-sqlite3": "^11.7.0", + "commander": "^12.1.0", + "gray-matter": "^4.0.3", + "zod": "^4.3.6" + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.12", + "@types/node": "^22.10.0", + "typescript": "^5.7.0", + "vitest": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/tools/embeddings/src/chunker.ts b/tools/embeddings/src/chunker.ts new file mode 100644 index 0000000..d5857c2 --- /dev/null +++ b/tools/embeddings/src/chunker.ts @@ -0,0 +1,390 @@ +/** + * Chunker module - Parses SKILL.md and CLAUDE.md files into semantic chunks + */ + +import { readFileSync } from 'fs'; +import { createHash } from 'crypto'; +import matter from 'gray-matter'; +import { Chunk, SkillFrontmatter } from './types.js'; + +const MAX_CHUNK_TOKENS = 500; // Approximate token limit per chunk +const CHARS_PER_TOKEN = 4; // Rough estimate + +/** + * Generate a unique ID for a chunk + */ +function generateChunkId(sourceFile: string, chunkType: string, index: number): string { + const hash = createHash('md5') + .update(`${sourceFile}:${chunkType}:${index}`) + .digest('hex') + .substring(0, 8); + return `${chunkType}-${hash}`; +} + +/** + * Estimate token count from text + */ +function estimateTokens(text: string): number { + return Math.ceil(text.length / CHARS_PER_TOKEN); +} + +/** + * Split text into chunks that don't exceed max tokens + */ +function splitByTokenLimit(text: string, maxTokens: number): string[] { + const maxChars = maxTokens * CHARS_PER_TOKEN; + if (text.length <= maxChars) { + return [text]; + } + + const chunks: string[] = []; + const paragraphs = text.split(/\n\n+/); + let currentChunk = ''; + + for (const para of paragraphs) { + if (currentChunk.length + para.length + 2 <= maxChars) { + currentChunk += (currentChunk ? '\n\n' : '') + para; + } else { + if (currentChunk) { + chunks.push(currentChunk); + } + // If single paragraph is too long, split by sentences + if (para.length > maxChars) { + const sentences = para.split(/(?<=[.!?])\s+/); + currentChunk = ''; + for (const sentence of sentences) { + if (currentChunk.length + sentence.length + 1 <= maxChars) { + currentChunk += (currentChunk ? ' ' : '') + sentence; + } else { + if (currentChunk) chunks.push(currentChunk); + currentChunk = sentence; + } + } + } else { + currentChunk = para; + } + } + } + if (currentChunk) { + chunks.push(currentChunk); + } + + return chunks; +} + +/** + * Extract sections from markdown content by heading level + */ +function extractSections(content: string, level: number = 2): Map { + const sections = new Map(); + const lines = content.split('\n'); + + let currentHeading = ''; + let currentContent: string[] = []; + let inSection = false; + + for (const line of lines) { + const headingMatch = line.match(new RegExp(`^#{${level}}\\s+(.+)$`)); + + if (headingMatch) { + // Save previous section + if (inSection && currentHeading) { + sections.set(currentHeading, currentContent.join('\n').trim()); + } + currentHeading = headingMatch[1]; + currentContent = []; + inSection = true; + } else if (inSection) { + // Check if we hit a higher-level heading + const higherHeading = line.match(/^#{1,}(?=\s)/); + if (higherHeading && higherHeading[0].length < level) { + // End current section + if (currentHeading) { + sections.set(currentHeading, currentContent.join('\n').trim()); + } + inSection = false; + currentHeading = ''; + currentContent = []; + } else { + currentContent.push(line); + } + } + } + + // Save last section + if (inSection && currentHeading) { + sections.set(currentHeading, currentContent.join('\n').trim()); + } + + return sections; +} + +/** + * Parse a SKILL.md file into semantic chunks + */ +export function parseSkillFile(filePath: string): Chunk[] { + const chunks: Chunk[] = []; + const fileContent = readFileSync(filePath, 'utf-8'); + const { data: frontmatter, content } = matter(fileContent); + + const fm = frontmatter as SkillFrontmatter; + const skillName = fm.name || filePath.split('/').slice(-2)[0]; + let chunkIndex = 0; + + // 1. Frontmatter chunk (always highest priority) + if (fm.name && fm.description) { + chunks.push({ + id: generateChunkId(filePath, 'frontmatter', chunkIndex++), + sourceFile: filePath, + sourceType: 'skill', + chunkType: 'frontmatter', + content: `Skill: ${fm.name}\n\nDescription: ${fm.description}`, + priority: 10, + metadata: { + skillName, + }, + }); + } + + // Extract H2 sections + const sections = extractSections(content, 2); + + // 2. Problem section + const problemContent = sections.get('Problem'); + if (problemContent) { + chunks.push({ + id: generateChunkId(filePath, 'problem', chunkIndex++), + sourceFile: filePath, + sourceType: 'skill', + chunkType: 'problem', + content: `Problem:\n${problemContent}`, + heading: 'Problem', + priority: 8, + metadata: { + skillName, + sectionPath: 'Problem', + }, + }); + } + + // 3. Trigger Conditions (very important for search) + const triggerContent = sections.get('Trigger Conditions'); + if (triggerContent) { + chunks.push({ + id: generateChunkId(filePath, 'trigger', chunkIndex++), + sourceFile: filePath, + sourceType: 'skill', + chunkType: 'trigger', + content: `Trigger Conditions for ${skillName}:\n${triggerContent}`, + heading: 'Trigger Conditions', + priority: 9, + metadata: { + skillName, + sectionPath: 'Trigger Conditions', + }, + }); + } + + // 4. Solution section (may need splitting) + const solutionContent = sections.get('Solution'); + if (solutionContent) { + const solutionChunks = splitByTokenLimit(solutionContent, MAX_CHUNK_TOKENS); + for (let i = 0; i < solutionChunks.length; i++) { + chunks.push({ + id: generateChunkId(filePath, 'solution', chunkIndex++), + sourceFile: filePath, + sourceType: 'skill', + chunkType: 'solution', + content: `Solution for ${skillName} (part ${i + 1}/${solutionChunks.length}):\n${solutionChunks[i]}`, + heading: 'Solution', + priority: 7, + metadata: { + skillName, + sectionPath: 'Solution', + }, + }); + } + } + + // 5. Verification section + const verificationContent = sections.get('Verification'); + if (verificationContent) { + chunks.push({ + id: generateChunkId(filePath, 'verification', chunkIndex++), + sourceFile: filePath, + sourceType: 'skill', + chunkType: 'verification', + content: `Verification for ${skillName}:\n${verificationContent}`, + heading: 'Verification', + priority: 5, + metadata: { + skillName, + sectionPath: 'Verification', + }, + }); + } + + // 6. Other sections (Notes, Example, etc.) + for (const [heading, sectionContent] of sections) { + if (['Problem', 'Trigger Conditions', 'Solution', 'Verification'].includes(heading)) { + continue; + } + + const sectionChunks = splitByTokenLimit(sectionContent, MAX_CHUNK_TOKENS); + for (let i = 0; i < sectionChunks.length; i++) { + chunks.push({ + id: generateChunkId(filePath, 'section', chunkIndex++), + sourceFile: filePath, + sourceType: 'skill', + chunkType: 'section', + content: `${heading} for ${skillName}:\n${sectionChunks[i]}`, + heading, + priority: 4, + metadata: { + skillName, + sectionPath: heading, + }, + }); + } + } + + return chunks; +} + +/** + * Parse a CLAUDE.md or AGENTS.md file into semantic chunks + */ +export function parseContextFile(filePath: string): Chunk[] { + const chunks: Chunk[] = []; + const fileContent = readFileSync(filePath, 'utf-8'); + const fileName = filePath.split('/').pop() || ''; + const sourceType = fileName.toLowerCase().includes('agent') ? 'agents-md' : 'claude-md'; + + // Check if it has frontmatter + const { content } = matter(fileContent); + let chunkIndex = 0; + + // Extract H2 sections + const h2Sections = extractSections(content, 2); + + for (const [heading, sectionContent] of h2Sections) { + // Check if section is large enough to need H3 splitting + if (estimateTokens(sectionContent) > MAX_CHUNK_TOKENS) { + // Try to split by H3 + const h3Sections = extractSections(`## ${heading}\n${sectionContent}`, 3); + + if (h3Sections.size > 1) { + // Split by H3 + for (const [subHeading, subContent] of h3Sections) { + const subChunks = splitByTokenLimit(subContent, MAX_CHUNK_TOKENS); + for (let i = 0; i < subChunks.length; i++) { + chunks.push({ + id: generateChunkId(filePath, 'section', chunkIndex++), + sourceFile: filePath, + sourceType, + chunkType: 'section', + content: `${heading} > ${subHeading}:\n${subChunks[i]}`, + heading: `${heading} > ${subHeading}`, + priority: 6, + metadata: { + sectionPath: `${heading} > ${subHeading}`, + }, + }); + } + } + } else { + // No H3s, split by token limit + const sectionChunks = splitByTokenLimit(sectionContent, MAX_CHUNK_TOKENS); + for (let i = 0; i < sectionChunks.length; i++) { + chunks.push({ + id: generateChunkId(filePath, 'section', chunkIndex++), + sourceFile: filePath, + sourceType, + chunkType: 'section', + content: `${heading}:\n${sectionChunks[i]}`, + heading, + priority: 6, + metadata: { + sectionPath: heading, + }, + }); + } + } + } else { + // Section fits in one chunk + chunks.push({ + id: generateChunkId(filePath, 'section', chunkIndex++), + sourceFile: filePath, + sourceType, + chunkType: 'section', + content: `${heading}:\n${sectionContent}`, + heading, + priority: 6, + metadata: { + sectionPath: heading, + }, + }); + } + } + + // If no sections found, create a single chunk with full content + if (chunks.length === 0 && content.trim()) { + const fullChunks = splitByTokenLimit(content, MAX_CHUNK_TOKENS); + for (let i = 0; i < fullChunks.length; i++) { + chunks.push({ + id: generateChunkId(filePath, 'full', chunkIndex++), + sourceFile: filePath, + sourceType, + chunkType: 'full', + content: fullChunks[i], + priority: 5, + metadata: {}, + }); + } + } + + return chunks; +} + +export interface ParseResult { + chunks: Chunk[]; + importance?: number; // From frontmatter, if specified +} + +/** + * Parse any supported file into chunks + * Returns chunks and importance (if specified in frontmatter) + */ +export function parseFile(filePath: string): ParseResult { + const fileName = filePath.toLowerCase(); + + if (fileName.endsWith('skill.md')) { + return parseSkillFileWithMeta(filePath); + } else if (fileName.endsWith('claude.md') || fileName.endsWith('agents.md')) { + return { chunks: parseContextFile(filePath) }; + } + + throw new Error(`Unsupported file type: ${filePath}`); +} + +/** + * Parse SKILL.md and extract importance from frontmatter + */ +function parseSkillFileWithMeta(filePath: string): ParseResult { + const fileContent = readFileSync(filePath, 'utf-8'); + const { data: frontmatter } = matter(fileContent); + const fm = frontmatter as SkillFrontmatter; + + return { + chunks: parseSkillFile(filePath), + importance: fm.importance, + }; +} + +/** + * Calculate hash of file content for change detection + */ +export function calculateFileHash(filePath: string): string { + const content = readFileSync(filePath, 'utf-8'); + return createHash('sha256').update(content).digest('hex'); +} diff --git a/tools/embeddings/src/db.ts b/tools/embeddings/src/db.ts new file mode 100644 index 0000000..cad1fad --- /dev/null +++ b/tools/embeddings/src/db.ts @@ -0,0 +1,519 @@ +/** + * Database module - SQLite with vector search + * + * Uses better-sqlite3 for SQLite operations + * Implements cosine similarity search in pure SQL/JS (no external vector extension needed) + */ + +import Database from 'better-sqlite3'; +import { existsSync, mkdirSync } from 'fs'; +import { dirname, join } from 'path'; +import { Chunk, FileIndex, IndexStats } from './types.js'; + +const DB_FILENAME = '.memory-forge/index.db'; + +// Default importance when not specified in frontmatter +const DEFAULT_IMPORTANCE = 5; + +export interface DbChunk extends Chunk { + embedding: string; // JSON serialized + hash: string; +} + +let db: Database.Database | null = null; + +/** + * Get database path for a project + */ +export function getDbPath(projectRoot: string): string { + return join(projectRoot, DB_FILENAME); +} + +/** + * Initialize database connection and schema + */ +export function initDatabase(projectRoot: string): Database.Database { + if (db) { + return db; + } + + const dbPath = getDbPath(projectRoot); + const dbDir = dirname(dbPath); + + // Create directory if needed + if (!existsSync(dbDir)) { + mkdirSync(dbDir, { recursive: true }); + } + + db = new Database(dbPath); + + // Enable WAL mode for better concurrent performance + db.pragma('journal_mode = WAL'); + + // Create tables + db.exec(` + -- Indexed files tracking + CREATE TABLE IF NOT EXISTS files ( + path TEXT PRIMARY KEY, + hash TEXT NOT NULL, + indexed_at TEXT NOT NULL, + last_accessed TEXT NOT NULL, + access_count INTEGER NOT NULL DEFAULT 0, + importance INTEGER NOT NULL DEFAULT 5, + chunk_count INTEGER NOT NULL + ); + + -- Chunks with embeddings + CREATE TABLE IF NOT EXISTS chunks ( + id TEXT PRIMARY KEY, + source_file TEXT NOT NULL, + source_type TEXT NOT NULL, + chunk_type TEXT NOT NULL, + content TEXT NOT NULL, + heading TEXT, + priority INTEGER NOT NULL, + metadata TEXT NOT NULL, + embedding TEXT NOT NULL, + FOREIGN KEY (source_file) REFERENCES files(path) ON DELETE CASCADE + ); + + -- Index for faster lookups + CREATE INDEX IF NOT EXISTS idx_chunks_source_file ON chunks(source_file); + CREATE INDEX IF NOT EXISTS idx_chunks_source_type ON chunks(source_type); + CREATE INDEX IF NOT EXISTS idx_chunks_priority ON chunks(priority DESC); + CREATE INDEX IF NOT EXISTS idx_files_last_accessed ON files(last_accessed); + `); + + // Migration: add columns if they don't exist (for existing databases) + try { + db.exec(`ALTER TABLE files ADD COLUMN last_accessed TEXT NOT NULL DEFAULT ''`); + } catch { /* column already exists */ } + try { + db.exec(`ALTER TABLE files ADD COLUMN access_count INTEGER NOT NULL DEFAULT 0`); + } catch { /* column already exists */ } + try { + db.exec(`ALTER TABLE files ADD COLUMN importance INTEGER NOT NULL DEFAULT 5`); + } catch { /* column already exists */ } + + return db; +} + +/** + * Close database connection + */ +export function closeDatabase(): void { + if (db) { + db.close(); + db = null; + } +} + +/** + * Check if database exists for a project + */ +export function databaseExists(projectRoot: string): boolean { + return existsSync(getDbPath(projectRoot)); +} + +/** + * Get file index entry + */ +export function getFileIndex(projectRoot: string, filePath: string): FileIndex | null { + const database = initDatabase(projectRoot); + const row = database.prepare('SELECT * FROM files WHERE path = ?').get(filePath) as any; + + if (!row) { + return null; + } + + return { + path: row.path, + hash: row.hash, + indexedAt: new Date(row.indexed_at), + chunkCount: row.chunk_count, + }; +} + +/** + * Get all indexed files + */ +export function getAllFileIndexes(projectRoot: string): FileIndex[] { + const database = initDatabase(projectRoot); + const rows = database.prepare('SELECT * FROM files').all() as any[]; + + return rows.map((row) => ({ + path: row.path, + hash: row.hash, + indexedAt: new Date(row.indexed_at), + chunkCount: row.chunk_count, + })); +} + +/** + * Insert or update chunks for a file + */ +export function upsertFileChunks( + projectRoot: string, + filePath: string, + fileHash: string, + chunks: Array, + importance?: number +): void { + const database = initDatabase(projectRoot); + + // Use provided importance or default + const fileImportance = importance ?? DEFAULT_IMPORTANCE; + + const transaction = database.transaction(() => { + // Delete existing chunks for this file + database.prepare('DELETE FROM chunks WHERE source_file = ?').run(filePath); + + // Delete existing file index + database.prepare('DELETE FROM files WHERE path = ?').run(filePath); + + // Insert new file index + const now = new Date().toISOString(); + database + .prepare( + 'INSERT INTO files (path, hash, indexed_at, last_accessed, access_count, importance, chunk_count) VALUES (?, ?, ?, ?, 0, ?, ?)' + ) + .run(filePath, fileHash, now, now, fileImportance, chunks.length); + + // Insert chunks + const insertChunk = database.prepare(` + INSERT INTO chunks (id, source_file, source_type, chunk_type, content, heading, priority, metadata, embedding) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + `); + + for (const chunk of chunks) { + insertChunk.run( + chunk.id, + chunk.sourceFile, + chunk.sourceType, + chunk.chunkType, + chunk.content, + chunk.heading || null, + chunk.priority, + JSON.stringify(chunk.metadata), + JSON.stringify(chunk.embedding) + ); + } + }); + + transaction(); +} + +/** + * Remove a file and its chunks from the index + */ +export function removeFile(projectRoot: string, filePath: string): void { + const database = initDatabase(projectRoot); + + const transaction = database.transaction(() => { + database.prepare('DELETE FROM chunks WHERE source_file = ?').run(filePath); + database.prepare('DELETE FROM files WHERE path = ?').run(filePath); + }); + + transaction(); +} + +/** + * Get all chunks (for vector search) + */ +export function getAllChunks( + projectRoot: string, + sourceTypes?: Array<'skill' | 'claude-md' | 'agents-md'> +): DbChunk[] { + const database = initDatabase(projectRoot); + + let query = 'SELECT * FROM chunks'; + const params: any[] = []; + + if (sourceTypes && sourceTypes.length > 0) { + const placeholders = sourceTypes.map(() => '?').join(', '); + query += ` WHERE source_type IN (${placeholders})`; + params.push(...sourceTypes); + } + + query += ' ORDER BY priority DESC'; + + const rows = database.prepare(query).all(...params) as any[]; + + return rows.map((row) => ({ + id: row.id, + sourceFile: row.source_file, + sourceType: row.source_type, + chunkType: row.chunk_type, + content: row.content, + heading: row.heading, + priority: row.priority, + metadata: JSON.parse(row.metadata), + embedding: row.embedding, + hash: '', // Not stored per chunk + })); +} + +/** + * Get index statistics + */ +export function getIndexStats(projectRoot: string): IndexStats { + if (!databaseExists(projectRoot)) { + return { + totalFiles: 0, + totalChunks: 0, + skillFiles: 0, + claudeMdFiles: 0, + agentsMdFiles: 0, + lastIndexed: null, + }; + } + + const database = initDatabase(projectRoot); + + const totalFiles = ( + database.prepare('SELECT COUNT(*) as count FROM files').get() as any + ).count; + const totalChunks = ( + database.prepare('SELECT COUNT(*) as count FROM chunks').get() as any + ).count; + + const skillFiles = ( + database + .prepare("SELECT COUNT(DISTINCT source_file) as count FROM chunks WHERE source_type = 'skill'") + .get() as any + ).count; + const claudeMdFiles = ( + database + .prepare("SELECT COUNT(DISTINCT source_file) as count FROM chunks WHERE source_type = 'claude-md'") + .get() as any + ).count; + const agentsMdFiles = ( + database + .prepare("SELECT COUNT(DISTINCT source_file) as count FROM chunks WHERE source_type = 'agents-md'") + .get() as any + ).count; + + const lastIndexedRow = database + .prepare('SELECT indexed_at FROM files ORDER BY indexed_at DESC LIMIT 1') + .get() as any; + + return { + totalFiles, + totalChunks, + skillFiles, + claudeMdFiles, + agentsMdFiles, + lastIndexed: lastIndexedRow ? new Date(lastIndexedRow.indexed_at) : null, + }; +} + +/** + * Clear all data from the database + */ +export function clearDatabase(projectRoot: string): void { + const database = initDatabase(projectRoot); + + database.exec(` + DELETE FROM chunks; + DELETE FROM files; + `); +} + +/** + * Update last_accessed timestamp for files that were accessed + */ +export function touchFiles(projectRoot: string, filePaths: string[]): void { + if (filePaths.length === 0) return; + + const database = initDatabase(projectRoot); + const now = new Date().toISOString(); + + const update = database.prepare( + 'UPDATE files SET last_accessed = ?, access_count = access_count + 1 WHERE path = ?' + ); + + const transaction = database.transaction(() => { + for (const path of filePaths) { + update.run(now, path); + } + }); + + transaction(); +} + +/** + * Configuration for memory retention + */ +export interface RetentionConfig { + maxFiles?: number; // Maximum number of files to keep + maxAgeDays?: number; // Remove files not accessed in X days + protectImportance?: number; // Files with importance >= this are NEVER deleted (default: 8) +} + +/** + * Get files that should be forgotten based on retention policy + * Respects importance: lower importance files are forgotten first + * Files with importance >= protectImportance are NEVER deleted + */ +export function getStaleFiles( + projectRoot: string, + config: RetentionConfig +): string[] { + const database = initDatabase(projectRoot); + const staleFiles: string[] = []; + + // Protection threshold (default 8 = root CLAUDE.md and app-level context files) + const protectThreshold = config.protectImportance ?? 8; + + // Get files older than maxAgeDays (only non-protected files) + if (config.maxAgeDays !== undefined) { + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - config.maxAgeDays); + const cutoffIso = cutoffDate.toISOString(); + + const oldFiles = database + .prepare( + `SELECT path FROM files + WHERE (last_accessed < ? OR last_accessed = '') + AND importance < ? + ORDER BY importance ASC, access_count ASC, last_accessed ASC` + ) + .all(cutoffIso, protectThreshold) as Array<{ path: string }>; + + for (const row of oldFiles) { + staleFiles.push(row.path); + } + } + + // Get files exceeding maxFiles limit (only non-protected files) + if (config.maxFiles !== undefined) { + // Count only non-protected files + const deletableFiles = ( + database + .prepare('SELECT COUNT(*) as count FROM files WHERE importance < ?') + .get(protectThreshold) as any + ).count; + + const protectedFiles = ( + database + .prepare('SELECT COUNT(*) as count FROM files WHERE importance >= ?') + .get(protectThreshold) as any + ).count; + + // Effective limit for deletable files + const effectiveLimit = Math.max(0, config.maxFiles - protectedFiles); + + if (deletableFiles > effectiveLimit) { + const toRemove = deletableFiles - effectiveLimit; + // Forget least important, least used, oldest files first + const lruFiles = database + .prepare( + `SELECT path FROM files + WHERE importance < ? + ORDER BY importance ASC, access_count ASC, last_accessed ASC + LIMIT ?` + ) + .all(protectThreshold, toRemove) as Array<{ path: string }>; + + for (const row of lruFiles) { + if (!staleFiles.includes(row.path)) { + staleFiles.push(row.path); + } + } + } + } + + return staleFiles; +} + +/** + * Forget (remove) stale files based on retention policy + * Returns the list of removed file paths + */ +export function forgetStaleFiles( + projectRoot: string, + config: RetentionConfig +): string[] { + const staleFiles = getStaleFiles(projectRoot, config); + + for (const filePath of staleFiles) { + removeFile(projectRoot, filePath); + } + + return staleFiles; +} + +/** + * Get memory usage statistics including access patterns + */ +export function getMemoryStats(projectRoot: string): { + totalFiles: number; + totalChunks: number; + oldestAccess: Date | null; + newestAccess: Date | null; + neverAccessed: number; + filesByAge: Array<{ path: string; lastAccessed: Date | null; accessCount: number }>; +} { + if (!databaseExists(projectRoot)) { + return { + totalFiles: 0, + totalChunks: 0, + oldestAccess: null, + newestAccess: null, + neverAccessed: 0, + filesByAge: [], + }; + } + + const database = initDatabase(projectRoot); + + const totalFiles = ( + database.prepare('SELECT COUNT(*) as count FROM files').get() as any + ).count; + + const totalChunks = ( + database.prepare('SELECT COUNT(*) as count FROM chunks').get() as any + ).count; + + const oldest = database + .prepare( + `SELECT last_accessed FROM files + WHERE last_accessed != '' + ORDER BY last_accessed ASC LIMIT 1` + ) + .get() as { last_accessed: string } | undefined; + + const newest = database + .prepare( + `SELECT last_accessed FROM files + WHERE last_accessed != '' + ORDER BY last_accessed DESC LIMIT 1` + ) + .get() as { last_accessed: string } | undefined; + + const neverAccessed = ( + database + .prepare(`SELECT COUNT(*) as count FROM files WHERE last_accessed = '' OR access_count = 0`) + .get() as any + ).count; + + const filesByAge = database + .prepare( + `SELECT path, last_accessed, access_count FROM files + ORDER BY last_accessed ASC` + ) + .all() as Array<{ path: string; last_accessed: string; access_count: number }>; + + return { + totalFiles, + totalChunks, + oldestAccess: oldest ? new Date(oldest.last_accessed) : null, + newestAccess: newest ? new Date(newest.last_accessed) : null, + neverAccessed, + filesByAge: filesByAge.map((f) => ({ + path: f.path, + lastAccessed: f.last_accessed ? new Date(f.last_accessed) : null, + accessCount: f.access_count, + })), + }; +} diff --git a/tools/embeddings/src/embeddings.ts b/tools/embeddings/src/embeddings.ts new file mode 100644 index 0000000..5cd3092 --- /dev/null +++ b/tools/embeddings/src/embeddings.ts @@ -0,0 +1,131 @@ +/** + * Embeddings module - Generate embeddings using Transformers.js + * + * Uses all-MiniLM-L6-v2 model (22MB) for local embedding generation + * No API keys required, fully offline capable + */ + +import { pipeline, FeatureExtractionPipeline } from '@xenova/transformers'; + +const MODEL_NAME = 'Xenova/all-MiniLM-L6-v2'; +const EMBEDDING_DIMENSION = 384; + +let embeddingPipeline: FeatureExtractionPipeline | null = null; +let initPromise: Promise | null = null; + +/** + * Initialize the embedding pipeline (lazy, singleton) + */ +async function initPipeline(): Promise { + if (embeddingPipeline) { + return embeddingPipeline; + } + + if (initPromise) { + return initPromise; + } + + initPromise = (async () => { + console.log('Loading embedding model (first run may download ~22MB)...'); + embeddingPipeline = await pipeline('feature-extraction', MODEL_NAME, { + quantized: true, // Use quantized model for smaller size + }) as FeatureExtractionPipeline; + console.log('Embedding model loaded.'); + return embeddingPipeline; + })(); + + return initPromise; +} + +/** + * Generate embedding for a single text + */ +export async function generateEmbedding(text: string): Promise { + const pipe = await initPipeline(); + + // Truncate text if too long (model has 256 token limit) + const truncatedText = text.slice(0, 2000); + + const output = await pipe(truncatedText, { + pooling: 'mean', + normalize: true, + }); + + // Convert to regular array + return Array.from(output.data as Float32Array); +} + +/** + * Generate embeddings for multiple texts (batched for efficiency) + */ +export async function generateEmbeddings(texts: string[]): Promise { + const pipe = await initPipeline(); + + const embeddings: number[][] = []; + + // Process in batches of 32 for memory efficiency + const batchSize = 32; + for (let i = 0; i < texts.length; i += batchSize) { + const batch = texts.slice(i, i + batchSize).map((t) => t.slice(0, 2000)); + + // Process batch + for (const text of batch) { + const output = await pipe(text, { + pooling: 'mean', + normalize: true, + }); + embeddings.push(Array.from(output.data as Float32Array)); + } + + // Progress indicator for large batches + if (texts.length > batchSize) { + const progress = Math.min(i + batchSize, texts.length); + process.stdout.write(`\rGenerating embeddings: ${progress}/${texts.length}`); + } + } + + if (texts.length > batchSize) { + console.log(); // New line after progress + } + + return embeddings; +} + +/** + * Calculate cosine similarity between two embeddings + */ +export function cosineSimilarity(a: number[], b: number[]): number { + if (a.length !== b.length) { + throw new Error('Embeddings must have same dimension'); + } + + let dotProduct = 0; + let normA = 0; + let normB = 0; + + for (let i = 0; i < a.length; i++) { + dotProduct += a[i] * b[i]; + normA += a[i] * a[i]; + normB += b[i] * b[i]; + } + + // Since embeddings are normalized, norms should be ~1 + // but calculate anyway for robustness + const similarity = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); + + return similarity; +} + +/** + * Get the embedding dimension for the current model + */ +export function getEmbeddingDimension(): number { + return EMBEDDING_DIMENSION; +} + +/** + * Preload the model (useful for startup) + */ +export async function preloadModel(): Promise { + await initPipeline(); +} diff --git a/tools/embeddings/src/index.ts b/tools/embeddings/src/index.ts new file mode 100644 index 0000000..f764ad2 --- /dev/null +++ b/tools/embeddings/src/index.ts @@ -0,0 +1,343 @@ +#!/usr/bin/env node +/** + * Memory Forge CLI - Knowledge system with semantic search + */ + +import { Command } from 'commander'; +import { resolve } from 'path'; +import { syncProject, formatSyncResult, discoverFiles } from './sync.js'; +import { search, searchUnique, formatResults, formatResultsJson, formatForContext } from './search.js'; +import { + getIndexStats, + closeDatabase, + databaseExists, + clearDatabase, + getMemoryStats, + forgetStaleFiles, + getStaleFiles, +} from './db.js'; +import { preloadModel } from './embeddings.js'; + +const program = new Command(); + +program + .name('memory-forge') + .description('Knowledge system with semantic search for AI coding agents') + .version('1.0.0'); + +/** + * Index command - Create or update the knowledge index + */ +program + .command('index') + .description('Index knowledge files (CLAUDE.md, AGENTS.md, SKILL.md)') + .option('-p, --path ', 'Project root path', process.cwd()) + .option('-f, --force', 'Force re-index all files') + .action(async (options) => { + const projectRoot = resolve(options.path); + console.log(`Indexing knowledge in: ${projectRoot}\n`); + + try { + if (options.force) { + console.log('Force re-indexing all files...\n'); + if (databaseExists(projectRoot)) { + clearDatabase(projectRoot); + } + } + + const result = await syncProject(projectRoot); + console.log('\n' + formatSyncResult(result, projectRoot)); + + const stats = getIndexStats(projectRoot); + console.log(`\nIndex stats:`); + console.log(` Total chunks: ${stats.totalChunks}`); + console.log(` Skills: ${stats.skillFiles}`); + console.log(` CLAUDE.md files: ${stats.claudeMdFiles}`); + console.log(` AGENTS.md files: ${stats.agentsMdFiles}`); + } catch (error) { + console.error('Error indexing:', (error as Error).message); + process.exit(1); + } finally { + closeDatabase(); + } + }); + +/** + * Query command - Search the knowledge index + */ +program + .command('query ') + .description('Search for relevant knowledge') + .option('-p, --path ', 'Project root path', process.cwd()) + .option('-n, --limit ', 'Maximum results', '5') + .option('-t, --threshold ', 'Minimum similarity threshold (0-1)', '0.3') + .option('--json', 'Output as JSON') + .option('--context', 'Output formatted for AI context injection') + .option('--unique', 'Return at most one result per source file') + .option('--type ', 'Filter by source type (skill,claude-md,agents-md)', '') + .action(async (text, options) => { + const projectRoot = resolve(options.path); + + try { + // Check if index exists + if (!databaseExists(projectRoot)) { + console.log('No index found. Creating index first...\n'); + await syncProject(projectRoot); + console.log(''); + } + + const searchOptions = { + limit: parseInt(options.limit, 10), + threshold: parseFloat(options.threshold), + sourceTypes: options.type + ? (options.type.split(',') as Array<'skill' | 'claude-md' | 'agents-md'>) + : undefined, + }; + + const searchFn = options.unique ? searchUnique : search; + const results = await searchFn(projectRoot, text, searchOptions); + + if (options.json) { + console.log(formatResultsJson(results)); + } else if (options.context) { + console.log(formatForContext(results)); + } else { + console.log(formatResults(results)); + } + } catch (error) { + console.error('Error searching:', (error as Error).message); + process.exit(1); + } finally { + closeDatabase(); + } + }); + +/** + * Sync command - Update index with changed files + */ +program + .command('sync') + .description('Sync index with file changes') + .option('-p, --path ', 'Project root path', process.cwd()) + .action(async (options) => { + const projectRoot = resolve(options.path); + + try { + console.log(`Syncing knowledge index in: ${projectRoot}\n`); + const result = await syncProject(projectRoot); + console.log('\n' + formatSyncResult(result, projectRoot)); + } catch (error) { + console.error('Error syncing:', (error as Error).message); + process.exit(1); + } finally { + closeDatabase(); + } + }); + +/** + * Stats command - Show index statistics + */ +program + .command('stats') + .description('Show index statistics') + .option('-p, --path ', 'Project root path', process.cwd()) + .action((options) => { + const projectRoot = resolve(options.path); + + try { + if (!databaseExists(projectRoot)) { + console.log('No index found. Run `memory-forge index` to create one.'); + return; + } + + const stats = getIndexStats(projectRoot); + console.log('Index Statistics:'); + console.log(` Total files: ${stats.totalFiles}`); + console.log(` Total chunks: ${stats.totalChunks}`); + console.log(` Skill files: ${stats.skillFiles}`); + console.log(` CLAUDE.md files: ${stats.claudeMdFiles}`); + console.log(` AGENTS.md files: ${stats.agentsMdFiles}`); + console.log( + ` Last indexed: ${stats.lastIndexed ? stats.lastIndexed.toLocaleString() : 'Never'}` + ); + } finally { + closeDatabase(); + } + }); + +/** + * List command - Show indexed files + */ +program + .command('list') + .description('List all indexed files') + .option('-p, --path ', 'Project root path', process.cwd()) + .option('--discover', 'Show discoverable files (not yet indexed)') + .action((options) => { + const projectRoot = resolve(options.path); + + try { + if (options.discover) { + const files = discoverFiles(projectRoot); + console.log('Discoverable knowledge files:\n'); + for (const file of files) { + console.log(` ${file.replace(projectRoot + '/', '')}`); + } + console.log(`\nTotal: ${files.length} file(s)`); + } else { + if (!databaseExists(projectRoot)) { + console.log('No index found. Run `memory-forge index` to create one.'); + return; + } + + const stats = getIndexStats(projectRoot); + console.log('Indexed files:\n'); + console.log(` (Run with --discover to see all discoverable files)`); + console.log(`\nTotal indexed: ${stats.totalFiles} file(s)`); + } + } finally { + closeDatabase(); + } + }); + +/** + * Preload command - Pre-download the embedding model + */ +program + .command('preload') + .description('Pre-download the embedding model') + .action(async () => { + try { + console.log('Pre-loading embedding model...'); + await preloadModel(); + console.log('Model ready!'); + } catch (error) { + console.error('Error preloading model:', (error as Error).message); + process.exit(1); + } + }); + +/** + * Clear command - Remove the index + */ +program + .command('clear') + .description('Clear the knowledge index') + .option('-p, --path ', 'Project root path', process.cwd()) + .action((options) => { + const projectRoot = resolve(options.path); + + try { + if (!databaseExists(projectRoot)) { + console.log('No index found.'); + return; + } + + clearDatabase(projectRoot); + console.log('Index cleared.'); + } finally { + closeDatabase(); + } + }); + +/** + * Memory command - Show memory usage and access patterns + */ +program + .command('memory') + .description('Show memory usage and access patterns') + .option('-p, --path ', 'Project root path', process.cwd()) + .action((options) => { + const projectRoot = resolve(options.path); + + try { + if (!databaseExists(projectRoot)) { + console.log('No index found. Run `memory-forge index` to create one.'); + return; + } + + const stats = getMemoryStats(projectRoot); + + console.log('Memory Usage Statistics:\n'); + console.log(` Total files: ${stats.totalFiles}`); + console.log(` Total chunks: ${stats.totalChunks}`); + console.log(` Never accessed: ${stats.neverAccessed}`); + console.log( + ` Oldest access: ${stats.oldestAccess ? stats.oldestAccess.toLocaleString() : 'N/A'}` + ); + console.log( + ` Newest access: ${stats.newestAccess ? stats.newestAccess.toLocaleString() : 'N/A'}` + ); + + if (stats.filesByAge.length > 0) { + console.log('\nFiles by last access (oldest first):\n'); + for (const file of stats.filesByAge) { + const age = file.lastAccessed + ? `${Math.floor((Date.now() - file.lastAccessed.getTime()) / (1000 * 60 * 60 * 24))}d ago` + : 'never'; + const shortPath = file.path.replace(projectRoot + '/', ''); + console.log(` ${shortPath}`); + console.log(` Last access: ${age} | Access count: ${file.accessCount}`); + } + } + } finally { + closeDatabase(); + } + }); + +/** + * Forget command - Remove stale memories based on retention policy + */ +program + .command('forget') + .description('Remove stale memories based on retention policy') + .option('-p, --path ', 'Project root path', process.cwd()) + .option('--max-files ', 'Maximum number of files to keep') + .option('--max-age ', 'Remove files not accessed in X days') + .option('--dry-run', 'Show what would be removed without removing') + .action((options) => { + const projectRoot = resolve(options.path); + + try { + if (!databaseExists(projectRoot)) { + console.log('No index found.'); + return; + } + + const config = { + maxFiles: options.maxFiles ? parseInt(options.maxFiles, 10) : undefined, + maxAgeDays: options.maxAge ? parseInt(options.maxAge, 10) : undefined, + }; + + if (!config.maxFiles && !config.maxAgeDays) { + console.log('Error: Specify at least one of --max-files or --max-age'); + process.exit(1); + } + + if (options.dryRun) { + const staleFiles = getStaleFiles(projectRoot, config); + if (staleFiles.length === 0) { + console.log('No files would be removed.'); + } else { + console.log(`Would remove ${staleFiles.length} file(s):\n`); + for (const file of staleFiles) { + console.log(` - ${file.replace(projectRoot + '/', '')}`); + } + } + } else { + const removed = forgetStaleFiles(projectRoot, config); + if (removed.length === 0) { + console.log('No files removed.'); + } else { + console.log(`Removed ${removed.length} file(s):\n`); + for (const file of removed) { + console.log(` - ${file.replace(projectRoot + '/', '')}`); + } + } + } + } finally { + closeDatabase(); + } + }); + +program.parse(); diff --git a/tools/embeddings/src/mcp-server.ts b/tools/embeddings/src/mcp-server.ts new file mode 100644 index 0000000..aa6e219 --- /dev/null +++ b/tools/embeddings/src/mcp-server.ts @@ -0,0 +1,310 @@ +#!/usr/bin/env node +/** + * Memory Forge MCP Server + * + * Provides semantic search over knowledge stored in CLAUDE.md and SKILL.md files. + * Claude Code can use this to retrieve relevant context on-demand. + */ + +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { z } from 'zod'; +import { search, searchUnique, formatForContext } from './search.js'; +import { syncProject } from './sync.js'; +import { + getIndexStats, + databaseExists, + closeDatabase, + getMemoryStats, + forgetStaleFiles, +} from './db.js'; + +// Get project root from environment or current directory +const PROJECT_ROOT = process.env.MEMORY_FORGE_PROJECT_ROOT || process.cwd(); + +const server = new McpServer({ + name: 'memory-forge', + version: '1.0.0', +}); + +/** + * Tool: search_knowledge + * Search for relevant knowledge based on a query + */ +server.tool( + 'search_knowledge', + 'Search for relevant knowledge in CLAUDE.md and SKILL.md files. Use this when you need context about errors, patterns, or conventions in the codebase.', + { + query: z.string().describe('The search query - can be an error message, concept, or question'), + limit: z.number().optional().default(3).describe('Maximum number of results to return'), + source_type: z + .enum(['all', 'skill', 'claude-md', 'agents-md']) + .optional() + .default('all') + .describe('Filter by source type'), + unique_files: z + .boolean() + .optional() + .default(true) + .describe('Return at most one result per source file'), + }, + async ({ query, limit, source_type, unique_files }) => { + try { + // Auto-index if no index exists + if (!databaseExists(PROJECT_ROOT)) { + await syncProject(PROJECT_ROOT); + } + + const sourceTypes = + source_type === 'all' + ? undefined + : ([source_type] as Array<'skill' | 'claude-md' | 'agents-md'>); + + const searchFn = unique_files ? searchUnique : search; + const results = await searchFn(PROJECT_ROOT, query, { + limit, + sourceTypes, + threshold: 0.3, + }); + + if (results.length === 0) { + return { + content: [ + { + type: 'text' as const, + text: 'No relevant knowledge found for this query.', + }, + ], + }; + } + + // Format results for context injection + const contextText = formatForContext(results); + + return { + content: [ + { + type: 'text' as const, + text: contextText, + }, + ], + }; + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error searching knowledge: ${(error as Error).message}`, + }, + ], + isError: true, + }; + } + } +); + +/** + * Tool: index_knowledge + * Index or re-index knowledge files + */ +server.tool( + 'index_knowledge', + 'Index or re-index knowledge files (CLAUDE.md, AGENTS.md, SKILL.md). Run this after adding new skills or updating documentation.', + { + force: z + .boolean() + .optional() + .default(false) + .describe('Force re-index all files, even if unchanged'), + }, + async ({ force }) => { + try { + if (force && databaseExists(PROJECT_ROOT)) { + const { clearDatabase } = await import('./db.js'); + clearDatabase(PROJECT_ROOT); + } + + const result = await syncProject(PROJECT_ROOT); + + const summary = [ + `Indexed knowledge in: ${PROJECT_ROOT}`, + '', + `Added: ${result.added.length} file(s)`, + `Updated: ${result.updated.length} file(s)`, + `Removed: ${result.removed.length} file(s)`, + `Unchanged: ${result.unchanged.length} file(s)`, + ].join('\n'); + + return { + content: [ + { + type: 'text' as const, + text: summary, + }, + ], + }; + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error indexing knowledge: ${(error as Error).message}`, + }, + ], + isError: true, + }; + } + } +); + +/** + * Tool: knowledge_stats + * Get statistics about indexed knowledge + */ +server.tool( + 'knowledge_stats', + 'Get statistics about indexed knowledge files and chunks.', + {}, + async () => { + try { + const stats = getIndexStats(PROJECT_ROOT); + const memStats = getMemoryStats(PROJECT_ROOT); + + const summary = [ + `Knowledge Index Statistics for: ${PROJECT_ROOT}`, + '', + `Total files: ${stats.totalFiles}`, + `Total chunks: ${stats.totalChunks}`, + `Skill files: ${stats.skillFiles}`, + `CLAUDE.md files: ${stats.claudeMdFiles}`, + `AGENTS.md files: ${stats.agentsMdFiles}`, + `Last indexed: ${stats.lastIndexed ? stats.lastIndexed.toLocaleString() : 'Never'}`, + '', + 'Memory Usage:', + ` Never accessed: ${memStats.neverAccessed}`, + ` Oldest access: ${memStats.oldestAccess ? memStats.oldestAccess.toLocaleString() : 'N/A'}`, + ` Newest access: ${memStats.newestAccess ? memStats.newestAccess.toLocaleString() : 'N/A'}`, + ].join('\n'); + + return { + content: [ + { + type: 'text' as const, + text: summary, + }, + ], + }; + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting stats: ${(error as Error).message}`, + }, + ], + isError: true, + }; + } + } +); + +/** + * Tool: forget_knowledge + * Remove stale memories based on retention policy + */ +server.tool( + 'forget_knowledge', + 'Remove stale/unused knowledge based on retention policy. Use to clean up old, unused skills and documentation.', + { + max_files: z + .number() + .optional() + .describe('Maximum number of files to keep (removes least recently used)'), + max_age_days: z + .number() + .optional() + .describe('Remove files not accessed in X days'), + }, + async ({ max_files, max_age_days }) => { + try { + if (!max_files && !max_age_days) { + return { + content: [ + { + type: 'text' as const, + text: 'Error: Specify at least one of max_files or max_age_days', + }, + ], + isError: true, + }; + } + + const config = { + maxFiles: max_files, + maxAgeDays: max_age_days, + }; + + const removed = forgetStaleFiles(PROJECT_ROOT, config); + + if (removed.length === 0) { + return { + content: [ + { + type: 'text' as const, + text: 'No stale files found to remove.', + }, + ], + }; + } + + const summary = [ + `Removed ${removed.length} stale file(s):`, + '', + ...removed.map((f) => `- ${f.replace(PROJECT_ROOT + '/', '')}`), + ].join('\n'); + + return { + content: [ + { + type: 'text' as const, + text: summary, + }, + ], + }; + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error forgetting knowledge: ${(error as Error).message}`, + }, + ], + isError: true, + }; + } + } +); + +/** + * Start the MCP server + */ +async function main() { + const transport = new StdioServerTransport(); + await server.connect(transport); + + // Handle cleanup on exit + process.on('SIGINT', () => { + closeDatabase(); + process.exit(0); + }); + + process.on('SIGTERM', () => { + closeDatabase(); + process.exit(0); + }); +} + +main().catch((error) => { + console.error('Failed to start MCP server:', error); + process.exit(1); +}); diff --git a/tools/embeddings/src/search.ts b/tools/embeddings/src/search.ts new file mode 100644 index 0000000..92a2821 --- /dev/null +++ b/tools/embeddings/src/search.ts @@ -0,0 +1,202 @@ +/** + * Search module - Semantic search and ranking + */ + +import { SearchResult, SearchOptions } from './types.js'; +import { generateEmbedding, cosineSimilarity } from './embeddings.js'; +import { getAllChunks, databaseExists, DbChunk, touchFiles } from './db.js'; + +const DEFAULT_LIMIT = 5; +const DEFAULT_THRESHOLD = 0.3; + +/** + * Search for relevant chunks using semantic similarity + */ +export async function search( + projectRoot: string, + query: string, + options: SearchOptions = {} +): Promise { + const { + limit = DEFAULT_LIMIT, + threshold = DEFAULT_THRESHOLD, + sourceTypes, + includeContent = true, + } = options; + + // Check if index exists + if (!databaseExists(projectRoot)) { + throw new Error( + 'No index found. Run `memory-forge index` first to create the knowledge index.' + ); + } + + // Get query embedding + const queryEmbedding = await generateEmbedding(query); + + // Get all chunks + const chunks = getAllChunks(projectRoot, sourceTypes); + + if (chunks.length === 0) { + return []; + } + + // Calculate similarity scores + const scoredChunks: Array<{ chunk: DbChunk; score: number }> = []; + + for (const chunk of chunks) { + const chunkEmbedding = JSON.parse(chunk.embedding) as number[]; + const similarity = cosineSimilarity(queryEmbedding, chunkEmbedding); + + // Apply priority boost (up to 20% boost for high priority) + const priorityBoost = (chunk.priority / 10) * 0.2; + const adjustedScore = similarity * (1 + priorityBoost); + + if (adjustedScore >= threshold) { + scoredChunks.push({ chunk, score: adjustedScore }); + } + } + + // Sort by score descending + scoredChunks.sort((a, b) => b.score - a.score); + + // Take top results + const topResults = scoredChunks.slice(0, limit); + + // Update access timestamps for returned files + const accessedFiles = [...new Set(topResults.map(({ chunk }) => chunk.sourceFile))]; + if (accessedFiles.length > 0) { + touchFiles(projectRoot, accessedFiles); + } + + // Format results + return topResults.map(({ chunk, score }) => ({ + chunk: { + id: chunk.id, + sourceFile: chunk.sourceFile, + sourceType: chunk.sourceType, + chunkType: chunk.chunkType, + content: includeContent ? chunk.content : '', + heading: chunk.heading, + priority: chunk.priority, + metadata: chunk.metadata, + }, + score, + sourceFile: chunk.sourceFile, + })); +} + +/** + * Search with deduplication by source file + * Returns at most one result per unique source file + */ +export async function searchUnique( + projectRoot: string, + query: string, + options: SearchOptions = {} +): Promise { + // Get more results than limit to account for duplicates + const allResults = await search(projectRoot, query, { + ...options, + limit: (options.limit || DEFAULT_LIMIT) * 3, + }); + + // Deduplicate by source file, keeping highest score + const seenFiles = new Set(); + const uniqueResults: SearchResult[] = []; + + for (const result of allResults) { + if (!seenFiles.has(result.sourceFile)) { + seenFiles.add(result.sourceFile); + uniqueResults.push(result); + + if (uniqueResults.length >= (options.limit || DEFAULT_LIMIT)) { + break; + } + } + } + + return uniqueResults; +} + +/** + * Format search results for display + */ +export function formatResults(results: SearchResult[]): string { + if (results.length === 0) { + return 'No matching results found.'; + } + + const lines: string[] = []; + lines.push(`Found ${results.length} relevant result(s):\n`); + + for (let i = 0; i < results.length; i++) { + const { chunk, score, sourceFile } = results[i]; + const scorePercent = (score * 100).toFixed(1); + + lines.push(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`); + lines.push(`📄 ${sourceFile}`); + lines.push(` Type: ${chunk.sourceType} | Section: ${chunk.heading || chunk.chunkType}`); + lines.push(` Score: ${scorePercent}% | Priority: ${chunk.priority}/10`); + + if (chunk.metadata.skillName) { + lines.push(` Skill: ${chunk.metadata.skillName}`); + } + + lines.push(''); + lines.push(chunk.content); + lines.push(''); + } + + return lines.join('\n'); +} + +/** + * Format results as JSON (for programmatic use) + */ +export function formatResultsJson(results: SearchResult[]): string { + return JSON.stringify( + results.map(({ chunk, score, sourceFile }) => ({ + sourceFile, + sourceType: chunk.sourceType, + chunkType: chunk.chunkType, + heading: chunk.heading, + score: Math.round(score * 1000) / 1000, + priority: chunk.priority, + skillName: chunk.metadata.skillName, + content: chunk.content, + })), + null, + 2 + ); +} + +/** + * Get context for AI agent consumption + * Returns formatted markdown suitable for injection into context + */ +export function formatForContext(results: SearchResult[]): string { + if (results.length === 0) { + return ''; + } + + const lines: string[] = []; + lines.push('## Relevant Knowledge\n'); + lines.push('The following knowledge was found relevant to your current task:\n'); + + for (const { chunk, score } of results) { + const relevance = score >= 0.7 ? 'High' : score >= 0.5 ? 'Medium' : 'Low'; + + if (chunk.metadata.skillName) { + lines.push(`### Skill: ${chunk.metadata.skillName}`); + } else { + lines.push(`### ${chunk.heading || 'Context'}`); + } + + lines.push(`_Source: ${chunk.sourceFile} | Relevance: ${relevance}_\n`); + lines.push(chunk.content); + lines.push(''); + } + + return lines.join('\n'); +} diff --git a/tools/embeddings/src/sync.ts b/tools/embeddings/src/sync.ts new file mode 100644 index 0000000..530c902 --- /dev/null +++ b/tools/embeddings/src/sync.ts @@ -0,0 +1,254 @@ +/** + * Sync module - File discovery and change detection + */ + +import { readdirSync, existsSync } from 'fs'; +import { join, relative } from 'path'; +import { SyncResult } from './types.js'; +import { calculateFileHash, parseFile, ParseResult } from './chunker.js'; +import { generateEmbeddings } from './embeddings.js'; +import { upsertFileChunks, removeFile, getAllFileIndexes, initDatabase } from './db.js'; + +const CONTEXT_FILE_NAMES = ['CLAUDE.md', 'AGENTS.md']; + +/** + * Recursively find files in directory + */ +function findFiles(dir: string): string[] { + const results: string[] = []; + + // Simple recursive search for now + function walkDir(currentDir: string) { + if (!existsSync(currentDir)) { + return; + } + + try { + const entries = readdirSync(currentDir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = join(currentDir, entry.name); + + if (entry.isDirectory()) { + // Skip common ignored directories + if ( + ['node_modules', '.git', 'dist', 'build', '.memory-forge'].includes( + entry.name + ) + ) { + continue; + } + walkDir(fullPath); + } else if (entry.isFile()) { + results.push(fullPath); + } + } + } catch { + // Ignore permission errors + } + } + + walkDir(dir); + return results; +} + +/** + * Discover all indexable files in a project + */ +export function discoverFiles(projectRoot: string): string[] { + const allFiles = findFiles(projectRoot); + const indexableFiles: string[] = []; + + for (const file of allFiles) { + const fileName = file.split('/').pop() || ''; + const relativePath = relative(projectRoot, file); + + // Check for SKILL.md files in skill directories + if (fileName === 'SKILL.md') { + const pathParts = relativePath.split('/'); + // Must be in a skills directory structure + if ( + pathParts.some((p) => ['skills', 'skill'].includes(p)) || + pathParts.includes('.claude') || + pathParts.includes('.opencode') || + pathParts.includes('.codex') + ) { + indexableFiles.push(file); + } + } + + // Check for context files (CLAUDE.md, AGENTS.md) + if (CONTEXT_FILE_NAMES.includes(fileName)) { + indexableFiles.push(file); + } + } + + return indexableFiles; +} + +/** + * Get files that need indexing (new or changed) + */ +export function getFilesToIndex( + projectRoot: string, + files: string[] +): { toIndex: string[]; toRemove: string[]; unchanged: string[] } { + const indexed = getAllFileIndexes(projectRoot); + const indexedMap = new Map(indexed.map((f) => [f.path, f])); + + const toIndex: string[] = []; + const unchanged: string[] = []; + const currentFiles = new Set(files); + + // Check each file + for (const file of files) { + const existing = indexedMap.get(file); + + if (!existing) { + // New file + toIndex.push(file); + } else { + // Check if changed + const currentHash = calculateFileHash(file); + if (currentHash !== existing.hash) { + toIndex.push(file); + } else { + unchanged.push(file); + } + } + } + + // Find removed files + const toRemove: string[] = []; + for (const existing of indexed) { + if (!currentFiles.has(existing.path)) { + toRemove.push(existing.path); + } + } + + return { toIndex, toRemove, unchanged }; +} + +/** + * Index a single file + */ +export async function indexFile( + projectRoot: string, + filePath: string +): Promise { + // Parse file into chunks (and get importance if specified) + const { chunks, importance } = parseFile(filePath); + + if (chunks.length === 0) { + return 0; + } + + // Generate embeddings for all chunks + const contents = chunks.map((c) => c.content); + const embeddings = await generateEmbeddings(contents); + + // Combine chunks with embeddings + const chunksWithEmbeddings = chunks.map((chunk, i) => ({ + ...chunk, + embedding: embeddings[i], + })); + + // Calculate file hash + const fileHash = calculateFileHash(filePath); + + // Store in database (with importance from frontmatter if available) + upsertFileChunks(projectRoot, filePath, fileHash, chunksWithEmbeddings, importance); + + return chunks.length; +} + +/** + * Full sync: discover, detect changes, and index + */ +export async function syncProject(projectRoot: string): Promise { + // Initialize database + initDatabase(projectRoot); + + // Discover files + const files = discoverFiles(projectRoot); + console.log(`Found ${files.length} knowledge file(s)`); + + // Determine what needs indexing + const { toIndex, toRemove, unchanged } = getFilesToIndex(projectRoot, files); + + const result: SyncResult = { + added: [], + updated: [], + removed: [], + unchanged, + }; + + // Remove deleted files + for (const file of toRemove) { + removeFile(projectRoot, file); + result.removed.push(file); + console.log(`Removed: ${relative(projectRoot, file)}`); + } + + // Index new/changed files + for (const file of toIndex) { + const existing = getAllFileIndexes(projectRoot).find((f) => f.path === file); + const isNew = !existing; + + console.log( + `${isNew ? 'Indexing' : 'Re-indexing'}: ${relative(projectRoot, file)}` + ); + + const chunkCount = await indexFile(projectRoot, file); + + if (isNew) { + result.added.push(file); + } else { + result.updated.push(file); + } + + console.log(` → ${chunkCount} chunk(s)`); + } + + return result; +} + +/** + * Format sync result for display + */ +export function formatSyncResult(result: SyncResult, projectRoot: string): string { + const lines: string[] = []; + + lines.push('Sync completed:\n'); + + if (result.added.length > 0) { + lines.push(`✅ Added: ${result.added.length} file(s)`); + for (const f of result.added) { + lines.push(` + ${relative(projectRoot, f)}`); + } + } + + if (result.updated.length > 0) { + lines.push(`🔄 Updated: ${result.updated.length} file(s)`); + for (const f of result.updated) { + lines.push(` ~ ${relative(projectRoot, f)}`); + } + } + + if (result.removed.length > 0) { + lines.push(`🗑️ Removed: ${result.removed.length} file(s)`); + for (const f of result.removed) { + lines.push(` - ${relative(projectRoot, f)}`); + } + } + + if (result.unchanged.length > 0) { + lines.push(`⏸️ Unchanged: ${result.unchanged.length} file(s)`); + } + + const total = + result.added.length + result.updated.length + result.unchanged.length; + lines.push(`\nTotal indexed: ${total} file(s)`); + + return lines.join('\n'); +} diff --git a/tools/embeddings/src/types.ts b/tools/embeddings/src/types.ts new file mode 100644 index 0000000..4314f50 --- /dev/null +++ b/tools/embeddings/src/types.ts @@ -0,0 +1,70 @@ +/** + * Types for Memory Forge embedding system + */ + +export interface SkillFrontmatter { + name: string; + description: string; + author?: string; + version?: string; + date?: string; + deprecated?: boolean; + importance?: number; // 1-10, decided by LLM when creating the skill +} + +export interface Chunk { + id: string; + sourceFile: string; + sourceType: 'skill' | 'claude-md' | 'agents-md'; + chunkType: 'frontmatter' | 'problem' | 'trigger' | 'solution' | 'verification' | 'section' | 'full'; + content: string; + heading?: string; + priority: number; // 1-10, higher = more important + metadata: { + skillName?: string; + sectionPath?: string; + lineStart?: number; + lineEnd?: number; + }; +} + +export interface IndexedChunk extends Chunk { + embedding: number[]; + hash: string; +} + +export interface FileIndex { + path: string; + hash: string; + indexedAt: Date; + chunkCount: number; +} + +export interface SearchResult { + chunk: Chunk; + score: number; + sourceFile: string; +} + +export interface SearchOptions { + limit?: number; + threshold?: number; + sourceTypes?: Array<'skill' | 'claude-md' | 'agents-md'>; + includeContent?: boolean; +} + +export interface IndexStats { + totalFiles: number; + totalChunks: number; + skillFiles: number; + claudeMdFiles: number; + agentsMdFiles: number; + lastIndexed: Date | null; +} + +export interface SyncResult { + added: string[]; + updated: string[]; + removed: string[]; + unchanged: string[]; +} diff --git a/tools/embeddings/tests/chunker.test.ts b/tools/embeddings/tests/chunker.test.ts new file mode 100644 index 0000000..a1b104e --- /dev/null +++ b/tools/embeddings/tests/chunker.test.ts @@ -0,0 +1,106 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { parseSkillFile, parseContextFile, calculateFileHash } from '../src/chunker.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const FIXTURES_DIR = resolve(__dirname, 'fixtures'); + +describe('chunker', () => { + describe('parseSkillFile', () => { + it('should parse a SKILL.md file into semantic chunks', () => { + const filePath = resolve(FIXTURES_DIR, 'sample-skill.md'); + const chunks = parseSkillFile(filePath); + + expect(chunks.length).toBeGreaterThan(0); + + // Should have frontmatter chunk + const frontmatter = chunks.find((c) => c.chunkType === 'frontmatter'); + expect(frontmatter).toBeDefined(); + expect(frontmatter?.content).toContain('test-skill'); + expect(frontmatter?.priority).toBe(10); + + // Should have trigger chunk + const trigger = chunks.find((c) => c.chunkType === 'trigger'); + expect(trigger).toBeDefined(); + expect(trigger?.content).toContain('Test error message'); + expect(trigger?.priority).toBe(9); + + // Should have problem chunk + const problem = chunks.find((c) => c.chunkType === 'problem'); + expect(problem).toBeDefined(); + expect(problem?.priority).toBe(8); + + // Should have solution chunk(s) + const solution = chunks.find((c) => c.chunkType === 'solution'); + expect(solution).toBeDefined(); + + // All chunks should have required fields + for (const chunk of chunks) { + expect(chunk.id).toBeDefined(); + expect(chunk.sourceFile).toBe(filePath); + expect(chunk.sourceType).toBe('skill'); + expect(chunk.content).toBeDefined(); + expect(chunk.priority).toBeGreaterThan(0); + } + }); + + it('should extract skill name from frontmatter', () => { + const filePath = resolve(FIXTURES_DIR, 'sample-skill.md'); + const chunks = parseSkillFile(filePath); + + const frontmatter = chunks.find((c) => c.chunkType === 'frontmatter'); + expect(frontmatter?.metadata.skillName).toBe('test-skill'); + }); + }); + + describe('parseContextFile', () => { + it('should parse a CLAUDE.md file into section chunks', () => { + const filePath = resolve(FIXTURES_DIR, 'sample-claude.md'); + const chunks = parseContextFile(filePath); + + expect(chunks.length).toBeGreaterThan(0); + + // Should have section chunks + const sections = chunks.filter((c) => c.chunkType === 'section'); + expect(sections.length).toBeGreaterThan(0); + + // All chunks should be claude-md type + for (const chunk of chunks) { + expect(chunk.sourceType).toBe('claude-md'); + } + }); + + it('should split large sections by H3', () => { + const filePath = resolve(FIXTURES_DIR, 'sample-claude.md'); + const chunks = parseContextFile(filePath); + + // Check for subsection chunks (Project Conventions has H3s) + const hasSubsections = chunks.some( + (c) => c.heading && c.heading.includes('>') + ); + // May or may not have subsections depending on content size + expect(chunks.length).toBeGreaterThan(0); + }); + }); + + describe('calculateFileHash', () => { + it('should return consistent hash for same file', () => { + const filePath = resolve(FIXTURES_DIR, 'sample-skill.md'); + const hash1 = calculateFileHash(filePath); + const hash2 = calculateFileHash(filePath); + + expect(hash1).toBe(hash2); + expect(hash1).toHaveLength(64); // SHA-256 hex + }); + + it('should return different hash for different files', () => { + const hash1 = calculateFileHash(resolve(FIXTURES_DIR, 'sample-skill.md')); + const hash2 = calculateFileHash(resolve(FIXTURES_DIR, 'sample-claude.md')); + + expect(hash1).not.toBe(hash2); + }); + }); +}); diff --git a/tools/embeddings/tests/db.test.ts b/tools/embeddings/tests/db.test.ts new file mode 100644 index 0000000..0e7e6c4 --- /dev/null +++ b/tools/embeddings/tests/db.test.ts @@ -0,0 +1,207 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { + initDatabase, + closeDatabase, + databaseExists, + getFileIndex, + getAllFileIndexes, + upsertFileChunks, + removeFile, + getAllChunks, + getIndexStats, + clearDatabase, +} from '../src/db.js'; +import { Chunk } from '../src/types.js'; + +describe('db', () => { + let tempDir: string; + + beforeEach(() => { + tempDir = mkdtempSync(join(tmpdir(), 'memory-forge-test-')); + }); + + afterEach(() => { + closeDatabase(); + rmSync(tempDir, { recursive: true, force: true }); + }); + + describe('initDatabase', () => { + it('should create database and tables', () => { + const db = initDatabase(tempDir); + expect(db).toBeDefined(); + expect(databaseExists(tempDir)).toBe(true); + }); + + it('should return same instance on multiple calls', () => { + const db1 = initDatabase(tempDir); + const db2 = initDatabase(tempDir); + expect(db1).toBe(db2); + }); + }); + + describe('file operations', () => { + const mockChunk: Chunk & { embedding: number[] } = { + id: 'test-chunk-1', + sourceFile: '/test/file.md', + sourceType: 'skill', + chunkType: 'frontmatter', + content: 'Test content', + priority: 10, + metadata: { skillName: 'test-skill' }, + embedding: Array(384).fill(0.1), + }; + + it('should insert and retrieve file index', () => { + initDatabase(tempDir); + upsertFileChunks(tempDir, '/test/file.md', 'abc123', [mockChunk]); + + const index = getFileIndex(tempDir, '/test/file.md'); + expect(index).toBeDefined(); + expect(index?.path).toBe('/test/file.md'); + expect(index?.hash).toBe('abc123'); + expect(index?.chunkCount).toBe(1); + }); + + it('should update file on re-index', () => { + initDatabase(tempDir); + + upsertFileChunks(tempDir, '/test/file.md', 'hash1', [mockChunk]); + const index1 = getFileIndex(tempDir, '/test/file.md'); + + upsertFileChunks(tempDir, '/test/file.md', 'hash2', [ + mockChunk, + { ...mockChunk, id: 'test-chunk-2' }, + ]); + const index2 = getFileIndex(tempDir, '/test/file.md'); + + expect(index1?.hash).toBe('hash1'); + expect(index2?.hash).toBe('hash2'); + expect(index2?.chunkCount).toBe(2); + }); + + it('should remove file and its chunks', () => { + initDatabase(tempDir); + upsertFileChunks(tempDir, '/test/file.md', 'abc123', [mockChunk]); + + removeFile(tempDir, '/test/file.md'); + + const index = getFileIndex(tempDir, '/test/file.md'); + const chunks = getAllChunks(tempDir); + + expect(index).toBeNull(); + expect(chunks.length).toBe(0); + }); + }); + + describe('chunk operations', () => { + it('should retrieve chunks filtered by source type', () => { + initDatabase(tempDir); + + upsertFileChunks(tempDir, '/skill.md', 'h1', [ + { + id: 'skill-1', + sourceFile: '/skill.md', + sourceType: 'skill', + chunkType: 'frontmatter', + content: 'Skill content', + priority: 10, + metadata: {}, + embedding: Array(384).fill(0.1), + }, + ]); + + upsertFileChunks(tempDir, '/claude.md', 'h2', [ + { + id: 'claude-1', + sourceFile: '/claude.md', + sourceType: 'claude-md', + chunkType: 'section', + content: 'Claude content', + priority: 5, + metadata: {}, + embedding: Array(384).fill(0.2), + }, + ]); + + const skillChunks = getAllChunks(tempDir, ['skill']); + const claudeChunks = getAllChunks(tempDir, ['claude-md']); + const allChunks = getAllChunks(tempDir); + + expect(skillChunks.length).toBe(1); + expect(claudeChunks.length).toBe(1); + expect(allChunks.length).toBe(2); + }); + }); + + describe('getIndexStats', () => { + it('should return zero stats for non-existent database', () => { + const stats = getIndexStats('/non-existent'); + expect(stats.totalFiles).toBe(0); + expect(stats.totalChunks).toBe(0); + expect(stats.lastIndexed).toBeNull(); + }); + + it('should return correct stats', () => { + initDatabase(tempDir); + + upsertFileChunks(tempDir, '/skill.md', 'h1', [ + { + id: 'skill-1', + sourceFile: '/skill.md', + sourceType: 'skill', + chunkType: 'frontmatter', + content: 'Skill', + priority: 10, + metadata: {}, + embedding: Array(384).fill(0.1), + }, + { + id: 'skill-2', + sourceFile: '/skill.md', + sourceType: 'skill', + chunkType: 'problem', + content: 'Problem', + priority: 8, + metadata: {}, + embedding: Array(384).fill(0.1), + }, + ]); + + const stats = getIndexStats(tempDir); + + expect(stats.totalFiles).toBe(1); + expect(stats.totalChunks).toBe(2); + expect(stats.skillFiles).toBe(1); + expect(stats.claudeMdFiles).toBe(0); + expect(stats.lastIndexed).toBeInstanceOf(Date); + }); + }); + + describe('clearDatabase', () => { + it('should remove all data', () => { + initDatabase(tempDir); + + upsertFileChunks(tempDir, '/file.md', 'hash', [ + { + id: 'chunk-1', + sourceFile: '/file.md', + sourceType: 'skill', + chunkType: 'full', + content: 'Content', + priority: 5, + metadata: {}, + embedding: Array(384).fill(0.1), + }, + ]); + + clearDatabase(tempDir); + + const stats = getIndexStats(tempDir); + expect(stats.totalFiles).toBe(0); + expect(stats.totalChunks).toBe(0); + }); + }); +}); diff --git a/tools/embeddings/tests/embeddings.test.ts b/tools/embeddings/tests/embeddings.test.ts new file mode 100644 index 0000000..42c8f9e --- /dev/null +++ b/tools/embeddings/tests/embeddings.test.ts @@ -0,0 +1,115 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { + generateEmbedding, + generateEmbeddings, + cosineSimilarity, + getEmbeddingDimension, + preloadModel, +} from '../src/embeddings.js'; + +describe('embeddings', () => { + // Preload model before all tests (first run downloads ~22MB) + beforeAll(async () => { + await preloadModel(); + }, 120000); // 2 min timeout for model download + + describe('generateEmbedding', () => { + it('should generate embedding with correct dimension', async () => { + const embedding = await generateEmbedding('test text'); + + expect(embedding).toBeInstanceOf(Array); + expect(embedding.length).toBe(getEmbeddingDimension()); + expect(embedding.length).toBe(384); // all-MiniLM-L6-v2 dimension + }); + + it('should generate normalized embeddings', async () => { + const embedding = await generateEmbedding('test text'); + + // Check normalization (magnitude should be ~1) + const magnitude = Math.sqrt( + embedding.reduce((sum, val) => sum + val * val, 0) + ); + expect(magnitude).toBeCloseTo(1, 2); + }); + + it('should handle long text by truncation', async () => { + const longText = 'a'.repeat(5000); + const embedding = await generateEmbedding(longText); + + expect(embedding.length).toBe(384); + }); + }); + + describe('generateEmbeddings', () => { + it('should generate embeddings for multiple texts', async () => { + const texts = ['first text', 'second text', 'third text']; + const embeddings = await generateEmbeddings(texts); + + expect(embeddings.length).toBe(3); + for (const embedding of embeddings) { + expect(embedding.length).toBe(384); + } + }); + + it('should handle empty array', async () => { + const embeddings = await generateEmbeddings([]); + expect(embeddings).toEqual([]); + }); + }); + + describe('cosineSimilarity', () => { + it('should return 1 for identical vectors', () => { + const vec = [1, 0, 0]; + expect(cosineSimilarity(vec, vec)).toBeCloseTo(1, 5); + }); + + it('should return 0 for orthogonal vectors', () => { + const vec1 = [1, 0, 0]; + const vec2 = [0, 1, 0]; + expect(cosineSimilarity(vec1, vec2)).toBeCloseTo(0, 5); + }); + + it('should return -1 for opposite vectors', () => { + const vec1 = [1, 0, 0]; + const vec2 = [-1, 0, 0]; + expect(cosineSimilarity(vec1, vec2)).toBeCloseTo(-1, 5); + }); + + it('should throw for vectors of different dimensions', () => { + const vec1 = [1, 0, 0]; + const vec2 = [1, 0]; + expect(() => cosineSimilarity(vec1, vec2)).toThrow(); + }); + }); + + describe('semantic similarity', () => { + it('should find similar texts more similar than unrelated', async () => { + const [embSimilar1, embSimilar2, embDifferent] = await generateEmbeddings([ + 'The quick brown fox jumps over the lazy dog', + 'A fast brown fox leaps over a sleepy dog', + 'TypeScript is a programming language', + ]); + + const similarityClose = cosineSimilarity(embSimilar1, embSimilar2); + const similarityFar = cosineSimilarity(embSimilar1, embDifferent); + + expect(similarityClose).toBeGreaterThan(similarityFar); + expect(similarityClose).toBeGreaterThan(0.7); // High similarity + expect(similarityFar).toBeLessThan(0.5); // Low similarity + }); + + it('should match error messages semantically', async () => { + const [embError, embRelated, embUnrelated] = await generateEmbeddings([ + 'Stripe webhook signature verification failed', + 'Invalid signature error in webhook validation', + 'How to cook pasta in boiling water', + ]); + + const relatedSim = cosineSimilarity(embError, embRelated); + const unrelatedSim = cosineSimilarity(embError, embUnrelated); + + expect(relatedSim).toBeGreaterThan(unrelatedSim); + expect(relatedSim).toBeGreaterThan(0.5); + }); + }); +}); diff --git a/tools/embeddings/tests/fixtures/sample-claude.md b/tools/embeddings/tests/fixtures/sample-claude.md new file mode 100644 index 0000000..05bef48 --- /dev/null +++ b/tools/embeddings/tests/fixtures/sample-claude.md @@ -0,0 +1,26 @@ +# CLAUDE.md + +Test context file for unit tests. + +## Project Conventions + +### Code Style + +Use TypeScript for all files. +Follow the team's style guide. + +### Testing + +- Unit tests: `*.test.ts` +- Integration tests: `*.integration.test.ts` + +## Build Commands + +```bash +npm run build +npm run test +``` + +## Notes + +Some additional notes about the project. diff --git a/tools/embeddings/tests/fixtures/sample-skill.md b/tools/embeddings/tests/fixtures/sample-skill.md new file mode 100644 index 0000000..f072900 --- /dev/null +++ b/tools/embeddings/tests/fixtures/sample-skill.md @@ -0,0 +1,52 @@ +--- +name: test-skill +description: | + A test skill for unit testing. + Use when: testing the chunker, running unit tests. + Helps with: validating skill parsing works correctly. +author: Memory Forge +version: 1.0.0 +date: 2025-01-28 +--- + +# Test Skill + +## Problem + +This is a sample problem description for testing purposes. +It explains what issue this skill addresses. + +## Trigger Conditions + +When to activate this skill: + +- Error: `Test error message` +- Error: `Another test error` +- When running unit tests + +## Solution + +### Step 1: First Action + +Do the first thing to solve the problem. + +```typescript +const example = 'code'; +``` + +### Step 2: Second Action + +Continue with the second step. + +## Verification + +How to confirm the solution worked: + +1. Run the tests +2. Check the output +3. Verify success + +## Notes + +- This is a test skill +- Only for testing purposes diff --git a/tools/embeddings/tests/search.test.ts b/tools/embeddings/tests/search.test.ts new file mode 100644 index 0000000..bbe2634 --- /dev/null +++ b/tools/embeddings/tests/search.test.ts @@ -0,0 +1,174 @@ +import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { search, searchUnique, formatResults, formatForContext } from '../src/search.js'; +import { syncProject } from '../src/sync.js'; +import { closeDatabase } from '../src/db.js'; +import { preloadModel } from '../src/embeddings.js'; + +describe('search', () => { + let tempDir: string; + + beforeAll(async () => { + await preloadModel(); + }, 120000); + + beforeEach(() => { + closeDatabase(); // Ensure clean state + tempDir = mkdtempSync(join(tmpdir(), 'memory-forge-search-')); + + // Create skill directory structure + const skillDir = join(tempDir, '.claude', 'skills', 'test-skill'); + mkdirSync(skillDir, { recursive: true }); + + // Write a test skill + writeFileSync( + join(skillDir, 'SKILL.md'), + `--- +name: stripe-webhook-fix +description: | + Fix for Stripe webhook signature validation failures. + Use when: webhook signature verification failed, Invalid signature. +author: Test +version: 1.0.0 +date: 2025-01-28 +--- + +# Stripe Webhook Fix + +## Problem + +Stripe webhook signature validation fails even with correct secret. + +## Trigger Conditions + +- Error: Stripe webhook signature verification failed +- Error: Invalid signature +- HTTP 400 on webhook endpoint + +## Solution + +Use raw body for signature verification. + +## Verification + +1. Send test webhook +2. Check for success +` + ); + + // Write a CLAUDE.md + writeFileSync( + join(tempDir, 'CLAUDE.md'), + `# CLAUDE.md + +## Code Style + +Use TypeScript with strict mode enabled. + +## Testing + +Run tests with npm test. +` + ); + }); + + afterEach(() => { + closeDatabase(); + if (tempDir) { + rmSync(tempDir, { recursive: true, force: true }); + } + }); + + describe('search', () => { + it('should find relevant skills by semantic search', async () => { + await syncProject(tempDir); + + const results = await search(tempDir, 'webhook validation error'); + + expect(results.length).toBeGreaterThan(0); + + // Should find the stripe webhook skill + const hasStripeSkill = results.some( + (r) => r.chunk.metadata.skillName === 'stripe-webhook-fix' + ); + expect(hasStripeSkill).toBe(true); + }); + + it('should rank trigger conditions highly', async () => { + await syncProject(tempDir); + + const results = await search(tempDir, 'Stripe webhook signature verification failed'); + + // Trigger chunk should be in results with high score + const triggerResult = results.find((r) => r.chunk.chunkType === 'trigger'); + expect(triggerResult).toBeDefined(); + expect(triggerResult!.score).toBeGreaterThan(0.5); + }); + + it('should respect limit option', async () => { + await syncProject(tempDir); + + const results = await search(tempDir, 'test', { limit: 2 }); + + expect(results.length).toBeLessThanOrEqual(2); + }); + + it('should filter by source type', async () => { + await syncProject(tempDir); + + const skillResults = await search(tempDir, 'test', { sourceTypes: ['skill'] }); + const claudeResults = await search(tempDir, 'test', { sourceTypes: ['claude-md'] }); + + for (const r of skillResults) { + expect(r.chunk.sourceType).toBe('skill'); + } + for (const r of claudeResults) { + expect(r.chunk.sourceType).toBe('claude-md'); + } + }); + }); + + describe('searchUnique', () => { + it('should return at most one result per source file', async () => { + await syncProject(tempDir); + + const results = await searchUnique(tempDir, 'webhook'); + + const sourceFiles = results.map((r) => r.sourceFile); + const uniqueSourceFiles = [...new Set(sourceFiles)]; + + expect(sourceFiles.length).toBe(uniqueSourceFiles.length); + }); + }); + + describe('formatResults', () => { + it('should format empty results', () => { + const output = formatResults([]); + expect(output).toContain('No matching results'); + }); + + it('should format results with file and score', async () => { + await syncProject(tempDir); + const results = await search(tempDir, 'webhook', { limit: 1 }); + const output = formatResults(results); + + expect(output).toContain('Found'); + expect(output).toContain('result'); + expect(output).toContain('Score:'); + }); + }); + + describe('formatForContext', () => { + it('should format for AI context injection', async () => { + await syncProject(tempDir); + const results = await search(tempDir, 'webhook', { limit: 1 }); + const output = formatForContext(results); + + expect(output).toContain('Relevant Knowledge'); + expect(output).toContain('Source:'); + expect(output).toContain('Relevance:'); + }); + }); +}); diff --git a/tools/embeddings/tsconfig.json b/tools/embeddings/tsconfig.json new file mode 100644 index 0000000..effbece --- /dev/null +++ b/tools/embeddings/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "examples"] +} diff --git a/tools/embeddings/vitest.config.ts b/tools/embeddings/vitest.config.ts new file mode 100644 index 0000000..3805d7e --- /dev/null +++ b/tools/embeddings/vitest.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['src/**/*.test.ts', 'tests/**/*.test.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + include: ['src/**/*.ts'], + exclude: ['src/index.ts', 'src/**/*.test.ts'], + }, + testTimeout: 60000, // Embedding model loading can be slow + }, +}); From 519f5c221c94528da8a3692ebe9755285de8374c Mon Sep 17 00:00:00 2001 From: javicasper Date: Wed, 28 Jan 2026 17:03:55 +0100 Subject: [PATCH 2/2] chore: use master branch in CI workflow --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2f9b55..0d61993 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,12 +2,12 @@ name: CI on: push: - branches: [main, master] + branches: [master] paths: - 'tools/embeddings/**' - '.github/workflows/ci.yml' pull_request: - branches: [main, master] + branches: [master] paths: - 'tools/embeddings/**' - '.github/workflows/ci.yml'