diff --git a/.gitignore b/.gitignore index 06d1697..fbb3d46 100644 --- a/.gitignore +++ b/.gitignore @@ -89,6 +89,10 @@ docker-compose.override.yml temp/ tmp/ +# Batch processing error files (may contain API tokens from templates) +docs/batch_*.jsonl +**/batch_*_error.jsonl + # Database files # Database files - nodes.db is now tracked directly # data/*.db diff --git a/MEMORY_TEMPLATE_UPDATE.md b/MEMORY_TEMPLATE_UPDATE.md new file mode 100644 index 0000000..91c2b9c --- /dev/null +++ b/MEMORY_TEMPLATE_UPDATE.md @@ -0,0 +1,336 @@ +# Template Update Process - Quick Reference + +## Overview + +The n8n-mcp project maintains a database of workflow templates from n8n.io. This guide explains how to update the template database incrementally without rebuilding from scratch. + +## Current Database State + +As of the last update: +- **2,598 templates** in database +- Templates from the last 12 months +- Latest template: September 12, 2025 + +## Quick Commands + +### Incremental Update (Recommended) +```bash +# Build if needed +npm run build + +# Fetch only NEW templates (5-10 minutes) +npm run fetch:templates:update +``` + +### Full Rebuild (Rare) +```bash +# Rebuild entire database from scratch (30-40 minutes) +npm run fetch:templates +``` + +## How It Works + +### Incremental Update Mode (`--update`) + +The incremental update is **smart and efficient**: + +1. **Loads existing template IDs** from database (~2,598 templates) +2. **Fetches template list** from n8n.io API (all templates from last 12 months) +3. **Filters** to find only NEW templates not in database +4. **Fetches details** for new templates only (saves time and API calls) +5. **Saves** new templates to database (existing ones untouched) +6. **Rebuilds FTS5** search index for new templates + +### Key Benefits + +✅ **Non-destructive**: All existing templates preserved +✅ **Fast**: Only fetches new templates (5-10 min vs 30-40 min) +✅ **API friendly**: Reduces load on n8n.io API +✅ **Safe**: Preserves AI-generated metadata +✅ **Smart**: Automatically skips duplicates + +## Performance Comparison + +| Mode | Templates Fetched | Time | Use Case | +|------|------------------|------|----------| +| **Update** | Only new (~50-200) | 5-10 min | Regular updates | +| **Rebuild** | All (~8000+) | 30-40 min | Initial setup or corruption | + +## Command Options + +### Basic Update +```bash +npm run fetch:templates:update +``` + +### Full Rebuild +```bash +npm run fetch:templates +``` + +### With Metadata Generation +```bash +# Update templates and generate AI metadata +npm run fetch:templates -- --update --generate-metadata + +# Or just generate metadata for existing templates +npm run fetch:templates -- --metadata-only +``` + +### Help +```bash +npm run fetch:templates -- --help +``` + +## Update Frequency + +Recommended update schedule: +- **Weekly**: Run incremental update to get latest templates +- **Monthly**: Review database statistics +- **As needed**: Rebuild only if database corruption suspected + +## Template Filtering + +The fetcher automatically filters templates: +- ✅ **Includes**: Templates from last 12 months +- ✅ **Includes**: Templates with >10 views +- ❌ **Excludes**: Templates with ≤10 views (too niche) +- ❌ **Excludes**: Templates older than 12 months + +## Workflow + +### Regular Update Workflow + +```bash +# 1. Check current state +sqlite3 data/nodes.db "SELECT COUNT(*) FROM templates" + +# 2. Build project (if code changed) +npm run build + +# 3. Run incremental update +npm run fetch:templates:update + +# 4. Verify new templates added +sqlite3 data/nodes.db "SELECT COUNT(*) FROM templates" +``` + +### After n8n Dependency Update + +When you update n8n dependencies, templates remain compatible: +```bash +# 1. Update n8n (from MEMORY_N8N_UPDATE.md) +npm run update:all + +# 2. Fetch new templates incrementally +npm run fetch:templates:update + +# 3. Check how many templates were added +sqlite3 data/nodes.db "SELECT COUNT(*) FROM templates" + +# 4. Generate AI metadata for new templates (optional, requires OPENAI_API_KEY) +npm run fetch:templates -- --metadata-only + +# 5. IMPORTANT: Sanitize templates before pushing database +npm run build +npm run sanitize:templates +``` + +Templates are independent of n8n version - they're just workflow JSON data. + +**CRITICAL**: Always run `npm run sanitize:templates` before pushing the database to remove API tokens from template workflows. + +**Note**: New templates fetched via `--update` mode will NOT have AI-generated metadata by default. You need to run `--metadata-only` separately to generate metadata for templates that don't have it yet. + +## Troubleshooting + +### No New Templates Found + +This is normal! It means: +- All recent templates are already in your database +- n8n.io hasn't published many new templates recently +- Your database is up to date + +```bash +📊 Update mode: 0 new templates to fetch (skipping 2598 existing) +✅ All templates already have metadata +``` + +### API Rate Limiting + +If you hit rate limits: +- The fetcher includes built-in delays (150ms between requests) +- Wait a few minutes and try again +- Use `--update` mode instead of full rebuild + +### Database Corruption + +If you suspect corruption: +```bash +# Full rebuild from scratch +npm run fetch:templates + +# This will: +# - Drop and recreate templates table +# - Fetch all templates fresh +# - Rebuild search indexes +``` + +## Database Schema + +Templates are stored with: +- Basic info (id, name, description, author, views, created_at) +- Node types used (JSON array) +- Complete workflow (gzip compressed, base64 encoded) +- AI-generated metadata (optional, requires OpenAI API key) +- FTS5 search index for fast text search + +## Metadata Generation + +Generate AI metadata for templates: +```bash +# Requires OPENAI_API_KEY in .env +export OPENAI_API_KEY="sk-..." + +# Generate for templates without metadata (recommended after incremental update) +npm run fetch:templates -- --metadata-only + +# Generate during template fetch (slower, but automatic) +npm run fetch:templates:update -- --generate-metadata +``` + +**Important**: Incremental updates (`--update`) do NOT generate metadata by default. After running `npm run fetch:templates:update`, you'll have new templates without metadata. Run `--metadata-only` separately to generate metadata for them. + +### Check Metadata Coverage + +```bash +# See how many templates have metadata +sqlite3 data/nodes.db "SELECT + COUNT(*) as total, + SUM(CASE WHEN metadata_json IS NOT NULL THEN 1 ELSE 0 END) as with_metadata, + SUM(CASE WHEN metadata_json IS NULL THEN 1 ELSE 0 END) as without_metadata +FROM templates" + +# See recent templates without metadata +sqlite3 data/nodes.db "SELECT id, name, created_at +FROM templates +WHERE metadata_json IS NULL +ORDER BY created_at DESC +LIMIT 10" +``` + +Metadata includes: +- Categories +- Complexity level (simple/medium/complex) +- Use cases +- Estimated setup time +- Required services +- Key features +- Target audience + +### Metadata Generation Troubleshooting + +If metadata generation fails: + +1. **Check error file**: Errors are saved to `temp/batch/batch_*_error.jsonl` +2. **Common issues**: + - `"Unsupported value: 'temperature'"` - Model doesn't support custom temperature + - `"Invalid request"` - Check OPENAI_API_KEY is valid + - Model availability issues +3. **Model**: Uses `gpt-5-mini-2025-08-07` by default +4. **Token limit**: 3000 tokens per request for detailed metadata + +The system will automatically: +- Process error files and assign default metadata to failed templates +- Save error details for debugging +- Continue processing even if some templates fail + +**Example error handling**: +```bash +# If you see: "No output file available for batch job" +# Check: temp/batch/batch_*_error.jsonl for error details +# The system now automatically processes errors and generates default metadata +``` + +## Environment Variables + +Optional configuration: +```bash +# OpenAI for metadata generation +OPENAI_API_KEY=sk-... +OPENAI_MODEL=gpt-4o-mini # Default model +OPENAI_BATCH_SIZE=50 # Batch size for metadata generation + +# Metadata generation limits +METADATA_LIMIT=100 # Max templates to process (0 = all) +``` + +## Statistics + +After update, check stats: +```bash +# Template count +sqlite3 data/nodes.db "SELECT COUNT(*) FROM templates" + +# Most recent template +sqlite3 data/nodes.db "SELECT MAX(created_at) FROM templates" + +# Templates by view count +sqlite3 data/nodes.db "SELECT COUNT(*), + CASE + WHEN views < 50 THEN '<50' + WHEN views < 100 THEN '50-100' + WHEN views < 500 THEN '100-500' + ELSE '500+' + END as view_range + FROM templates GROUP BY view_range" +``` + +## Integration with n8n-mcp + +Templates are available through MCP tools: +- `list_templates`: List all templates +- `get_template`: Get specific template with workflow +- `search_templates`: Search by keyword +- `list_node_templates`: Templates using specific nodes +- `get_templates_for_task`: Templates for common tasks +- `search_templates_by_metadata`: Advanced filtering + +See `npm run test:templates` for usage examples. + +## Time Estimates + +Typical incremental update: +- Loading existing IDs: 1-2 seconds +- Fetching template list: 2-3 minutes +- Filtering new templates: instant +- Fetching details for 100 new templates: ~15 seconds (0.15s each) +- Saving and indexing: 5-10 seconds +- **Total: 3-5 minutes** + +Full rebuild: +- Fetching 8000+ templates: 25-30 minutes +- Saving and indexing: 5-10 minutes +- **Total: 30-40 minutes** + +## Best Practices + +1. **Use incremental updates** for regular maintenance +2. **Rebuild only when necessary** (corruption, major changes) +3. **Generate metadata incrementally** to avoid OpenAI costs +4. **Monitor template count** to verify updates working +5. **Keep database backed up** before major operations + +## Next Steps + +After updating templates: +1. Test template search: `npm run test:templates` +2. Verify MCP tools work: Test in Claude Desktop +3. Check statistics in database +4. Commit changes if desired (database changes) + +## Related Documentation + +- `MEMORY_N8N_UPDATE.md` - Updating n8n dependencies +- `CLAUDE.md` - Project overview and architecture +- `README.md` - User documentation \ No newline at end of file diff --git a/README.md b/README.md index 611bd92..153ce0e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![npm version](https://img.shields.io/npm/v/n8n-mcp.svg)](https://www.npmjs.com/package/n8n-mcp) [![codecov](https://codecov.io/gh/czlonkowski/n8n-mcp/graph/badge.svg?token=YOUR_TOKEN)](https://codecov.io/gh/czlonkowski/n8n-mcp) [![Tests](https://img.shields.io/badge/tests-2883%20passing-brightgreen.svg)](https://github.com/czlonkowski/n8n-mcp/actions) -[![n8n version](https://img.shields.io/badge/n8n-^1.112.3-orange.svg)](https://github.com/n8n-io/n8n) +[![n8n version](https://img.shields.io/badge/n8n-^1.113.3-orange.svg)](https://github.com/n8n-io/n8n) [![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fczlonkowski%2Fn8n--mcp-green.svg)](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp) [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp) diff --git a/data/nodes.db b/data/nodes.db index 18a711e..0c9fc84 100644 Binary files a/data/nodes.db and b/data/nodes.db differ diff --git a/package-lock.json b/package-lock.json index 861b3ab..2716080 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,23 @@ { "name": "n8n-mcp", - "version": "2.13.2", + "version": "2.14.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "n8n-mcp", - "version": "2.13.2", + "version": "2.14.3", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.13.2", - "@n8n/n8n-nodes-langchain": "^1.111.1", + "@n8n/n8n-nodes-langchain": "^1.112.2", "@supabase/supabase-js": "^2.57.4", "dotenv": "^16.5.0", "express": "^5.1.0", "lru-cache": "^11.2.1", - "n8n": "^1.112.3", - "n8n-core": "^1.111.0", - "n8n-workflow": "^1.109.0", + "n8n": "^1.113.3", + "n8n-core": "^1.112.1", + "n8n-workflow": "^1.110.0", "openai": "^4.77.0", "sql.js": "^1.13.0", "uuid": "^10.0.0", @@ -7087,6 +7087,8 @@ "resolved": "https://registry.npmjs.org/@google-ai/generativelanguage/-/generativelanguage-2.6.0.tgz", "integrity": "sha512-T2tULO1/j4Gs1oYF9OMKCGXHE/m7aCPUonav32iu+sA4nN+acy5Z+Sz6yR4EzL+LkPSfkeW0FOjeRGkl5xtwvw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "google-gax": "^4.0.3" }, @@ -7266,6 +7268,109 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@google/genai": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.19.0.tgz", + "integrity": "sha512-mIMV3M/KfzzFA//0fziK472wKBJ1TdJLhozIUJKTPLyTDN1NotU+hyoHW/N0cfrcEWUK20YA0GxCeHC4z0SbMA==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^9.14.2", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.11.4" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@google/genai/node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google/genai/node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google/genai/node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google/genai/node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@google/genai/node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google/genai/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@google/generative-ai": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", @@ -8856,12 +8961,12 @@ } }, "node_modules/@langchain/google-vertexai": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/@langchain/google-vertexai/-/google-vertexai-0.2.13.tgz", - "integrity": "sha512-Y97f0IBr4uWsyJTcDJROWXuu+qh4elSDLK1e6MD+mrxCx+UlgcXCReg4zvEFJzqpBKrfFt+lvXstJ6XTR6Zfyg==", + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@langchain/google-vertexai/-/google-vertexai-0.2.18.tgz", + "integrity": "sha512-oZsOp9Sx4rsFpHH5UiuObo5NYCAqhhmroL3f3pDZ06DB6hpfnNc6XNjdpbmt0AemP6PO/52UlKHeSYtnYlBzIQ==", "license": "MIT", "dependencies": { - "@langchain/google-gauth": "^0.2.10" + "@langchain/google-gauth": "^0.2.18" }, "engines": { "node": ">=18" @@ -9378,9 +9483,9 @@ } }, "node_modules/@n8n/ai-workflow-builder": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@n8n/ai-workflow-builder/-/ai-workflow-builder-0.22.0.tgz", - "integrity": "sha512-KJDyyDbx3Auuu/5VcZhABYXDY5sMKpwI1WgW11eu7qQ6XceBV/69fgG8mAfpqNvaBGoU8zLIpaaGYpAWRmGYVQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@n8n/ai-workflow-builder/-/ai-workflow-builder-0.23.1.tgz", + "integrity": "sha512-F5DGql0WHWDg74ASuA/jX3ubSsVVNHFCOKO43QH0mpQEGF/ed07RkFbMTUb9dsxmdJ+3wUkCbrxCsErp971drQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@langchain/anthropic": "0.3.26", @@ -9388,16 +9493,52 @@ "@langchain/langgraph": "0.2.74", "@langchain/openai": "0.6.7", "@n8n_io/ai-assistant-sdk": "1.15.0", - "@n8n/backend-common": "^0.22.0", - "@n8n/config": "1.55.0", + "@n8n/backend-common": "^0.23.1", + "@n8n/config": "1.56.1", "@n8n/di": "0.9.0", "langsmith": "^0.3.45", "lodash": "4.17.21", - "n8n-workflow": "1.109.0", + "n8n-workflow": "1.110.0", "picocolors": "1.0.1", "zod": "3.25.67" } }, + "node_modules/@n8n/ai-workflow-builder/node_modules/@n8n/config": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@n8n/config/-/config-1.56.1.tgz", + "integrity": "sha512-3Y84MfY8v477e9GfJjghyYI+35vpxEJotIW2IpYzk2LM9asdrUMpFqWT0AAmIKUZnEsDmDCwWwxrzF1lHNN6xQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/di": "0.9.0", + "reflect-metadata": "0.2.2", + "zod": "3.25.67" + } + }, + "node_modules/@n8n/ai-workflow-builder/node_modules/n8n-workflow": { + "version": "1.110.0", + "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-1.110.0.tgz", + "integrity": "sha512-xAwh9qLh2JwYWcUufHXD0M0zIxlAQ5iHuzYQh6mxjnrQ8yW5iA+Fparn52vqj/CNf9CsqKBYyOV4V2O12L4A1A==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/errors": "^0.5.0", + "@n8n/tournament": "1.0.6", + "ast-types": "0.15.2", + "callsites": "3.1.0", + "esprima-next": "5.8.4", + "form-data": "4.0.0", + "jmespath": "0.16.0", + "js-base64": "3.7.2", + "jssha": "3.3.1", + "lodash": "4.17.21", + "luxon": "3.4.4", + "md5": "2.3.0", + "recast": "0.22.0", + "title-case": "3.0.3", + "transliteration": "2.3.5", + "xml2js": "0.6.2", + "zod": "3.25.67" + } + }, "node_modules/@n8n/ai-workflow-builder/node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -9414,18 +9555,43 @@ } }, "node_modules/@n8n/api-types": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@n8n/api-types/-/api-types-0.46.0.tgz", - "integrity": "sha512-aY75QfIRzmZZQEo8yUgNx2lxJkYVOgO8slbiLLY6tehKdzeTHOms52PldarWmoY7eTncPcoRzNjmag0TBi9n0A==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@n8n/api-types/-/api-types-0.47.0.tgz", + "integrity": "sha512-Ferp8byymCtgIGQtI8CIocDGOrwdG5TyGnJNBg7b5OmJXWNQOgr7zqlgTH6vGLclmrA30v1skVZlct8U7iA6pQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@n8n/permissions": "0.35.0", - "n8n-workflow": "1.109.0", + "@n8n/permissions": "0.36.0", + "n8n-workflow": "1.110.0", "xss": "1.0.15", "zod": "3.25.67", "zod-class": "0.0.16" } }, + "node_modules/@n8n/api-types/node_modules/n8n-workflow": { + "version": "1.110.0", + "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-1.110.0.tgz", + "integrity": "sha512-xAwh9qLh2JwYWcUufHXD0M0zIxlAQ5iHuzYQh6mxjnrQ8yW5iA+Fparn52vqj/CNf9CsqKBYyOV4V2O12L4A1A==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/errors": "^0.5.0", + "@n8n/tournament": "1.0.6", + "ast-types": "0.15.2", + "callsites": "3.1.0", + "esprima-next": "5.8.4", + "form-data": "4.0.0", + "jmespath": "0.16.0", + "js-base64": "3.7.2", + "jssha": "3.3.1", + "lodash": "4.17.21", + "luxon": "3.4.4", + "md5": "2.3.0", + "recast": "0.22.0", + "title-case": "3.0.3", + "transliteration": "2.3.5", + "xml2js": "0.6.2", + "zod": "3.25.67" + } + }, "node_modules/@n8n/api-types/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", @@ -9436,17 +9602,17 @@ } }, "node_modules/@n8n/backend-common": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@n8n/backend-common/-/backend-common-0.22.0.tgz", - "integrity": "sha512-qH9X+NTXTmqtkzdKH+yYEmZZ+FQZB3yuQbLUOesjWGlwLu3YyHSfLutsKk5tds++v1njNE+0qsKl8CX6LdAS/A==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@n8n/backend-common/-/backend-common-0.23.1.tgz", + "integrity": "sha512-J3DpnYFkWHpA/bUrjnPrQWnl5rJKXZiJJF3sWOxDbN5Cy++5uzZCqsJR4GppzYA08AFYRBC5MDD2Qu441YmSNQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@n8n/config": "^1.55.0", + "@n8n/config": "^1.56.1", "@n8n/constants": "^0.12.0", - "@n8n/decorators": "^0.22.0", + "@n8n/decorators": "^0.23.0", "@n8n/di": "^0.9.0", "callsites": "3.1.0", - "n8n-workflow": "^1.109.0", + "n8n-workflow": "^1.110.0", "picocolors": "1.0.1", "reflect-metadata": "0.2.2", "winston": "3.14.2", @@ -9496,20 +9662,20 @@ } }, "node_modules/@n8n/backend-test-utils": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@n8n/backend-test-utils/-/backend-test-utils-0.15.1.tgz", - "integrity": "sha512-G8cW1d0QGJpfA6KmZunhnptVtpswaSEb+ZUdCAML4nqhLFX5RN1QS3l5uESRMZpYQWHNdVWG5hqtHssEsOZucg==", + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@n8n/backend-test-utils/-/backend-test-utils-0.16.1.tgz", + "integrity": "sha512-2DzBP6/aOdJV0yQ+KU9oEA5q5idM/ERQfl4LbrmYZPNZZajLbKhc8kRw+ia9m4D4hKnRYup0xd9d/rAjygpymw==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@n8n/backend-common": "^0.22.0", - "@n8n/config": "^1.55.0", + "@n8n/backend-common": "^0.23.1", + "@n8n/config": "^1.56.1", "@n8n/constants": "^0.12.0", - "@n8n/db": "^0.23.1", + "@n8n/db": "^0.24.1", "@n8n/di": "^0.9.0", - "@n8n/permissions": "^0.35.0", + "@n8n/permissions": "^0.36.0", "@n8n/typeorm": "0.3.20-12", "jest-mock-extended": "^3.0.4", - "n8n-workflow": "^1.109.0", + "n8n-workflow": "^1.110.0", "reflect-metadata": "0.2.2", "uuid": "10.0.0" } @@ -10427,29 +10593,45 @@ } }, "node_modules/@n8n/client-oauth2": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/@n8n/client-oauth2/-/client-oauth2-0.29.0.tgz", - "integrity": "sha512-odf1C6ET1HwL64sA2HRxSOm4LLODdppCTv52IyaE4CkoX0Ghr5lPOMXi1qrt8RiQe1lsQwrFuQQYgVr0IZnAkQ==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@n8n/client-oauth2/-/client-oauth2-0.30.0.tgz", + "integrity": "sha512-XoRl9UQqKbaTa+cahewbZPkwlsyqSq7yK6HJ5W7t/zQVvH+emYPGXCb6EdRM6DLAjnfE5oifTjuLile8flccmA==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "axios": "1.8.3" + "axios": "1.12.0" } }, "node_modules/@n8n/client-oauth2/node_modules/axios": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", - "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.0.tgz", + "integrity": "sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, + "node_modules/@n8n/client-oauth2/node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@n8n/config": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/@n8n/config/-/config-1.55.0.tgz", - "integrity": "sha512-4C2WHUC4UxBM6F+bAQEJASvMkUrfcIirTquPwQlturf2kBXPxfNp3rKZbvYiZLZHtuPyflnUsUYp936KDjZlAg==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@n8n/config/-/config-1.57.0.tgz", + "integrity": "sha512-aUV6E0qtU14zoNIkKg6CdwmWSZCHMa/R2ze+jTj8DQ9zNnn5iwYYCf4pIzum0xHiz/IF2O3qIIEV+uoMQDkIqA==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@n8n/di": "0.9.0", @@ -10473,24 +10655,24 @@ "license": "SEE LICENSE IN LICENSE.md" }, "node_modules/@n8n/db": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@n8n/db/-/db-0.23.1.tgz", - "integrity": "sha512-ovcSdzkHKssL2DBX1W+zREqn7W1NiYmo2fWrmZiBNgZgGM8lCyURDXyKcFYuDtPLYolXbGpKG2J1HREhJMI2Lw==", + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@n8n/db/-/db-0.24.1.tgz", + "integrity": "sha512-Tptm7TIp5h7+Zr9+mbwiA8qPZnxcgDXc1p0cIEb0zh91DdzSdZUmx4x4cz2xTfzYIumwPtk8xfMCeRMBgmkyGA==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@n8n/api-types": "^0.46.0", - "@n8n/backend-common": "^0.22.0", - "@n8n/config": "^1.55.0", + "@n8n/api-types": "^0.47.0", + "@n8n/backend-common": "^0.23.1", + "@n8n/config": "^1.56.1", "@n8n/constants": "^0.12.0", - "@n8n/decorators": "^0.22.0", + "@n8n/decorators": "^0.23.0", "@n8n/di": "^0.9.0", - "@n8n/permissions": "^0.35.0", + "@n8n/permissions": "^0.36.0", "@n8n/typeorm": "0.3.20-12", "class-validator": "0.14.0", "flatted": "3.2.7", "lodash": "4.17.21", - "n8n-core": "^1.111.0", - "n8n-workflow": "^1.109.0", + "n8n-core": "^1.112.1", + "n8n-workflow": "^1.110.0", "nanoid": "3.3.8", "p-lazy": "3.1.0", "reflect-metadata": "0.2.2", @@ -11427,16 +11609,16 @@ } }, "node_modules/@n8n/decorators": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@n8n/decorators/-/decorators-0.22.0.tgz", - "integrity": "sha512-MgPIdn6RxElzHV/OEw8KsIzQIctdZ6sr3tJvnJ++Z1IhNlTPecYpbmgySEbTrqDFQBsULQLDRVIo4UaLayTD3g==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@n8n/decorators/-/decorators-0.23.0.tgz", + "integrity": "sha512-HddkcRzd38ofM7ZuQsEotFtTh/oTlDdEo+xLqsg5sCF04TvHBSaVjc12bygi0rURpwO1trVBWEkXsvB38HgDSw==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@n8n/constants": "^0.12.0", "@n8n/di": "^0.9.0", - "@n8n/permissions": "^0.35.0", + "@n8n/permissions": "^0.36.0", "lodash": "4.17.21", - "n8n-workflow": "^1.109.0" + "n8n-workflow": "^1.110.0" } }, "node_modules/@n8n/di": { @@ -11505,17 +11687,17 @@ } }, "node_modules/@n8n/n8n-nodes-langchain": { - "version": "1.111.1", - "resolved": "https://registry.npmjs.org/@n8n/n8n-nodes-langchain/-/n8n-nodes-langchain-1.111.1.tgz", - "integrity": "sha512-3Lz9njuuwIcAh8IYHYz4humrP1AMcZO1ZanACp2rIciQcE7Yg7QA/hvlKNdJ7QBMEQTD772IRq9/mtyO7Uc+Mw==", + "version": "1.113.0", + "resolved": "https://registry.npmjs.org/@n8n/n8n-nodes-langchain/-/n8n-nodes-langchain-1.113.0.tgz", + "integrity": "sha512-rW2kk+meknWxc99wE9YmcNnZbLuA0OKZrhAB8osLHs2i6ocSkfRYCyBE803ZMkOnAAbs7hqwZqdbwb9Ftnpx9Q==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@aws-sdk/client-sso-oidc": "3.808.0", "@azure/identity": "4.3.0", "@getzep/zep-cloud": "1.0.12", "@getzep/zep-js": "0.9.0", - "@google-ai/generativelanguage": "2.6.0", "@google-cloud/resource-manager": "5.3.0", + "@google/genai": "1.19.0", "@google/generative-ai": "0.21.0", "@huggingface/inference": "4.0.5", "@langchain/anthropic": "0.3.26", @@ -11524,7 +11706,7 @@ "@langchain/community": "0.3.50", "@langchain/core": "0.3.68", "@langchain/google-genai": "0.2.17", - "@langchain/google-vertexai": "0.2.13", + "@langchain/google-vertexai": "0.2.18", "@langchain/groq": "0.2.3", "@langchain/mistralai": "0.2.1", "@langchain/mongodb": "^0.1.0", @@ -11537,8 +11719,8 @@ "@langchain/weaviate": "0.2.0", "@modelcontextprotocol/sdk": "1.12.0", "@mozilla/readability": "0.6.0", - "@n8n/client-oauth2": "0.29.0", - "@n8n/config": "1.55.0", + "@n8n/client-oauth2": "0.30.0", + "@n8n/config": "1.57.0", "@n8n/di": "0.9.0", "@n8n/errors": "^0.5.0", "@n8n/json-schema-to-zod": "1.5.0", @@ -11567,8 +11749,8 @@ "mammoth": "1.7.2", "mime-types": "2.1.35", "mongodb": "6.11.0", - "n8n-nodes-base": "1.110.1", - "n8n-workflow": "1.109.0", + "n8n-nodes-base": "1.112.0", + "n8n-workflow": "1.111.0", "openai": "5.12.2", "pdf-parse": "1.1.1", "pg": "8.12.0", @@ -12609,9 +12791,9 @@ "license": "MIT" }, "node_modules/@n8n/permissions": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@n8n/permissions/-/permissions-0.35.0.tgz", - "integrity": "sha512-VSfCdikOH+IbHl4tsYbi/rIoxL1wJUJlu/rguZ3oR8SaoDaa4bbIf9H8o3NhAyiFsWabWRU57ct941kGoxzT6Q==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@n8n/permissions/-/permissions-0.36.0.tgz", + "integrity": "sha512-g0R6PpqhkANyHW4f3tQManRw897+w7FyLrWlfxbF+kuv9gk4AmQ2ZIKM4uLLgRb+0dPCka6k7Qv3PvpZ/uSPJQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "zod": "3.25.67" @@ -12627,12 +12809,12 @@ } }, "node_modules/@n8n/task-runner": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/@n8n/task-runner/-/task-runner-1.48.0.tgz", - "integrity": "sha512-m9JT8CLxBfCAi31Ldq5a/kQII1wUrou3vdKDzeJzZLtFec0XJZ7XlBcussjZHtMmL/mymt2eOD9ZG6+h4rXJxA==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@n8n/task-runner/-/task-runner-1.49.1.tgz", + "integrity": "sha512-Ht2NOw9p1dp/VEBl++C2zKzHGSW1qHTzCSJA0hBSxU9EWcLqKZYaB25bAxlwcSx/JA1371LaAqPTdVbnORO+yg==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@n8n/config": "1.55.0", + "@n8n/config": "1.56.1", "@n8n/di": "0.9.0", "@n8n/errors": "^0.5.0", "@sentry/node": "^9.42.1", @@ -12640,12 +12822,43 @@ "acorn-walk": "8.3.4", "lodash": "4.17.21", "luxon": "3.4.4", - "n8n-core": "1.111.0", - "n8n-workflow": "1.109.0", + "n8n-core": "1.112.1", + "n8n-workflow": "1.110.0", "nanoid": "3.3.8", "ws": "^8.18.0" } }, + "node_modules/@n8n/task-runner/node_modules/@n8n/client-oauth2": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@n8n/client-oauth2/-/client-oauth2-0.29.0.tgz", + "integrity": "sha512-odf1C6ET1HwL64sA2HRxSOm4LLODdppCTv52IyaE4CkoX0Ghr5lPOMXi1qrt8RiQe1lsQwrFuQQYgVr0IZnAkQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "axios": "1.8.3" + } + }, + "node_modules/@n8n/task-runner/node_modules/@n8n/client-oauth2/node_modules/axios": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", + "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@n8n/task-runner/node_modules/@n8n/config": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@n8n/config/-/config-1.56.1.tgz", + "integrity": "sha512-3Y84MfY8v477e9GfJjghyYI+35vpxEJotIW2IpYzk2LM9asdrUMpFqWT0AAmIKUZnEsDmDCwWwxrzF1lHNN6xQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/di": "0.9.0", + "reflect-metadata": "0.2.2", + "zod": "3.25.67" + } + }, "node_modules/@n8n/task-runner/node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -12658,6 +12871,218 @@ "node": ">=0.4.0" } }, + "node_modules/@n8n/task-runner/node_modules/axios": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.0.tgz", + "integrity": "sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@n8n/task-runner/node_modules/axios/node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@n8n/task-runner/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@n8n/task-runner/node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/@n8n/task-runner/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@n8n/task-runner/node_modules/n8n-core": { + "version": "1.112.1", + "resolved": "https://registry.npmjs.org/n8n-core/-/n8n-core-1.112.1.tgz", + "integrity": "sha512-6kPFzwysE+oUFSUDVkiA/m5VzuuEFXR4dnx4Snhr2T/4cd7eFRhWxSzpQPfGWKB+N23LdiLxZRr43MVggCm4mg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@aws-sdk/client-s3": "3.808.0", + "@langchain/core": "0.3.68", + "@n8n/backend-common": "^0.23.1", + "@n8n/client-oauth2": "0.29.0", + "@n8n/config": "1.56.1", + "@n8n/constants": "0.12.0", + "@n8n/decorators": "0.23.0", + "@n8n/di": "0.9.0", + "@sentry/node": "^9.42.1", + "@sentry/node-native": "^9.42.1", + "axios": "1.12.0", + "callsites": "3.1.0", + "chardet": "2.0.0", + "cron": "3.1.7", + "fast-glob": "3.2.12", + "file-type": "16.5.4", + "form-data": "4.0.0", + "htmlparser2": "^10.0.0", + "http-proxy-agent": "7.0.2", + "https-proxy-agent": "7.0.6", + "iconv-lite": "0.6.3", + "jsonwebtoken": "9.0.2", + "lodash": "4.17.21", + "luxon": "3.4.4", + "mime-types": "2.1.35", + "n8n-workflow": "1.110.0", + "nanoid": "3.3.8", + "oauth-1.0a": "2.2.6", + "p-cancelable": "2.1.1", + "picocolors": "1.0.1", + "pretty-bytes": "5.6.0", + "proxy-from-env": "^1.1.0", + "qs": "6.11.0", + "ssh2": "1.15.0", + "uuid": "10.0.0", + "winston": "3.14.2", + "xml2js": "0.6.2", + "zod": "3.25.67" + }, + "bin": { + "n8n-copy-static-files": "bin/copy-static-files", + "n8n-generate-metadata": "bin/generate-metadata", + "n8n-generate-translations": "bin/generate-translations" + } + }, + "node_modules/@n8n/task-runner/node_modules/n8n-workflow": { + "version": "1.110.0", + "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-1.110.0.tgz", + "integrity": "sha512-xAwh9qLh2JwYWcUufHXD0M0zIxlAQ5iHuzYQh6mxjnrQ8yW5iA+Fparn52vqj/CNf9CsqKBYyOV4V2O12L4A1A==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/errors": "^0.5.0", + "@n8n/tournament": "1.0.6", + "ast-types": "0.15.2", + "callsites": "3.1.0", + "esprima-next": "5.8.4", + "form-data": "4.0.0", + "jmespath": "0.16.0", + "js-base64": "3.7.2", + "jssha": "3.3.1", + "lodash": "4.17.21", + "luxon": "3.4.4", + "md5": "2.3.0", + "recast": "0.22.0", + "title-case": "3.0.3", + "transliteration": "2.3.5", + "xml2js": "0.6.2", + "zod": "3.25.67" + } + }, + "node_modules/@n8n/task-runner/node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "license": "ISC" + }, + "node_modules/@n8n/task-runner/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@n8n/task-runner/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/@n8n/task-runner/node_modules/winston": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.14.2.tgz", + "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.6.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@n8n/task-runner/node_modules/zod": { + "version": "3.25.67", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", + "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@n8n/tournament": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@n8n/tournament/-/tournament-1.0.6.tgz", @@ -14469,9 +14894,9 @@ } }, "node_modules/@sentry-internal/node-native-stacktrace/node_modules/detect-libc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", - "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", + "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -17642,9 +18067,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", - "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", + "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", "license": "Apache-2.0", "peer": true, "bin": { @@ -18232,9 +18657,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001743", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", - "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "version": "1.0.30001745", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", + "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==", "funding": [ { "type": "opencollective", @@ -19812,9 +20237,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.222", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", - "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", + "version": "1.5.227", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", + "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", "license": "ISC", "peer": true }, @@ -24437,9 +24862,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.12.18", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.18.tgz", - "integrity": "sha512-k0pdkX8DXHqVrby7yJ23WBcHMCX1lhwvX/Uazh0vf3wfGQa0qDIyRB2Z2C01JREGGt8Assfwl1yZduq59OjXXQ==", + "version": "1.12.23", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.23.tgz", + "integrity": "sha512-RN3q3gImZ91BvRDYjWp7ICz3gRn81mW5L4SW+2afzNCC0I/nkXstBgZThQGTE3S/9q5J90FH4dP+TXx8NhdZKg==", "license": "MIT" }, "node_modules/libqp": { @@ -24656,6 +25081,21 @@ "node": "20 || >=22" } }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/luxon": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", @@ -25683,17 +26123,17 @@ } }, "node_modules/mysql2": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz", - "integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.0.tgz", + "integrity": "sha512-tT6pomf5Z/I7Jzxu8sScgrYBMK9bUFWd7Kbo6Fs1L0M13OOIJ/ZobGKS3Z7tQ8Re4lj+LnLXIQVZZxa3fhYKzA==", "license": "MIT", "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", - "iconv-lite": "^0.6.3", + "iconv-lite": "^0.7.0", "long": "^5.2.1", - "lru-cache": "^8.0.0", + "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" @@ -25703,30 +26143,25 @@ } }, "node_modules/mysql2/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" - } - }, - "node_modules/mysql2/node_modules/lru-cache": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", - "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", - "license": "ISC", - "engines": { - "node": ">=16.14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/n8n": { - "version": "1.112.3", - "resolved": "https://registry.npmjs.org/n8n/-/n8n-1.112.3.tgz", - "integrity": "sha512-8pbQ1/CBPNIFoT9+JAOHTk4X2F1oQfxYpUMI2H1hUkPvmegGZ3aKzQ+xFKJU323xmh7nVNCxfs00hHrTYDPmZQ==", + "version": "1.113.3", + "resolved": "https://registry.npmjs.org/n8n/-/n8n-1.113.3.tgz", + "integrity": "sha512-TEhRx11SR23M4gl4XxwLXkdH8kNKTt+A0iHpQp12/aqs4bXu2jC0N06S3SRH0qLRio7U6wNBZn4vJHhqxJVK6w==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@aws-sdk/client-secrets-manager": "3.808.0", @@ -25735,21 +26170,21 @@ "@google-cloud/secret-manager": "5.6.0", "@n8n_io/ai-assistant-sdk": "1.15.0", "@n8n_io/license-sdk": "2.23.0", - "@n8n/ai-workflow-builder": "0.22.0", - "@n8n/api-types": "0.46.0", - "@n8n/backend-common": "^0.22.0", - "@n8n/backend-test-utils": "^0.15.1", + "@n8n/ai-workflow-builder": "0.23.1", + "@n8n/api-types": "0.47.0", + "@n8n/backend-common": "^0.23.1", + "@n8n/backend-test-utils": "^0.16.1", "@n8n/client-oauth2": "0.29.0", - "@n8n/config": "1.55.0", + "@n8n/config": "1.56.1", "@n8n/constants": "^0.12.0", - "@n8n/db": "^0.23.1", - "@n8n/decorators": "0.22.0", + "@n8n/db": "^0.24.1", + "@n8n/decorators": "0.23.0", "@n8n/di": "0.9.0", "@n8n/errors": "0.5.0", "@n8n/localtunnel": "3.0.0", - "@n8n/n8n-nodes-langchain": "1.111.1", - "@n8n/permissions": "0.35.0", - "@n8n/task-runner": "1.48.0", + "@n8n/n8n-nodes-langchain": "1.112.2", + "@n8n/permissions": "0.36.0", + "@n8n/task-runner": "1.49.1", "@n8n/typeorm": "0.3.20-12", "@parcel/watcher": "^2.5.1", "@rudderstack/rudder-sdk-node": "2.1.4", @@ -25788,11 +26223,11 @@ "ldapts": "4.2.6", "lodash": "4.17.21", "luxon": "3.4.4", - "mysql2": "3.11.0", - "n8n-core": "1.111.0", - "n8n-editor-ui": "1.112.1", - "n8n-nodes-base": "1.110.1", - "n8n-workflow": "1.109.0", + "mysql2": "3.15.0", + "n8n-core": "1.112.1", + "n8n-editor-ui": "1.113.1", + "n8n-nodes-base": "1.111.2", + "n8n-workflow": "1.110.0", "nanoid": "3.3.8", "nodemailer": "6.9.9", "oauth-1.0a": "2.2.6", @@ -25837,18 +26272,18 @@ } }, "node_modules/n8n-core": { - "version": "1.111.0", - "resolved": "https://registry.npmjs.org/n8n-core/-/n8n-core-1.111.0.tgz", - "integrity": "sha512-anIWeUlxGAxyfu6nNKQhNQ8rUXhnVoJASJdzyDoydX1vtcjD6Vm4ZEynsj+XYKh14oFZDmE/wEMc+Rg2EiP9yw==", + "version": "1.113.0", + "resolved": "https://registry.npmjs.org/n8n-core/-/n8n-core-1.113.0.tgz", + "integrity": "sha512-Mp3BO3hdWfltj4jheg7OgoM2VAS4RUJP7f8fVUy95BKt1m9ZB2FUv4XP6iWdvwDpmDELWcYxX1pIaumOBn8lmA==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@aws-sdk/client-s3": "3.808.0", "@langchain/core": "0.3.68", - "@n8n/backend-common": "^0.22.0", - "@n8n/client-oauth2": "0.29.0", - "@n8n/config": "1.55.0", - "@n8n/constants": "0.12.0", - "@n8n/decorators": "0.22.0", + "@n8n/backend-common": "^0.24.0", + "@n8n/client-oauth2": "0.30.0", + "@n8n/config": "1.57.0", + "@n8n/constants": "0.13.0", + "@n8n/decorators": "0.24.0", "@n8n/di": "0.9.0", "@sentry/node": "^9.42.1", "@sentry/node-native": "^9.42.1", @@ -25867,7 +26302,7 @@ "lodash": "4.17.21", "luxon": "3.4.4", "mime-types": "2.1.35", - "n8n-workflow": "1.109.0", + "n8n-workflow": "1.111.0", "nanoid": "3.3.8", "oauth-1.0a": "2.2.6", "p-cancelable": "2.1.1", @@ -25887,6 +26322,52 @@ "n8n-generate-translations": "bin/generate-translations" } }, + "node_modules/n8n-core/node_modules/@n8n/backend-common": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@n8n/backend-common/-/backend-common-0.24.0.tgz", + "integrity": "sha512-nqEDrbUeDdyr7nrFzBq2RaxSPq+skfxVsWjBndEW4xEdklgMwIkkGdJ4pUZPnuEa9p+dio0MNVcudDuYosOcMA==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/config": "^1.57.0", + "@n8n/constants": "^0.13.0", + "@n8n/decorators": "^0.24.0", + "@n8n/di": "^0.9.0", + "callsites": "3.1.0", + "n8n-workflow": "^1.111.0", + "picocolors": "1.0.1", + "reflect-metadata": "0.2.2", + "winston": "3.14.2", + "yargs-parser": "21.1.1" + } + }, + "node_modules/n8n-core/node_modules/@n8n/constants": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@n8n/constants/-/constants-0.13.0.tgz", + "integrity": "sha512-y6IL0/hho+1q3jEXUm/qdkajqzO+hIiJlAsbMXCaUwjvRsSyQg0YBZYCbhEFhAyWt1VuQAJn5Hl333TSCzVbEw==", + "license": "SEE LICENSE IN LICENSE.md" + }, + "node_modules/n8n-core/node_modules/@n8n/decorators": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@n8n/decorators/-/decorators-0.24.0.tgz", + "integrity": "sha512-niIBjYRq/dk2JXvn40ENvbOQrD7SAXy9tPJmymp8VWAv6DeTsR4qA8daL/ug3BPkpm594g/Q+Vu5XqfMMTCDvA==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/constants": "^0.13.0", + "@n8n/di": "^0.9.0", + "@n8n/permissions": "^0.37.0", + "lodash": "4.17.21", + "n8n-workflow": "^1.111.0" + } + }, + "node_modules/n8n-core/node_modules/@n8n/permissions": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@n8n/permissions/-/permissions-0.37.0.tgz", + "integrity": "sha512-niOSmXrtkAZQ4l0dpNHJl2PI2wQKQ2MsWUEitytVcqfxSufpBjyXF60guW/nJdpkU8L56LVjPfDrveUBDlKk3A==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "zod": "3.25.67" + } + }, "node_modules/n8n-core/node_modules/axios": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.0.tgz", @@ -26024,21 +26505,21 @@ } }, "node_modules/n8n-editor-ui": { - "version": "1.112.1", - "resolved": "https://registry.npmjs.org/n8n-editor-ui/-/n8n-editor-ui-1.112.1.tgz", - "integrity": "sha512-+6+PsA2ttwIFMhXjEpNVnidYmK36c6Rl7gxCUFDrgugwEJZIX6JyZ8TKqiU/fOPsjBsbWg/j6LxLqN13OP9rtg==", + "version": "1.113.1", + "resolved": "https://registry.npmjs.org/n8n-editor-ui/-/n8n-editor-ui-1.113.1.tgz", + "integrity": "sha512-K137PyDkJQH3FhUMrt1x7lY1+07O87wGafqEdK/P1yR3p0P5Eoddw53P9Mto0QkR05pYZUD3YTDzHoFfYRkK7w==", "license": "SEE LICENSE IN LICENSE.md" }, "node_modules/n8n-nodes-base": { - "version": "1.110.1", - "resolved": "https://registry.npmjs.org/n8n-nodes-base/-/n8n-nodes-base-1.110.1.tgz", - "integrity": "sha512-ElqG9a49I1ED7B4fm74tQAtgQW3eN7DB4eZ3J56u0RCK3glFRpQtkL/l+TZlswluDnAPb0zBMcuwOskFINksHw==", + "version": "1.112.0", + "resolved": "https://registry.npmjs.org/n8n-nodes-base/-/n8n-nodes-base-1.112.0.tgz", + "integrity": "sha512-Hh7G3LJsSI1QSOJYF8jSO9bnUB+laS6jTOL0qwhY8wbQxpwT+TVtDorOjlijfDuqQNfvPjmDM6/qL3soGZjnpQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@aws-sdk/client-sso-oidc": "3.808.0", "@kafkajs/confluent-schema-registry": "3.8.0", "@mozilla/readability": "0.6.0", - "@n8n/config": "1.55.0", + "@n8n/config": "1.57.0", "@n8n/di": "0.9.0", "@n8n/errors": "^0.5.0", "@n8n/imap": "0.15.0", @@ -26079,8 +26560,8 @@ "mongodb": "6.11.0", "mqtt": "5.7.2", "mssql": "10.0.2", - "mysql2": "3.11.0", - "n8n-workflow": "1.109.0", + "mysql2": "3.15.0", + "n8n-workflow": "1.111.0", "node-html-markdown": "1.2.0", "node-ssh": "13.2.0", "nodemailer": "6.9.9", @@ -26376,9 +26857,9 @@ } }, "node_modules/n8n-workflow": { - "version": "1.109.0", - "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-1.109.0.tgz", - "integrity": "sha512-r1RE305J8wHZUvTAYtokt1+3XxmnXk7rsocEypWv1iHoP13zSNyh7yxIhZR5GBPguamU8FNcGXA6BbtW2UtFzw==", + "version": "1.111.0", + "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-1.111.0.tgz", + "integrity": "sha512-pnbb5Tl2QIH+FxqilttZlqEGn2Kyk0CZBj4B8cNb7JDmd756fPR/SDFPq/0fntRUoqagNm1QENh36MRcGLCtOg==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@n8n/errors": "^0.5.0", @@ -26409,6 +26890,305 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/n8n/node_modules/@modelcontextprotocol/sdk": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.0.tgz", + "integrity": "sha512-m//7RlINx1F3sz3KqwY1WWzVgTcYX52HYk4bJ1hkBXV3zccAEth+jRvG8DBRrdaQuRsPAJOx2MH3zaHNCKL7Zg==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/n8n/node_modules/@n8n/client-oauth2": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@n8n/client-oauth2/-/client-oauth2-0.29.0.tgz", + "integrity": "sha512-odf1C6ET1HwL64sA2HRxSOm4LLODdppCTv52IyaE4CkoX0Ghr5lPOMXi1qrt8RiQe1lsQwrFuQQYgVr0IZnAkQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "axios": "1.8.3" + } + }, + "node_modules/n8n/node_modules/@n8n/client-oauth2/node_modules/axios": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", + "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/n8n/node_modules/@n8n/config": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@n8n/config/-/config-1.56.1.tgz", + "integrity": "sha512-3Y84MfY8v477e9GfJjghyYI+35vpxEJotIW2IpYzk2LM9asdrUMpFqWT0AAmIKUZnEsDmDCwWwxrzF1lHNN6xQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/di": "0.9.0", + "reflect-metadata": "0.2.2", + "zod": "3.25.67" + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain": { + "version": "1.112.2", + "resolved": "https://registry.npmjs.org/@n8n/n8n-nodes-langchain/-/n8n-nodes-langchain-1.112.2.tgz", + "integrity": "sha512-kmTaJEWyGBWp/C5Ew1HkAjATnCuNfcdwHQAT6IjcFpTqwHsjxrz3BO8Mj6GhyJMxL7xTV4riKEfuFWkud+Y+Lw==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.808.0", + "@azure/identity": "4.3.0", + "@getzep/zep-cloud": "1.0.12", + "@getzep/zep-js": "0.9.0", + "@google-cloud/resource-manager": "5.3.0", + "@google/genai": "1.19.0", + "@google/generative-ai": "0.21.0", + "@huggingface/inference": "4.0.5", + "@langchain/anthropic": "0.3.26", + "@langchain/aws": "0.1.11", + "@langchain/cohere": "0.3.4", + "@langchain/community": "0.3.50", + "@langchain/core": "0.3.68", + "@langchain/google-genai": "0.2.17", + "@langchain/google-vertexai": "0.2.18", + "@langchain/groq": "0.2.3", + "@langchain/mistralai": "0.2.1", + "@langchain/mongodb": "^0.1.0", + "@langchain/ollama": "0.2.3", + "@langchain/openai": "0.6.7", + "@langchain/pinecone": "0.2.0", + "@langchain/qdrant": "0.1.2", + "@langchain/redis": "0.1.1", + "@langchain/textsplitters": "0.1.0", + "@langchain/weaviate": "0.2.0", + "@modelcontextprotocol/sdk": "1.12.0", + "@mozilla/readability": "0.6.0", + "@n8n/client-oauth2": "0.29.0", + "@n8n/config": "1.56.1", + "@n8n/di": "0.9.0", + "@n8n/errors": "^0.5.0", + "@n8n/json-schema-to-zod": "1.5.0", + "@n8n/typeorm": "0.3.20-12", + "@n8n/typescript-config": "1.3.0", + "@n8n/vm2": "3.9.25", + "@pinecone-database/pinecone": "^5.0.2", + "@qdrant/js-client-rest": "1.14.1", + "@supabase/supabase-js": "2.49.9", + "@xata.io/client": "0.28.4", + "@zilliz/milvus2-sdk-node": "^2.5.7", + "basic-auth": "2.0.1", + "cheerio": "1.0.0", + "cohere-ai": "7.14.0", + "d3-dsv": "2.0.0", + "epub2": "3.0.2", + "form-data": "4.0.0", + "generate-schema": "2.6.0", + "html-to-text": "9.0.5", + "https-proxy-agent": "7.0.6", + "ignore": "^5.2.0", + "js-tiktoken": "^1.0.12", + "jsdom": "23.0.1", + "langchain": "0.3.33", + "lodash": "4.17.21", + "mammoth": "1.7.2", + "mime-types": "2.1.35", + "mongodb": "6.11.0", + "n8n-nodes-base": "1.111.2", + "n8n-workflow": "1.110.0", + "openai": "5.12.2", + "pdf-parse": "1.1.1", + "pg": "8.12.0", + "proxy-from-env": "^1.1.0", + "redis": "4.6.12", + "sanitize-html": "2.12.1", + "sqlite3": "5.1.7", + "temp": "0.9.4", + "tmp-promise": "3.0.3", + "undici": "^6.21.0", + "weaviate-client": "3.6.2", + "zod": "3.25.67", + "zod-to-json-schema": "3.23.3" + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/mongodb": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.11.0.tgz", + "integrity": "sha512-yVbPw0qT268YKhG241vAMLaDQAPbRyTgo++odSgGc9kXnzOujQI60Iyj23B9sQQFPSvmNPvMZ3dsFz0aN55KgA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/openai": { + "version": "5.12.2", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.12.2.tgz", + "integrity": "sha512-xqzHHQch5Tws5PcKR2xsZGX9xtch+JQFz5zb14dGqlshmmDAFBFEWmeIpf7wVqWV+w7Emj7jRgkNJakyKE0tYQ==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/n8n/node_modules/@n8n/n8n-nodes-langchain/node_modules/zod-to-json-schema": { + "version": "3.23.3", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.3.tgz", + "integrity": "sha512-TYWChTxKQbRJp5ST22o/Irt9KC5nj7CdBKYB/AosCRdj/wxEMvv4NNaj9XVUHDOIp53ZxArGhnw5HMZziPFjog==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.23.3" + } + }, "node_modules/n8n/node_modules/@n8n/typeorm": { "version": "0.3.20-12", "resolved": "https://registry.npmjs.org/@n8n/typeorm/-/typeorm-0.3.20-12.tgz", @@ -26550,6 +27330,121 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/n8n/node_modules/@qdrant/js-client-rest": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@qdrant/js-client-rest/-/js-client-rest-1.14.1.tgz", + "integrity": "sha512-CkCCTDc4gCXq+hhjB3yDw9Hs/PxCJ0bKqk/LjAAmuL9+nDm/RPue4C/tGOIMlzouTQ2l6J6t+JPeM//j38VFug==", + "license": "Apache-2.0", + "dependencies": { + "@qdrant/openapi-typescript-fetch": "1.2.6", + "@sevinf/maybe": "0.5.0", + "undici": "^6.0.0" + }, + "engines": { + "node": ">=18.17.0", + "pnpm": ">=8" + }, + "peerDependencies": { + "typescript": ">=4.7" + } + }, + "node_modules/n8n/node_modules/@redis/client": { + "version": "1.5.16", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.16.tgz", + "integrity": "sha512-X1a3xQ5kEMvTib5fBrHKh6Y+pXbeKXqziYuxOUo1ojQNECg4M5Etd1qqyhMap+lFUOAh8S7UYevgJHOm4A+NOg==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/n8n/node_modules/@supabase/auth-js": { + "version": "2.69.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.69.1.tgz", + "integrity": "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/n8n/node_modules/@supabase/functions-js": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", + "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/n8n/node_modules/@supabase/postgrest-js": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz", + "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/n8n/node_modules/@supabase/realtime-js": { + "version": "2.11.9", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.9.tgz", + "integrity": "sha512-fLseWq8tEPCO85x3TrV9Hqvk7H4SGOqnFQ223NPJSsxjSYn0EmzU1lvYO6wbA0fc8DE94beCAiiWvGvo4g33lQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "ws": "^8.18.2" + } + }, + "node_modules/n8n/node_modules/@supabase/realtime-js/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/n8n/node_modules/@supabase/storage-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", + "integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/n8n/node_modules/@supabase/supabase-js": { + "version": "2.49.9", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.9.tgz", + "integrity": "sha512-lB2A2X8k1aWAqvlpO4uZOdfvSuZ2s0fCMwJ1Vq6tjWsi3F+au5lMbVVn92G0pG8gfmis33d64Plkm6eSDs6jRA==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.69.1", + "@supabase/functions-js": "2.4.4", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.4", + "@supabase/realtime-js": "2.11.9", + "@supabase/storage-js": "2.7.1" + } + }, "node_modules/n8n/node_modules/@types/whatwg-url": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", @@ -26597,6 +27492,134 @@ "node": ">=14.20.1" } }, + "node_modules/n8n/node_modules/cheerio-select": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.6.0.tgz", + "integrity": "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==", + "license": "BSD-2-Clause", + "dependencies": { + "css-select": "^4.3.0", + "css-what": "^6.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.3.1", + "domutils": "^2.8.0" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/n8n/node_modules/cheerio-select/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/n8n/node_modules/cheerio-select/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/n8n/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/n8n/node_modules/css-select/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/n8n/node_modules/css-select/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/n8n/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/n8n/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/n8n/node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/n8n/node_modules/dotenv": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", @@ -26606,6 +27629,18 @@ "node": ">=10" } }, + "node_modules/n8n/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/n8n/node_modules/express-rate-limit": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", @@ -26621,6 +27656,12 @@ "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, + "node_modules/n8n/node_modules/fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "license": "MIT" + }, "node_modules/n8n/node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", @@ -26663,6 +27704,25 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/n8n/node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, "node_modules/n8n/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -26766,6 +27826,389 @@ "whatwg-url": "^11.0.0" } }, + "node_modules/n8n/node_modules/n8n-core": { + "version": "1.112.1", + "resolved": "https://registry.npmjs.org/n8n-core/-/n8n-core-1.112.1.tgz", + "integrity": "sha512-6kPFzwysE+oUFSUDVkiA/m5VzuuEFXR4dnx4Snhr2T/4cd7eFRhWxSzpQPfGWKB+N23LdiLxZRr43MVggCm4mg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@aws-sdk/client-s3": "3.808.0", + "@langchain/core": "0.3.68", + "@n8n/backend-common": "^0.23.1", + "@n8n/client-oauth2": "0.29.0", + "@n8n/config": "1.56.1", + "@n8n/constants": "0.12.0", + "@n8n/decorators": "0.23.0", + "@n8n/di": "0.9.0", + "@sentry/node": "^9.42.1", + "@sentry/node-native": "^9.42.1", + "axios": "1.12.0", + "callsites": "3.1.0", + "chardet": "2.0.0", + "cron": "3.1.7", + "fast-glob": "3.2.12", + "file-type": "16.5.4", + "form-data": "4.0.0", + "htmlparser2": "^10.0.0", + "http-proxy-agent": "7.0.2", + "https-proxy-agent": "7.0.6", + "iconv-lite": "0.6.3", + "jsonwebtoken": "9.0.2", + "lodash": "4.17.21", + "luxon": "3.4.4", + "mime-types": "2.1.35", + "n8n-workflow": "1.110.0", + "nanoid": "3.3.8", + "oauth-1.0a": "2.2.6", + "p-cancelable": "2.1.1", + "picocolors": "1.0.1", + "pretty-bytes": "5.6.0", + "proxy-from-env": "^1.1.0", + "qs": "6.11.0", + "ssh2": "1.15.0", + "uuid": "10.0.0", + "winston": "3.14.2", + "xml2js": "0.6.2", + "zod": "3.25.67" + }, + "bin": { + "n8n-copy-static-files": "bin/copy-static-files", + "n8n-generate-metadata": "bin/generate-metadata", + "n8n-generate-translations": "bin/generate-translations" + } + }, + "node_modules/n8n/node_modules/n8n-core/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base": { + "version": "1.111.2", + "resolved": "https://registry.npmjs.org/n8n-nodes-base/-/n8n-nodes-base-1.111.2.tgz", + "integrity": "sha512-mNi3L0vq2sH46q6MbUClE+sGL4Pdj7+P1DddHuqahpf4QXxjJpfVflGfyYGI2tV9jl/l9MWUoc4mha6mqrZb0g==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.808.0", + "@kafkajs/confluent-schema-registry": "3.8.0", + "@mozilla/readability": "0.6.0", + "@n8n/config": "1.56.1", + "@n8n/di": "0.9.0", + "@n8n/errors": "^0.5.0", + "@n8n/imap": "0.15.0", + "@n8n/vm2": "3.9.25", + "alasql": "4.4.0", + "amqplib": "0.10.6", + "aws4": "1.11.0", + "basic-auth": "2.0.1", + "change-case": "4.1.2", + "cheerio": "1.0.0-rc.6", + "chokidar": "4.0.3", + "cron": "3.1.7", + "csv-parse": "5.5.0", + "currency-codes": "2.1.0", + "eventsource": "2.0.2", + "fast-glob": "3.2.12", + "fastest-levenshtein": "^1.0.16", + "fflate": "0.7.4", + "generate-schema": "2.6.0", + "get-system-fonts": "2.0.2", + "gm": "1.25.1", + "html-to-text": "9.0.5", + "iconv-lite": "0.6.3", + "ics": "2.40.0", + "isbot": "3.6.13", + "iso-639-1": "2.1.15", + "js-nacl": "1.4.0", + "jsdom": "23.0.1", + "jsonwebtoken": "9.0.2", + "kafkajs": "2.2.4", + "ldapts": "4.2.6", + "lodash": "4.17.21", + "lossless-json": "1.0.5", + "luxon": "3.4.4", + "mailparser": "3.6.7", + "minifaker": "1.34.1", + "moment-timezone": "0.5.48", + "mongodb": "6.11.0", + "mqtt": "5.7.2", + "mssql": "10.0.2", + "mysql2": "3.15.0", + "n8n-workflow": "1.110.0", + "node-html-markdown": "1.2.0", + "node-ssh": "13.2.0", + "nodemailer": "6.9.9", + "otpauth": "9.1.1", + "pdfjs-dist": "5.3.31", + "pg": "8.12.0", + "pg-promise": "11.9.1", + "promise-ftp": "1.3.5", + "pyodide": "0.28.0", + "redis": "4.6.14", + "rfc2047": "4.0.1", + "rhea": "3.0.4", + "rrule": "2.8.1", + "rss-parser": "3.13.0", + "sanitize-html": "2.12.1", + "semver": "7.5.4", + "showdown": "2.1.0", + "simple-git": "3.28.0", + "snowflake-sdk": "2.1.0", + "ssh2-sftp-client": "12.0.1", + "tmp-promise": "3.0.3", + "ts-ics": "1.2.2", + "uuid": "10.0.0", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz", + "xml2js": "0.6.2", + "xmlhttprequest-ssl": "3.1.0" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/cheerio": { + "version": "1.0.0-rc.6", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.6.tgz", + "integrity": "sha512-hjx1XE1M/D5pAtMgvWwE21QClmAEeGHOIDfycgmndisdNgI6PE1cGRQkMGBcsbUbmEQyWu5PJLUcAOjtQS8DWw==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^1.3.0", + "dom-serializer": "^1.3.1", + "domhandler": "^4.1.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/mongodb": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.11.0.tgz", + "integrity": "sha512-yVbPw0qT268YKhG241vAMLaDQAPbRyTgo++odSgGc9kXnzOujQI60Iyj23B9sQQFPSvmNPvMZ3dsFz0aN55KgA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/redis": { + "version": "4.6.14", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.14.tgz", + "integrity": "sha512-GrNg/e33HtsQwNXL7kJT+iNFPSwE1IPmd7wzV3j4f2z0EYxZfZE7FVTmUysgAtqQQtg5NXF5SNLR9OdO/UHOfw==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.16", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.6", + "@redis/search": "1.1.6", + "@redis/time-series": "1.0.5" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/n8n/node_modules/n8n-nodes-base/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/n8n/node_modules/n8n-workflow": { + "version": "1.110.0", + "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-1.110.0.tgz", + "integrity": "sha512-xAwh9qLh2JwYWcUufHXD0M0zIxlAQ5iHuzYQh6mxjnrQ8yW5iA+Fparn52vqj/CNf9CsqKBYyOV4V2O12L4A1A==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@n8n/errors": "^0.5.0", + "@n8n/tournament": "1.0.6", + "ast-types": "0.15.2", + "callsites": "3.1.0", + "esprima-next": "5.8.4", + "form-data": "4.0.0", + "jmespath": "0.16.0", + "js-base64": "3.7.2", + "jssha": "3.3.1", + "lodash": "4.17.21", + "luxon": "3.4.4", + "md5": "2.3.0", + "recast": "0.22.0", + "title-case": "3.0.3", + "transliteration": "2.3.5", + "xml2js": "0.6.2", + "zod": "3.25.67" + } + }, + "node_modules/n8n/node_modules/n8n-workflow/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/n8n/node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", @@ -26782,6 +28225,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/n8n/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT" + }, + "node_modules/n8n/node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, "node_modules/n8n/node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -26804,6 +28262,21 @@ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "license": "ISC" }, + "node_modules/n8n/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/n8n/node_modules/raw-body": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", @@ -26819,6 +28292,20 @@ "node": ">= 0.8" } }, + "node_modules/n8n/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/n8n/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -26875,6 +28362,28 @@ "node": ">=12" } }, + "node_modules/n8n/node_modules/winston": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.14.2.tgz", + "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.6.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/n8n/node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", diff --git a/package.json b/package.json index c514b2a..721a8b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-mcp", - "version": "2.14.2", + "version": "2.14.3", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "main": "dist/index.js", "bin": { @@ -37,6 +37,7 @@ "update:n8n": "node scripts/update-n8n-deps.js", "update:n8n:check": "node scripts/update-n8n-deps.js --dry-run", "fetch:templates": "node dist/scripts/fetch-templates.js", + "fetch:templates:update": "node dist/scripts/fetch-templates.js --update", "fetch:templates:robust": "node dist/scripts/fetch-templates-robust.js", "prebuild:fts5": "npx tsx scripts/prebuild-fts5.ts", "test:templates": "node dist/scripts/test-templates.js", @@ -128,14 +129,14 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.13.2", - "@n8n/n8n-nodes-langchain": "^1.111.1", + "@n8n/n8n-nodes-langchain": "^1.112.2", "@supabase/supabase-js": "^2.57.4", "dotenv": "^16.5.0", "express": "^5.1.0", "lru-cache": "^11.2.1", - "n8n": "^1.112.3", - "n8n-core": "^1.111.0", - "n8n-workflow": "^1.109.0", + "n8n": "^1.113.3", + "n8n-core": "^1.112.1", + "n8n-workflow": "^1.110.0", "openai": "^4.77.0", "sql.js": "^1.13.0", "uuid": "^10.0.0", diff --git a/src/scripts/sanitize-templates.ts b/src/scripts/sanitize-templates.ts index 988d724..6b87165 100644 --- a/src/scripts/sanitize-templates.ts +++ b/src/scripts/sanitize-templates.ts @@ -2,32 +2,50 @@ import { createDatabaseAdapter } from '../database/database-adapter'; import { logger } from '../utils/logger'; import { TemplateSanitizer } from '../utils/template-sanitizer'; +import { gunzipSync, gzipSync } from 'zlib'; async function sanitizeTemplates() { console.log('🧹 Sanitizing workflow templates in database...\n'); - + const db = await createDatabaseAdapter('./data/nodes.db'); const sanitizer = new TemplateSanitizer(); - + try { - // Get all templates - const templates = db.prepare('SELECT id, name, workflow_json FROM templates').all() as any[]; + // Get all templates - check both old and new format + const templates = db.prepare('SELECT id, name, workflow_json, workflow_json_compressed FROM templates').all() as any[]; console.log(`Found ${templates.length} templates to check\n`); - + let sanitizedCount = 0; const problematicTemplates: any[] = []; - + for (const template of templates) { - if (!template.workflow_json) { - continue; // Skip templates without workflow data + let originalWorkflow: any = null; + let useCompressed = false; + + // Try compressed format first (newer format) + if (template.workflow_json_compressed) { + try { + const buffer = Buffer.from(template.workflow_json_compressed, 'base64'); + const decompressed = gunzipSync(buffer).toString('utf-8'); + originalWorkflow = JSON.parse(decompressed); + useCompressed = true; + } catch (e) { + console.log(`⚠️ Failed to decompress template ${template.id}, trying uncompressed`); + } } - let originalWorkflow; - try { - originalWorkflow = JSON.parse(template.workflow_json); - } catch (e) { - console.log(`⚠️ Skipping template ${template.id}: Invalid JSON`); - continue; + // Fall back to uncompressed format (deprecated) + if (!originalWorkflow && template.workflow_json) { + try { + originalWorkflow = JSON.parse(template.workflow_json); + } catch (e) { + console.log(`⚠️ Skipping template ${template.id}: Invalid JSON in both formats`); + continue; + } + } + + if (!originalWorkflow) { + continue; // Skip templates without workflow data } const { sanitized: sanitizedWorkflow, wasModified } = sanitizer.sanitizeWorkflow(originalWorkflow); @@ -35,18 +53,24 @@ async function sanitizeTemplates() { if (wasModified) { // Get detected tokens for reporting const detectedTokens = sanitizer.detectTokens(originalWorkflow); - - // Update the template with sanitized version - const stmt = db.prepare('UPDATE templates SET workflow_json = ? WHERE id = ?'); - stmt.run(JSON.stringify(sanitizedWorkflow), template.id); - + + // Update the template with sanitized version in the same format + if (useCompressed) { + const compressed = gzipSync(JSON.stringify(sanitizedWorkflow)).toString('base64'); + const stmt = db.prepare('UPDATE templates SET workflow_json_compressed = ? WHERE id = ?'); + stmt.run(compressed, template.id); + } else { + const stmt = db.prepare('UPDATE templates SET workflow_json = ? WHERE id = ?'); + stmt.run(JSON.stringify(sanitizedWorkflow), template.id); + } + sanitizedCount++; problematicTemplates.push({ id: template.id, name: template.name, tokens: detectedTokens }); - + console.log(`✅ Sanitized template ${template.id}: ${template.name}`); detectedTokens.forEach(token => { console.log(` - Found: ${token.substring(0, 20)}...`); diff --git a/src/templates/batch-processor.ts b/src/templates/batch-processor.ts index 4042458..7b3e15b 100644 --- a/src/templates/batch-processor.ts +++ b/src/templates/batch-processor.ts @@ -258,85 +258,132 @@ export class BatchProcessor { } /** - * Monitor batch job with exponential backoff + * Monitor batch job with fixed 1-minute polling interval */ private async monitorBatchJob(batchId: string): Promise { - // Start with shorter wait times for better UX - const waitTimes = [30, 60, 120, 300, 600, 900, 1800]; // Progressive wait times in seconds - let waitIndex = 0; + const pollInterval = 60; // Check every 60 seconds (1 minute) let attempts = 0; - const maxAttempts = 100; // Safety limit + const maxAttempts = 120; // 120 minutes max (2 hours) const startTime = Date.now(); let lastStatus = ''; - + while (attempts < maxAttempts) { const batchJob = await this.client.batches.retrieve(batchId); - - // Only log if status changed + const elapsedMinutes = Math.floor((Date.now() - startTime) / 60000); + + // Log status on every check (not just on change) + const statusSymbol = batchJob.status === 'in_progress' ? '⚙️' : + batchJob.status === 'finalizing' ? '📦' : + batchJob.status === 'validating' ? '🔍' : + batchJob.status === 'completed' ? '✅' : + batchJob.status === 'failed' ? '❌' : '⏳'; + + console.log(` ${statusSymbol} Batch ${batchId.slice(-8)}: ${batchJob.status} (${elapsedMinutes} min, check ${attempts + 1})`); + if (batchJob.status !== lastStatus) { - const elapsedMinutes = Math.floor((Date.now() - startTime) / 60000); - const statusSymbol = batchJob.status === 'in_progress' ? '⚙️' : - batchJob.status === 'finalizing' ? '📦' : - batchJob.status === 'validating' ? '🔍' : '⏳'; - - console.log(` ${statusSymbol} Batch ${batchId.slice(-8)}: ${batchJob.status} (${elapsedMinutes} min)`); + logger.info(`Batch ${batchId} status changed: ${lastStatus} -> ${batchJob.status}`); lastStatus = batchJob.status; } - - logger.debug(`Batch ${batchId} status: ${batchJob.status} (attempt ${attempts + 1})`); - + if (batchJob.status === 'completed') { - const elapsedMinutes = Math.floor((Date.now() - startTime) / 60000); - console.log(` ✅ Batch ${batchId.slice(-8)} completed in ${elapsedMinutes} minutes`); + console.log(` ✅ Batch ${batchId.slice(-8)} completed successfully in ${elapsedMinutes} minutes`); logger.info(`Batch job ${batchId} completed successfully`); return batchJob; } - + if (['failed', 'expired', 'cancelled'].includes(batchJob.status)) { + logger.error(`Batch job ${batchId} failed with status: ${batchJob.status}`); throw new Error(`Batch job failed with status: ${batchJob.status}`); } - - // Wait before next check - const waitTime = waitTimes[Math.min(waitIndex, waitTimes.length - 1)]; - logger.debug(`Waiting ${waitTime} seconds before next check...`); - await this.sleep(waitTime * 1000); - - waitIndex = Math.min(waitIndex + 1, waitTimes.length - 1); + + // Wait before next check (always 1 minute) + logger.debug(`Waiting ${pollInterval} seconds before next check...`); + await this.sleep(pollInterval * 1000); + attempts++; } - - throw new Error(`Batch job monitoring timed out after ${maxAttempts} attempts`); + + throw new Error(`Batch job monitoring timed out after ${maxAttempts} minutes`); } /** * Retrieve and parse results */ private async retrieveResults(batchJob: any): Promise { - if (!batchJob.output_file_id) { - throw new Error('No output file available for batch job'); - } - - // Download result file - const fileResponse = await this.client.files.content(batchJob.output_file_id); - const fileContent = await fileResponse.text(); - - // Parse JSONL results const results: MetadataResult[] = []; - const lines = fileContent.trim().split('\n'); - - for (const line of lines) { - if (!line) continue; - + + // Check if we have an output file (successful results) + if (batchJob.output_file_id) { + const fileResponse = await this.client.files.content(batchJob.output_file_id); + const fileContent = await fileResponse.text(); + + const lines = fileContent.trim().split('\n'); + for (const line of lines) { + if (!line) continue; + try { + const result = JSON.parse(line); + const parsed = this.generator.parseResult(result); + results.push(parsed); + } catch (error) { + logger.error('Error parsing result line:', error); + } + } + logger.info(`Retrieved ${results.length} successful results from batch job`); + } + + // Check if we have an error file (failed results) + if (batchJob.error_file_id) { + logger.warn(`Batch job has error file: ${batchJob.error_file_id}`); + try { - const result = JSON.parse(line); - const parsed = this.generator.parseResult(result); - results.push(parsed); + const errorResponse = await this.client.files.content(batchJob.error_file_id); + const errorContent = await errorResponse.text(); + + // Save error file locally for debugging + const errorFilePath = path.join(this.outputDir, `batch_${batchJob.id}_error.jsonl`); + fs.writeFileSync(errorFilePath, errorContent); + logger.warn(`Error file saved to: ${errorFilePath}`); + + // Parse errors and create default metadata for failed templates + const errorLines = errorContent.trim().split('\n'); + logger.warn(`Found ${errorLines.length} failed requests in error file`); + + for (const line of errorLines) { + if (!line) continue; + try { + const errorResult = JSON.parse(line); + const templateId = parseInt(errorResult.custom_id?.replace('template-', '') || '0'); + + if (templateId > 0) { + const errorMessage = errorResult.response?.body?.error?.message || + errorResult.error?.message || + 'Unknown error'; + + logger.debug(`Template ${templateId} failed: ${errorMessage}`); + + // Use getDefaultMetadata() from generator (it's private but accessible via bracket notation) + const defaultMeta = (this.generator as any).getDefaultMetadata(); + results.push({ + templateId, + metadata: defaultMeta, + error: errorMessage + }); + } + } catch (parseError) { + logger.error('Error parsing error line:', parseError); + } + } } catch (error) { - logger.error('Error parsing result line:', error); + logger.error('Failed to process error file:', error); } } - - logger.info(`Retrieved ${results.length} results from batch job`); + + // If we have no results at all, something is very wrong + if (results.length === 0 && !batchJob.output_file_id && !batchJob.error_file_id) { + throw new Error('No output file or error file available for batch job'); + } + + logger.info(`Total results (successful + failed): ${results.length}`); return results; } diff --git a/src/templates/metadata-generator.ts b/src/templates/metadata-generator.ts index d4102db..2f933bd 100644 --- a/src/templates/metadata-generator.ts +++ b/src/templates/metadata-generator.ts @@ -34,7 +34,7 @@ export class MetadataGenerator { private client: OpenAI; private model: string; - constructor(apiKey: string, model: string = 'gpt-4o-mini') { + constructor(apiKey: string, model: string = 'gpt-5-mini-2025-08-07') { this.client = new OpenAI({ apiKey }); this.model = model; } @@ -131,8 +131,8 @@ export class MetadataGenerator { url: '/v1/chat/completions', body: { model: this.model, - temperature: 0.3, // Lower temperature for more consistent structured outputs - max_completion_tokens: 1000, + // temperature removed - batch API only supports default (1.0) for this model + max_completion_tokens: 3000, response_format: { type: 'json_schema', json_schema: this.getJsonSchema() @@ -288,8 +288,8 @@ export class MetadataGenerator { try { const completion = await this.client.chat.completions.create({ model: this.model, - temperature: 0.3, // Lower temperature for more consistent structured outputs - max_completion_tokens: 1000, + // temperature removed - not supported in batch API for this model + max_completion_tokens: 3000, response_format: { type: 'json_schema', json_schema: this.getJsonSchema() diff --git a/src/utils/template-sanitizer.ts b/src/utils/template-sanitizer.ts index 9c11d67..90d7639 100644 --- a/src/utils/template-sanitizer.ts +++ b/src/utils/template-sanitizer.ts @@ -19,11 +19,17 @@ export const defaultSanitizerConfig: SanitizerConfig = { tokenPatterns: [ /apify_api_[A-Za-z0-9]+/g, /sk-[A-Za-z0-9]+/g, // OpenAI tokens + /pat[A-Za-z0-9_]{40,}/g, // Airtable Personal Access Tokens + /ghp_[A-Za-z0-9]{36,}/g, // GitHub Personal Access Tokens + /gho_[A-Za-z0-9]{36,}/g, // GitHub OAuth tokens /Bearer\s+[A-Za-z0-9\-._~+\/]+=*/g // Generic bearer tokens ], replacements: new Map([ ['apify_api_', 'apify_api_YOUR_TOKEN_HERE'], ['sk-', 'sk-YOUR_OPENAI_KEY_HERE'], + ['pat', 'patYOUR_AIRTABLE_TOKEN_HERE'], + ['ghp_', 'ghp_YOUR_GITHUB_TOKEN_HERE'], + ['gho_', 'gho_YOUR_GITHUB_TOKEN_HERE'], ['Bearer ', 'Bearer YOUR_TOKEN_HERE'] ]) }; diff --git a/tests/unit/templates/batch-processor.test.ts b/tests/unit/templates/batch-processor.test.ts index f516b71..f80921c 100644 --- a/tests/unit/templates/batch-processor.test.ts +++ b/tests/unit/templates/batch-processor.test.ts @@ -71,7 +71,7 @@ describe('BatchProcessor', () => { options = { apiKey: 'test-api-key', - model: 'gpt-4o-mini', + model: 'gpt-5-mini-2025-08-07', batchSize: 3, outputDir: './test-temp' }; @@ -177,13 +177,38 @@ describe('BatchProcessor', () => { it('should handle batch submission errors gracefully', async () => { mockClient.files.create.mockRejectedValue(new Error('Upload failed')); - + const results = await processor.processTemplates([mockTemplates[0]]); - + // Should not throw, should return empty results expect(results.size).toBe(0); }); + it('should log submission errors to console and logger', async () => { + const consoleErrorSpy = vi.spyOn(console, 'error'); + const { logger } = await import('../../../src/utils/logger'); + const loggerErrorSpy = vi.spyOn(logger, 'error'); + + mockClient.files.create.mockRejectedValue(new Error('Network error')); + + await processor.processTemplates([mockTemplates[0]]); + + // Should log error to console (actual format from line 95: " ❌ Batch N failed:", error) + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringContaining('Batch'), + expect.objectContaining({ message: 'Network error' }) + ); + + // Should also log to logger (line 94) + expect(loggerErrorSpy).toHaveBeenCalledWith( + expect.stringMatching(/Error processing batch/), + expect.objectContaining({ message: 'Network error' }) + ); + + consoleErrorSpy.mockRestore(); + loggerErrorSpy.mockRestore(); + }); + // Skipping: Parallel batch processing creates unhandled promise rejections in tests // The error handling works in production but the parallel promise structure is // difficult to test cleanly without refactoring the implementation @@ -368,7 +393,7 @@ describe('BatchProcessor', () => { it('should download and parse results correctly', async () => { const batchJob = { output_file_id: 'output-123' }; const fileContent = '{"custom_id": "template-1"}\n{"custom_id": "template-2"}'; - + mockClient.files.content.mockResolvedValue({ text: () => Promise.resolve(fileContent) }); @@ -377,7 +402,7 @@ describe('BatchProcessor', () => { { templateId: 1, metadata: { categories: ['test'] } }, { templateId: 2, metadata: { categories: ['test2'] } } ]; - + mockGenerator.parseResult.mockReturnValueOnce(mockResults[0]) .mockReturnValueOnce(mockResults[1]); @@ -389,17 +414,17 @@ describe('BatchProcessor', () => { }); it('should throw error when no output file available', async () => { - const batchJob = { output_file_id: null }; + const batchJob = { output_file_id: null, error_file_id: null }; await expect( (processor as any).retrieveResults(batchJob) - ).rejects.toThrow('No output file available for batch job'); + ).rejects.toThrow('No output file or error file available for batch job'); }); it('should handle malformed result lines gracefully', async () => { const batchJob = { output_file_id: 'output-123' }; const fileContent = '{"valid": "json"}\ninvalid json line\n{"another": "valid"}'; - + mockClient.files.content.mockResolvedValue({ text: () => Promise.resolve(fileContent) }); @@ -422,6 +447,227 @@ describe('BatchProcessor', () => { (processor as any).retrieveResults(batchJob) ).rejects.toThrow('Download failed'); }); + + it('should process error file when present', async () => { + const batchJob = { + id: 'batch-123', + output_file_id: 'output-123', + error_file_id: 'error-456' + }; + + const outputContent = '{"custom_id": "template-1"}'; + const errorContent = '{"custom_id": "template-2", "error": {"message": "Rate limit exceeded"}}\n{"custom_id": "template-3", "response": {"body": {"error": {"message": "Invalid request"}}}}'; + + mockClient.files.content + .mockResolvedValueOnce({ text: () => Promise.resolve(outputContent) }) + .mockResolvedValueOnce({ text: () => Promise.resolve(errorContent) }); + + mockedFs.writeFileSync = vi.fn(); + + const successResult = { templateId: 1, metadata: { categories: ['success'] } }; + mockGenerator.parseResult.mockReturnValue(successResult); + + // Mock getDefaultMetadata + const defaultMetadata = { + categories: ['General'], + complexity: 'medium', + estimatedSetupMinutes: 15, + useCases: [], + requiredServices: [], + targetAudience: [] + }; + (processor as any).generator.getDefaultMetadata = vi.fn().mockReturnValue(defaultMetadata); + + const results = await (processor as any).retrieveResults(batchJob); + + // Should have 1 successful + 2 failed results + expect(results).toHaveLength(3); + expect(mockClient.files.content).toHaveBeenCalledWith('output-123'); + expect(mockClient.files.content).toHaveBeenCalledWith('error-456'); + expect(mockedFs.writeFileSync).toHaveBeenCalled(); + + // Check error file was saved + const savedPath = (mockedFs.writeFileSync as any).mock.calls[0][0]; + expect(savedPath).toContain('batch_batch-123_error.jsonl'); + }); + + it('should handle error file with empty lines', async () => { + const batchJob = { + id: 'batch-789', + error_file_id: 'error-789' + }; + + const errorContent = '\n{"custom_id": "template-1", "error": {"message": "Failed"}}\n\n{"custom_id": "template-2", "error": {"message": "Error"}}\n'; + + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve(errorContent) + }); + + mockedFs.writeFileSync = vi.fn(); + + const defaultMetadata = { + categories: ['General'], + complexity: 'medium', + estimatedSetupMinutes: 15, + useCases: [], + requiredServices: [], + targetAudience: [] + }; + (processor as any).generator.getDefaultMetadata = vi.fn().mockReturnValue(defaultMetadata); + + const results = await (processor as any).retrieveResults(batchJob); + + // Should skip empty lines and process only valid ones + expect(results).toHaveLength(2); + expect(results[0].templateId).toBe(1); + expect(results[0].error).toBe('Failed'); + expect(results[1].templateId).toBe(2); + expect(results[1].error).toBe('Error'); + }); + + it('should assign default metadata to failed templates', async () => { + const batchJob = { + error_file_id: 'error-456' + }; + + const errorContent = '{"custom_id": "template-42", "error": {"message": "Timeout"}}'; + + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve(errorContent) + }); + + mockedFs.writeFileSync = vi.fn(); + + const defaultMetadata = { + categories: ['General'], + complexity: 'medium', + estimatedSetupMinutes: 15, + useCases: ['General automation'], + requiredServices: [], + targetAudience: ['Developers'] + }; + (processor as any).generator.getDefaultMetadata = vi.fn().mockReturnValue(defaultMetadata); + + const results = await (processor as any).retrieveResults(batchJob); + + expect(results).toHaveLength(1); + expect(results[0].templateId).toBe(42); + expect(results[0].metadata).toEqual(defaultMetadata); + expect(results[0].error).toBe('Timeout'); + }); + + it('should handle malformed error lines gracefully', async () => { + const batchJob = { + error_file_id: 'error-999' + }; + + const errorContent = '{"custom_id": "template-1", "error": {"message": "Valid error"}}\ninvalid json\n{"invalid": "no custom_id"}\n{"custom_id": "template-2", "error": {"message": "Another valid"}}'; + + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve(errorContent) + }); + + mockedFs.writeFileSync = vi.fn(); + + const defaultMetadata = { categories: ['General'] }; + (processor as any).generator.getDefaultMetadata = vi.fn().mockReturnValue(defaultMetadata); + + const results = await (processor as any).retrieveResults(batchJob); + + // Should only process valid error lines with template IDs + expect(results).toHaveLength(2); + expect(results[0].templateId).toBe(1); + expect(results[1].templateId).toBe(2); + }); + + it('should extract error message from response body', async () => { + const batchJob = { + error_file_id: 'error-123' + }; + + const errorContent = '{"custom_id": "template-5", "response": {"body": {"error": {"message": "API error from response body"}}}}'; + + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve(errorContent) + }); + + mockedFs.writeFileSync = vi.fn(); + + const defaultMetadata = { categories: ['General'] }; + (processor as any).generator.getDefaultMetadata = vi.fn().mockReturnValue(defaultMetadata); + + const results = await (processor as any).retrieveResults(batchJob); + + expect(results).toHaveLength(1); + expect(results[0].error).toBe('API error from response body'); + }); + + it('should use unknown error when no error message found', async () => { + const batchJob = { + error_file_id: 'error-000' + }; + + const errorContent = '{"custom_id": "template-10"}'; + + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve(errorContent) + }); + + mockedFs.writeFileSync = vi.fn(); + + const defaultMetadata = { categories: ['General'] }; + (processor as any).generator.getDefaultMetadata = vi.fn().mockReturnValue(defaultMetadata); + + const results = await (processor as any).retrieveResults(batchJob); + + expect(results).toHaveLength(1); + expect(results[0].error).toBe('Unknown error'); + }); + + it('should handle error file download failure gracefully', async () => { + const batchJob = { + output_file_id: 'output-123', + error_file_id: 'error-failed' + }; + + const outputContent = '{"custom_id": "template-1"}'; + + mockClient.files.content + .mockResolvedValueOnce({ text: () => Promise.resolve(outputContent) }) + .mockRejectedValueOnce(new Error('Error file download failed')); + + const successResult = { templateId: 1, metadata: { categories: ['success'] } }; + mockGenerator.parseResult.mockReturnValue(successResult); + + const results = await (processor as any).retrieveResults(batchJob); + + // Should still return successful results even if error file fails + expect(results).toHaveLength(1); + expect(results[0].templateId).toBe(1); + }); + + it('should skip templates with invalid or zero ID in error file', async () => { + const batchJob = { + error_file_id: 'error-invalid' + }; + + const errorContent = '{"custom_id": "template-0", "error": {"message": "Zero ID"}}\n{"custom_id": "invalid-id", "error": {"message": "Invalid"}}\n{"custom_id": "template-5", "error": {"message": "Valid ID"}}'; + + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve(errorContent) + }); + + mockedFs.writeFileSync = vi.fn(); + + const defaultMetadata = { categories: ['General'] }; + (processor as any).generator.getDefaultMetadata = vi.fn().mockReturnValue(defaultMetadata); + + const results = await (processor as any).retrieveResults(batchJob); + + // Should only include template with valid ID > 0 + expect(results).toHaveLength(1); + expect(results[0].templateId).toBe(5); + }); }); describe('cleanup', () => { @@ -526,7 +772,7 @@ describe('BatchProcessor', () => { mockClient.files.create.mockRejectedValue(new Error('Upload failed')); const submitBatch = (processor as any).submitBatch.bind(processor); - + await expect( submitBatch(templates, 'error_test') ).rejects.toThrow('Upload failed'); @@ -544,7 +790,7 @@ describe('BatchProcessor', () => { // Mock successful processing mockClient.files.create.mockResolvedValue({ id: 'file-123' }); - const completedJob = { + const completedJob = { id: 'batch-123', status: 'completed', output_file_id: 'output-123' @@ -565,4 +811,391 @@ describe('BatchProcessor', () => { expect(mockClient.batches.create).toHaveBeenCalled(); }); }); + + describe('submitBatch', () => { + it('should clean up input file immediately after upload', async () => { + const templates = [{ templateId: 1, name: 'Test', nodes: ['node1'] }]; + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + const completedJob = { + id: 'batch-123', + status: 'completed', + output_file_id: 'output-123' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + + // Mock sleep to speed up test + (processor as any).sleep = vi.fn().mockResolvedValue(undefined); + + const promise = (processor as any).submitBatch(templates, 'test_batch'); + + // Wait a bit for synchronous cleanup + await new Promise(resolve => setTimeout(resolve, 10)); + + // Input file should be deleted immediately + expect(mockedFs.unlinkSync).toHaveBeenCalled(); + + await promise; + }); + + it('should clean up OpenAI files after batch completion', async () => { + const templates = [{ templateId: 1, name: 'Test', nodes: ['node1'] }]; + + mockClient.files.create.mockResolvedValue({ id: 'file-upload-123' }); + const completedJob = { + id: 'batch-123', + status: 'completed', + output_file_id: 'output-123' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + + // Mock sleep to speed up test + (processor as any).sleep = vi.fn().mockResolvedValue(undefined); + + await (processor as any).submitBatch(templates, 'cleanup_test'); + + // Wait for promise chain to complete + await new Promise(resolve => setTimeout(resolve, 50)); + + // Should have attempted to delete the input file + expect(mockClient.files.del).toHaveBeenCalledWith('file-upload-123'); + }); + + it('should handle cleanup errors gracefully', async () => { + const templates = [{ templateId: 1, name: 'Test', nodes: ['node1'] }]; + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + mockClient.files.del.mockRejectedValue(new Error('Delete failed')); + const completedJob = { + id: 'batch-123', + status: 'completed' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + + // Mock sleep to speed up test + (processor as any).sleep = vi.fn().mockResolvedValue(undefined); + + // Should not throw even if cleanup fails + await expect( + (processor as any).submitBatch(templates, 'error_cleanup') + ).resolves.toBeDefined(); + }); + + it('should handle local file cleanup errors silently', async () => { + const templates = [{ templateId: 1, name: 'Test', nodes: ['node1'] }]; + + mockedFs.unlinkSync = vi.fn().mockImplementation(() => { + throw new Error('Cannot delete file'); + }); + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + const completedJob = { + id: 'batch-123', + status: 'completed' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + + // Mock sleep to speed up test + (processor as any).sleep = vi.fn().mockResolvedValue(undefined); + + // Should not throw even if local cleanup fails + await expect( + (processor as any).submitBatch(templates, 'local_cleanup_error') + ).resolves.toBeDefined(); + }); + }); + + describe('progress callback', () => { + it('should call progress callback during batch submission', async () => { + const templates = [ + { templateId: 1, name: 'T1', nodes: ['node1'] }, + { templateId: 2, name: 'T2', nodes: ['node2'] }, + { templateId: 3, name: 'T3', nodes: ['node3'] }, + { templateId: 4, name: 'T4', nodes: ['node4'] } + ]; + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + const completedJob = { + id: 'batch-123', + status: 'completed', + output_file_id: 'output-123' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve('{"custom_id": "template-1"}') + }); + mockGenerator.parseResult.mockReturnValue({ + templateId: 1, + metadata: { categories: ['test'] } + }); + + const progressCallback = vi.fn(); + + await processor.processTemplates(templates, progressCallback); + + // Should be called during submission and retrieval + expect(progressCallback).toHaveBeenCalled(); + expect(progressCallback.mock.calls.some((call: any) => + call[0].includes('Submitting') + )).toBe(true); + }); + + it('should work without progress callback', async () => { + const templates = [{ templateId: 1, name: 'T1', nodes: ['node1'] }]; + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + const completedJob = { + id: 'batch-123', + status: 'completed', + output_file_id: 'output-123' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve('{"custom_id": "template-1"}') + }); + mockGenerator.parseResult.mockReturnValue({ + templateId: 1, + metadata: { categories: ['test'] } + }); + + // Should not throw without callback + await expect( + processor.processTemplates(templates) + ).resolves.toBeDefined(); + }); + + it('should call progress callback with correct parameters', async () => { + const templates = [ + { templateId: 1, name: 'T1', nodes: ['node1'] }, + { templateId: 2, name: 'T2', nodes: ['node2'] } + ]; + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + const completedJob = { + id: 'batch-123', + status: 'completed', + output_file_id: 'output-123' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve('{"custom_id": "template-1"}') + }); + mockGenerator.parseResult.mockReturnValue({ + templateId: 1, + metadata: { categories: ['test'] } + }); + + const progressCallback = vi.fn(); + + await processor.processTemplates(templates, progressCallback); + + // Check that callback was called with proper arguments + const submissionCall = progressCallback.mock.calls.find((call: any) => + call[0].includes('Submitting') + ); + expect(submissionCall).toBeDefined(); + if (submissionCall) { + expect(submissionCall[1]).toBeGreaterThanOrEqual(0); + expect(submissionCall[2]).toBe(2); + } + }); + }); + + describe('batch result merging', () => { + it('should merge results from multiple batches', async () => { + const templates = Array.from({ length: 6 }, (_, i) => ({ + templateId: i + 1, + name: `T${i + 1}`, + nodes: ['node'] + })); + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + + // Create different completed jobs for each batch + let batchCounter = 0; + mockClient.batches.create.mockImplementation(() => { + batchCounter++; + return Promise.resolve({ + id: `batch-${batchCounter}`, + status: 'completed', + output_file_id: `output-${batchCounter}` + }); + }); + + mockClient.batches.retrieve.mockImplementation((id: string) => { + return Promise.resolve({ + id, + status: 'completed', + output_file_id: `output-${id.split('-')[1]}` + }); + }); + + let fileCounter = 0; + mockClient.files.content.mockImplementation(() => { + fileCounter++; + return Promise.resolve({ + text: () => Promise.resolve(`{"custom_id": "template-${fileCounter}"}`) + }); + }); + + mockGenerator.parseResult.mockImplementation((result: any) => { + const id = parseInt(result.custom_id.split('-')[1]); + return { + templateId: id, + metadata: { categories: [`batch-${Math.ceil(id / 3)}`] } + }; + }); + + const results = await processor.processTemplates(templates); + + // Should have results from both batches (6 templates, batchSize=3) + expect(results.size).toBeGreaterThan(0); + expect(mockClient.batches.create).toHaveBeenCalledTimes(2); + }); + + it('should handle empty batch results', async () => { + const templates = [ + { templateId: 1, name: 'T1', nodes: ['node'] }, + { templateId: 2, name: 'T2', nodes: ['node'] } + ]; + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + const completedJob = { + id: 'batch-123', + status: 'completed', + output_file_id: 'output-123' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + + // Return empty content + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve('') + }); + + const results = await processor.processTemplates(templates); + + // Should handle empty results gracefully + expect(results.size).toBe(0); + }); + }); + + describe('sleep', () => { + it('should delay for specified milliseconds', async () => { + const start = Date.now(); + await (processor as any).sleep(100); + const elapsed = Date.now() - start; + + expect(elapsed).toBeGreaterThanOrEqual(95); + expect(elapsed).toBeLessThan(150); + }); + }); + + describe('processBatch (legacy method)', () => { + it('should process a single batch synchronously', async () => { + const templates = [ + { templateId: 1, name: 'Test1', nodes: ['node1'] }, + { templateId: 2, name: 'Test2', nodes: ['node2'] } + ]; + + mockClient.files.create.mockResolvedValue({ id: 'file-abc' }); + const completedJob = { + id: 'batch-xyz', + status: 'completed', + output_file_id: 'output-xyz' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + + const fileContent = '{"custom_id": "template-1"}\n{"custom_id": "template-2"}'; + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve(fileContent) + }); + + const mockResults = [ + { templateId: 1, metadata: { categories: ['test1'] } }, + { templateId: 2, metadata: { categories: ['test2'] } } + ]; + mockGenerator.parseResult.mockReturnValueOnce(mockResults[0]) + .mockReturnValueOnce(mockResults[1]); + + // Mock sleep to speed up test + (processor as any).sleep = vi.fn().mockResolvedValue(undefined); + + const results = await (processor as any).processBatch(templates, 'legacy_test'); + + expect(results).toHaveLength(2); + expect(results[0].templateId).toBe(1); + expect(results[1].templateId).toBe(2); + expect(mockClient.batches.create).toHaveBeenCalled(); + }); + + it('should clean up files after processing', async () => { + const templates = [{ templateId: 1, name: 'Test', nodes: ['node1'] }]; + + mockClient.files.create.mockResolvedValue({ id: 'file-clean' }); + const completedJob = { + id: 'batch-clean', + status: 'completed', + output_file_id: 'output-clean' + }; + mockClient.batches.create.mockResolvedValue(completedJob); + mockClient.batches.retrieve.mockResolvedValue(completedJob); + mockClient.files.content.mockResolvedValue({ + text: () => Promise.resolve('{"custom_id": "template-1"}') + }); + mockGenerator.parseResult.mockReturnValue({ + templateId: 1, + metadata: { categories: ['test'] } + }); + + // Mock sleep to speed up test + (processor as any).sleep = vi.fn().mockResolvedValue(undefined); + + await (processor as any).processBatch(templates, 'cleanup_test'); + + // Should clean up all files + expect(mockedFs.unlinkSync).toHaveBeenCalled(); + expect(mockClient.files.del).toHaveBeenCalledWith('file-clean'); + expect(mockClient.files.del).toHaveBeenCalledWith('output-clean'); + }); + + it('should clean up local file on error', async () => { + const templates = [{ templateId: 1, name: 'Test', nodes: ['node1'] }]; + + mockClient.files.create.mockRejectedValue(new Error('Upload failed')); + + await expect( + (processor as any).processBatch(templates, 'error_test') + ).rejects.toThrow('Upload failed'); + + // Should clean up local file even on error + expect(mockedFs.unlinkSync).toHaveBeenCalled(); + }); + + it('should handle batch job monitoring errors', async () => { + const templates = [{ templateId: 1, name: 'Test', nodes: ['node1'] }]; + + mockClient.files.create.mockResolvedValue({ id: 'file-123' }); + mockClient.batches.create.mockResolvedValue({ id: 'batch-123' }); + mockClient.batches.retrieve.mockResolvedValue({ + id: 'batch-123', + status: 'failed' + }); + + await expect( + (processor as any).processBatch(templates, 'failed_batch') + ).rejects.toThrow('Batch job failed with status: failed'); + + // Should still attempt cleanup + expect(mockedFs.unlinkSync).toHaveBeenCalled(); + }); + }); }); \ No newline at end of file diff --git a/tests/unit/templates/metadata-generator.test.ts b/tests/unit/templates/metadata-generator.test.ts index 2614865..27c9b0d 100644 --- a/tests/unit/templates/metadata-generator.test.ts +++ b/tests/unit/templates/metadata-generator.test.ts @@ -18,7 +18,7 @@ describe('MetadataGenerator', () => { let generator: MetadataGenerator; beforeEach(() => { - generator = new MetadataGenerator('test-api-key', 'gpt-4o-mini'); + generator = new MetadataGenerator('test-api-key', 'gpt-5-mini-2025-08-07'); }); describe('createBatchRequest', () => { @@ -35,7 +35,7 @@ describe('MetadataGenerator', () => { expect(request.custom_id).toBe('template-123'); expect(request.method).toBe('POST'); expect(request.url).toBe('/v1/chat/completions'); - expect(request.body.model).toBe('gpt-4o-mini'); + expect(request.body.model).toBe('gpt-5-mini-2025-08-07'); expect(request.body.response_format.type).toBe('json_schema'); expect(request.body.response_format.json_schema.strict).toBe(true); expect(request.body.messages).toHaveLength(2); @@ -217,7 +217,7 @@ describe('MetadataGenerator', () => { // but should not cause any injection in our code expect(userMessage).toContain(''); expect(userMessage).toContain('javascript:alert(1)'); - expect(request.body.model).toBe('gpt-4o-mini'); + expect(request.body.model).toBe('gpt-5-mini-2025-08-07'); }); it('should handle extremely long template names', () => {