diff --git a/context-connectors/.gitignore b/context-connectors/.gitignore new file mode 100644 index 0000000..f06235c --- /dev/null +++ b/context-connectors/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/context-connectors/README.md b/context-connectors/README.md new file mode 100644 index 0000000..abec283 --- /dev/null +++ b/context-connectors/README.md @@ -0,0 +1,503 @@ +# Context Connectors + +Index any data source and make it searchable with Augment's context engine. + +## Features + +- **Multiple Sources**: Index from GitHub, GitLab, BitBucket, websites, or local filesystem +- **Flexible Storage**: Store indexes locally, in S3, or other backends +- **Multiple Clients**: CLI search, interactive agent, MCP server (local & remote) +- **Incremental Updates**: Only re-index what changed +- **Smart Filtering**: Respects `.gitignore`, `.augmentignore`, and filters binary/generated files + +## Installation + +```bash +npm install @augmentcode/context-connectors +``` + +Install optional dependencies based on your use case: + +```bash +# For GitHub source +npm install @octokit/rest + +# For S3 storage +npm install @aws-sdk/client-s3 + +# For MCP server (Claude Desktop) +npm install @modelcontextprotocol/sdk +``` + +## Quick Start + +### 1. Index Your Codebase + +```bash +# Set required environment variables +export AUGMENT_API_TOKEN='your-token' +export AUGMENT_API_URL='https://your-tenant.api.augmentcode.com/' + +# Index a local directory +npx context-connectors index -s filesystem -p /path/to/project -k my-project + +# Index a GitHub repository +export GITHUB_TOKEN='your-github-token' +npx context-connectors index -s github --owner myorg --repo myrepo -k my-project + +# Index a BitBucket repository +export BITBUCKET_TOKEN='your-bitbucket-token' +npx context-connectors index -s bitbucket --workspace myworkspace --repo myrepo -k my-project +``` + +### 2. Search + +```bash +# Simple search +npx context-connectors search "authentication logic" -k my-project + +# With file reading capabilities +npx context-connectors search "API routes" -k my-project --with-source -p /path/to/project +``` + +### 3. Interactive Agent + +```bash +npx context-connectors agent -k my-project --with-source -p /path/to/project +``` + +## CLI Commands + +### `index` - Index a data source + +```bash +context-connectors index [options] +``` + +| Option | Description | Default | +|--------|-------------|---------| +| `-s, --source ` | Source type: `filesystem`, `github`, `gitlab`, `bitbucket`, `website` | Required | +| `-k, --key ` | Index key/name | Required | +| `-p, --path ` | Path for filesystem source | `.` | +| `--owner ` | GitHub repository owner | - | +| `--repo ` | GitHub/BitBucket repository name | - | +| `--ref ` | Git ref (branch/tag/commit) | `HEAD` | +| `--workspace ` | BitBucket workspace slug | - | +| `--bitbucket-url ` | BitBucket base URL (for Server/Data Center) | `https://api.bitbucket.org/2.0` | +| `--store ` | Store type: `filesystem`, `s3` | `filesystem` | +| `--store-path ` | Filesystem store path | `.context-connectors` | +| `--bucket ` | S3 bucket name | - | + +### `search` - Search indexed content + +```bash +context-connectors search [options] +``` + +| Option | Description | Default | +|--------|-------------|---------| +| `-k, --key ` | Index key/name | Required | +| `--max-chars ` | Max output characters | - | +| `--with-source` | Enable file operations | `false` | +| `-p, --path ` | Source path (with --with-source) | - | + +### `agent` - Interactive AI agent + +```bash +context-connectors agent [options] +``` + +| Option | Description | Default | +|--------|-------------|---------| +| `-k, --key ` | Index key/name | Required | +| `--model ` | OpenAI model | `gpt-4o` | +| `--max-steps ` | Max agent steps | `10` | +| `-v, --verbose` | Show tool calls | `false` | +| `-q, --query ` | Single query (non-interactive) | - | +| `--with-source` | Enable file operations | `false` | + +### `mcp` - Start MCP server + +```bash +context-connectors mcp [options] +``` + +| Option | Description | Default | +|--------|-------------|---------| +| `-k, --key ` | Index key/name | Required | +| `--with-source` | Enable file tools | `false` | + +### `mcp-serve` - Start MCP HTTP server + +Start an MCP server accessible over HTTP for remote clients. + +```bash +context-connectors mcp-serve [options] +``` + +| Option | Description | Default | +|--------|-------------|---------| +| `-k, --key ` | Index key/name | Required | +| `--port ` | Port to listen on | `3000` | +| `--host ` | Host to bind to | `localhost` | +| `--cors ` | CORS origins (comma-separated or `*`) | - | +| `--base-path ` | Base path for MCP endpoint | `/mcp` | +| `--api-key ` | API key for authentication | - | +| `--store ` | Store type: `filesystem`, `s3` | `filesystem` | +| `--store-path ` | Store base path | `.context-connectors` | +| `--search-only` | Disable file operations | `false` | + +Example: +```bash +# Start server on port 8080, allow any CORS origin +context-connectors mcp-serve -k my-project --port 8080 --cors "*" + +# With authentication +context-connectors mcp-serve -k my-project --api-key "secret-key" + +# Or use environment variable for the key +MCP_API_KEY="secret-key" context-connectors mcp-serve -k my-project +``` + +### About `--with-source` + +The `--with-source` flag enables the `listFiles` and `readFile` tools in addition to `search`. Without this flag, only the `search` tool is available. + +When using `--with-source`, you must also provide the source path with `-p, --path` so that files can be read from disk. + +## Programmatic Usage + +### Basic Indexing + +```typescript +import { Indexer } from "@augmentcode/context-connectors"; +import { FilesystemSource } from "@augmentcode/context-connectors/sources"; +import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + +const source = new FilesystemSource({ rootPath: "./my-project" }); +const store = new FilesystemStore({ basePath: ".context-connectors" }); +const indexer = new Indexer(); + +const result = await indexer.index(source, store, "my-project"); +console.log(`Indexed ${result.filesIndexed} files`); +``` + +### Search Client + +```typescript +import { SearchClient } from "@augmentcode/context-connectors"; +import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + +const store = new FilesystemStore({ basePath: ".context-connectors" }); +const client = new SearchClient({ store, key: "my-project" }); +await client.initialize(); // Required before calling search() + +const result = await client.search("authentication"); +console.log(result.results); +``` + +> **Important:** You must call `await client.initialize()` before calling `search()`. This loads the index state and prepares the client for queries. + +### MCP Server + +```typescript +import { runMCPServer } from "@augmentcode/context-connectors"; +import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + +const store = new FilesystemStore({ basePath: ".context-connectors" }); + +await runMCPServer({ + store, + key: "my-project", +}); +``` + +### MCP HTTP Server + +```typescript +import { runMCPHttpServer } from "@augmentcode/context-connectors"; +import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + +const store = new FilesystemStore({ basePath: ".context-connectors" }); + +const server = await runMCPHttpServer({ + store, + key: "my-project", + port: 3000, + host: "0.0.0.0", + cors: "*", + apiKey: process.env.MCP_API_KEY, +}); + +console.log(`MCP server running at ${server.getUrl()}`); + +// Graceful shutdown +process.on("SIGTERM", () => server.stop()); +``` + +## Claude Desktop Integration + +Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`): + +```json +{ + "mcpServers": { + "my-project": { + "command": "npx", + "args": ["context-connectors", "mcp", "-k", "my-project", "--with-source", "-p", "/path/to/project"], + "env": { + "AUGMENT_API_TOKEN": "your-token", + "AUGMENT_API_URL": "https://your-tenant.api.augmentcode.com/" + } + } + } +} +``` + +## Remote MCP Client Integration + +The `mcp-serve` command exposes your indexed data over HTTP using the MCP Streamable HTTP transport. Any MCP-compatible client can connect. + +### Connecting with MCP SDK + +```typescript +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; + +const transport = new StreamableHTTPClientTransport( + new URL("http://localhost:3000/mcp"), + { + requestInit: { + headers: { + "Authorization": "Bearer your-api-key" + } + } + } +); + +const client = new Client({ name: "my-client", version: "1.0.0" }); +await client.connect(transport); + +// List available tools +const tools = await client.listTools(); +console.log(tools); + +// Call search tool +const result = await client.callTool("search", { query: "authentication" }); +console.log(result); +``` + +### Testing with curl + +```bash +# List tools +curl -X POST http://localhost:3000/mcp \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer your-api-key" \ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' + +# Call search tool +curl -X POST http://localhost:3000/mcp \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer your-api-key" \ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search","arguments":{"query":"authentication"}}}' +``` + +## GitHub Actions + +Automate indexing on every push: + +```yaml +name: Index Repository + +on: + push: + branches: [main] + +jobs: + index: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Index repository + run: | + npx @augmentcode/context-connectors index \ + -s github \ + --owner ${{ github.repository_owner }} \ + --repo ${{ github.event.repository.name }} \ + --ref ${{ github.sha }} \ + -k ${{ github.ref_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + AUGMENT_API_TOKEN: ${{ secrets.AUGMENT_API_TOKEN }} + AUGMENT_API_URL: ${{ secrets.AUGMENT_API_URL }} +``` + +## GitHub Webhook Integration + +Automatically index repositories on push using GitHub webhooks. Supports Vercel/Next.js, Express, and custom frameworks. + +### Vercel / Next.js App Router + +```typescript +// app/api/webhook/route.ts +import { createVercelHandler } from "@augmentcode/context-connectors/integrations/vercel"; +import { S3Store } from "@augmentcode/context-connectors/stores"; + +const store = new S3Store({ bucket: process.env.INDEX_BUCKET! }); + +export const POST = createVercelHandler({ + store, + secret: process.env.GITHUB_WEBHOOK_SECRET!, + + // Only index main branch + shouldIndex: (event) => event.ref === "refs/heads/main", + + // Log results + onIndexed: (key, result) => { + console.log(`Indexed ${key}: ${result.filesIndexed} files`); + }, +}); +``` + +### Express + +```typescript +import express from "express"; +import { createExpressHandler } from "@augmentcode/context-connectors/integrations/express"; +import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + +const app = express(); +const store = new FilesystemStore({ basePath: "./indexes" }); + +// Must use raw body for signature verification +app.post( + "/webhook", + express.raw({ type: "application/json" }), + createExpressHandler({ + store, + secret: process.env.GITHUB_WEBHOOK_SECRET!, + }) +); + +app.listen(3000); +``` + +### Custom Framework + +```typescript +import { + createGitHubWebhookHandler, + verifyWebhookSignature +} from "@augmentcode/context-connectors/integrations"; +import { S3Store } from "@augmentcode/context-connectors/stores"; + +const store = new S3Store({ bucket: "my-indexes" }); +const handler = createGitHubWebhookHandler({ store, secret: "..." }); + +// In your request handler: +async function handleRequest(req: Request) { + const signature = req.headers.get("x-hub-signature-256")!; + const eventType = req.headers.get("x-github-event")!; + const body = await req.text(); + + if (!await verifyWebhookSignature(body, signature, secret)) { + return new Response("Unauthorized", { status: 401 }); + } + + const result = await handler(eventType, JSON.parse(body)); + return Response.json(result); +} +``` + +### GitHub App Setup + +1. Go to **Settings > Developer settings > GitHub Apps > New GitHub App** +2. Set webhook URL to your deployed handler +3. Generate and save the webhook secret +4. Set **Repository contents** permission to **Read** +5. Subscribe to **Push** events +6. Install the app on your repositories + +## Environment Variables + +| Variable | Description | Required For | +|----------|-------------|--------------| +| `AUGMENT_API_TOKEN` | Augment API token | All operations | +| `AUGMENT_API_URL` | Augment API URL | All operations | +| `GITHUB_TOKEN` | GitHub access token | GitHub source | +| `GITLAB_TOKEN` | GitLab access token | GitLab source | +| `BITBUCKET_TOKEN` | BitBucket access token | BitBucket source | +| `GITHUB_WEBHOOK_SECRET` | Webhook signature secret | Webhook integration | +| `OPENAI_API_KEY` | OpenAI API key | Agent | +| `AWS_ACCESS_KEY_ID` | AWS access key | S3 store | +| `AWS_SECRET_ACCESS_KEY` | AWS secret key | S3 store | + +## Architecture + +``` +Sources → Indexer → Stores → Clients +``` + +- **Sources**: Fetch files from data sources (GitHub, Filesystem, etc.) +- **Indexer**: Orchestrates indexing using Augment's context engine +- **Stores**: Persist index state (Filesystem, S3) +- **Clients**: Consume the index (CLI, Agent, MCP Server via stdio or HTTP) + +## Filtering + +Files are automatically filtered based on: + +1. `.augmentignore` - Custom ignore patterns (highest priority) +2. Built-in filters - Binary files, large files, generated code, secrets +3. `.gitignore` - Standard git ignore patterns + +Create a `.augmentignore` file to customize: + +``` +# Ignore test fixtures +tests/fixtures/ + +# Ignore generated docs +docs/api/ + +# Ignore specific files +config.local.json +``` + +> **Note:** The `.augmentignore` file must be placed in the **source root directory** (the path passed to the index command), not the current working directory. + +## Website Source + +The website source crawls and indexes static HTML content. + +### Limitations + +- **JavaScript-rendered content is not supported.** Only static HTML is crawled. Single-page applications (SPAs) or pages that require JavaScript to render content will not be fully indexed. +- Link-based crawling only - pages must be discoverable through links from the starting URL. + +## S3-Compatible Storage + +When using S3-compatible services like MinIO, DigitalOcean Spaces, or Backblaze B2: + +```bash +npx context-connectors index -s filesystem -p ./project -k my-project \ + --store s3 \ + --bucket my-bucket \ + --s3-endpoint http://localhost:9000 \ + --s3-force-path-style +``` + +| Option | Description | +|--------|-------------| +| `--s3-endpoint ` | Custom S3 endpoint URL | +| `--s3-force-path-style` | Use path-style URLs (required for MinIO and most S3-compatible services) | + +## License + +MIT + diff --git a/context-connectors/package-lock.json b/context-connectors/package-lock.json new file mode 100644 index 0000000..e608630 --- /dev/null +++ b/context-connectors/package-lock.json @@ -0,0 +1,11250 @@ +{ + "name": "@augmentcode/context-connectors", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@augmentcode/context-connectors", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@augmentcode/auggie-sdk": "^0.1.11", + "cheerio": "^1.1.2", + "commander": "^12.0.0", + "ignore": "^5.3.0", + "minimatch": "^9.0.0", + "tar": "^6.2.0" + }, + "bin": { + "context-connectors": "dist/bin/index.js" + }, + "devDependencies": { + "@ai-sdk/anthropic": "^2.0.0", + "@ai-sdk/google": "^2.0.0", + "@ai-sdk/openai": "^2.0.86", + "@aws-sdk/client-s3": "^3.948.0", + "@modelcontextprotocol/sdk": "^1.24.3", + "@octokit/rest": "^22.0.1", + "@types/node": "^20.10.0", + "@types/tar": "^6.1.10", + "tsx": "^4.7.0", + "typescript": "^5.3.3", + "vitest": "^1.1.0" + }, + "peerDependencies": { + "@ai-sdk/anthropic": ">=1.0.0", + "@ai-sdk/google": ">=1.0.0", + "@ai-sdk/openai": ">=1.0.0", + "@anthropic-ai/sdk": ">=0.30.0", + "@aws-sdk/client-s3": ">=3.0.0", + "@modelcontextprotocol/sdk": ">=1.0.0", + "@octokit/rest": ">=20.0.0", + "ai": ">=4.0.0", + "ioredis": ">=5.0.0", + "zod": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@ai-sdk/anthropic": { + "optional": true + }, + "@ai-sdk/google": { + "optional": true + }, + "@ai-sdk/openai": { + "optional": true + }, + "@anthropic-ai/sdk": { + "optional": true + }, + "@aws-sdk/client-s3": { + "optional": true + }, + "@modelcontextprotocol/sdk": { + "optional": true + }, + "@octokit/rest": { + "optional": true + }, + "ai": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@a2a-js/sdk": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@a2a-js/sdk/-/sdk-0.2.5.tgz", + "integrity": "sha512-VTDuRS5V0ATbJ/LkaQlisMnTAeYKXAK6scMguVBstf+KIBQ7HIuKhiXLv+G/hvejkV+THoXzoNifInAkU81P1g==", + "peer": true, + "dependencies": { + "@types/cors": "^2.8.17", + "@types/express": "^4.17.23", + "body-parser": "^2.2.0", + "cors": "^2.8.5", + "express": "^4.21.2", + "uuid": "^11.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@agentclientprotocol/sdk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.5.1.tgz", + "integrity": "sha512-9bq2TgjhLBSUSC5jE04MEe+Hqw8YePzKghhYZ9QcjOyonY3q2oJfX6GoSO83hURpEnsqEPIrex6VZN3+61fBJg==", + "license": "Apache-2.0", + "dependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/anthropic": { + "version": "2.0.56", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.56.tgz", + "integrity": "sha512-XHJKu0Yvfu9SPzRfsAFESa+9T7f2YJY6TxykKMfRsAwpeWAiX/Gbx5J5uM15AzYC3Rw8tVP3oH+j7jEivENirQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.19" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/anthropic-v5": { + "name": "@ai-sdk/anthropic", + "version": "2.0.33", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.33.tgz", + "integrity": "sha512-egqr9PHqqX2Am5mn/Xs1C3+1/wphVKiAjpsVpW85eLc2WpW7AgiAg52DCBr4By9bw3UVVuMeR4uEO1X0dKDUDA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/anthropic-v5/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/anthropic-v5/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.12.tgz", + "integrity": "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.19.tgz", + "integrity": "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/gateway": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.21.tgz", + "integrity": "sha512-BwV7DU/lAm3Xn6iyyvZdWgVxgLu3SNXzl5y57gMvkW4nGhAOV5269IrJzQwGt03bb107sa6H6uJwWxc77zXoGA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.19", + "@vercel/oidc": "3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.19.tgz", + "integrity": "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/google": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-2.0.46.tgz", + "integrity": "sha512-8PK6u4sGE/kXebd7ZkTp+0aya4kNqzoqpS5m7cHY2NfTK6fhPc6GNvE+MZIZIoHQTp5ed86wGBdeBPpFaaUtyg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.19" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/google-v5": { + "name": "@ai-sdk/google", + "version": "2.0.40", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-2.0.40.tgz", + "integrity": "sha512-E7MTVE6vhWXQJzXQDvojwA9t5xlhWpxttCH3R/kUyiE6y0tT8Ay2dmZLO+bLpFBQ5qrvBMrjKWpDVQMoo6TJZg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.17" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/google-v5/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/google-v5/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.17.tgz", + "integrity": "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.19.tgz", + "integrity": "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/mistral-v5": { + "name": "@ai-sdk/mistral", + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/@ai-sdk/mistral/-/mistral-2.0.23.tgz", + "integrity": "sha512-np2bTlL5ZDi7iAOPCF5SZ5xKqls059iOvsigbgd9VNUCIrWSf6GYOaPvoWEgJ650TUOZitTfMo9MiEhLgutPfA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.16" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/mistral-v5/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/mistral-v5/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.16.tgz", + "integrity": "sha512-lsWQY9aDXHitw7C1QRYIbVGmgwyT98TF3MfM8alNIXKpdJdi+W782Rzd9f1RyOfgRmZ08gJ2EYNDhWNK7RqpEA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai": { + "version": "2.0.86", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-2.0.86.tgz", + "integrity": "sha512-obsLIOyA93lbQiSt1rvBItoVQp1U2RDPs0bNG0JYhm6Gku8Dg/0Cm8e4NUWT5p5PN10/doKSb3SMSKCixwIAKA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.19" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai-compatible": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-1.0.22.tgz", + "integrity": "sha512-Q+lwBIeMprc/iM+vg1yGjvzRrp74l316wDpqWdbmd4VXXlllblzGsUgBLTeKvcEapFTgqk0FRETvSb58Y6dsfA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai-compatible-v5": { + "name": "@ai-sdk/openai-compatible", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-1.0.22.tgz", + "integrity": "sha512-Q+lwBIeMprc/iM+vg1yGjvzRrp74l316wDpqWdbmd4VXXlllblzGsUgBLTeKvcEapFTgqk0FRETvSb58Y6dsfA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai-compatible-v5/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/openai-compatible-v5/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.12.tgz", + "integrity": "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.12.tgz", + "integrity": "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai-v5": { + "name": "@ai-sdk/openai", + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-2.0.53.tgz", + "integrity": "sha512-GIkR3+Fyif516ftXv+YPSPstnAHhcZxNoR2s8uSHhQ1yBT7I7aQYTVwpjAuYoT3GR+TeP50q7onj2/nDRbT2FQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai-v5/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/openai-v5/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.12.tgz", + "integrity": "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.19.tgz", + "integrity": "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", + "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz", + "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "nanoid": "^3.3.8", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@ai-sdk/provider-utils-v5": { + "name": "@ai-sdk/provider-utils", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.12.tgz", + "integrity": "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/provider-utils-v5/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-v5": { + "name": "@ai-sdk/provider", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/react": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", + "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/ui-utils": "1.2.11", + "swr": "^2.2.5", + "throttleit": "2.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/ui-utils": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", + "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@ai-sdk/xai-v5": { + "name": "@ai-sdk/xai", + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@ai-sdk/xai/-/xai-2.0.26.tgz", + "integrity": "sha512-+VtaLZSxmoKnNeJGM9bbtbZ3QMkPFlBB4N8prngbrSnvU/hG8cNdvvSBW/rIk6/DHrc2R8nFntNIBQoIRuBdQw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/openai-compatible": "1.0.22", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/xai-v5/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/xai-v5/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.12.tgz", + "integrity": "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.2.1.tgz", + "integrity": "sha512-HmdFw9CDYqM6B25pqGBpNeLCKvGPlIx1EbLrVL0zPvj50CJQUHyBNBw45Muk0kEIkogo1VZvOKHajdMuAzSxRg==", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + }, + "peerDependencies": { + "@types/json-schema": "^7.0.15" + } + }, + "node_modules/@augmentcode/auggie-sdk": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@augmentcode/auggie-sdk/-/auggie-sdk-0.1.11.tgz", + "integrity": "sha512-laEzvqI71JnUZQLs9HgPbZBcqxCwu3Qkj6PIT1AnVh5XqGMaJDKVUCSq5oFkg2DD1VYEFx/17LZy4GF5w7nRgQ==", + "dependencies": { + "@agentclientprotocol/sdk": "^0.5.1", + "@mastra/mcp": "^0.14.1", + "ai": "^5.0.86", + "async-mutex": "^0.5.0", + "uuid": "^11.1.0", + "zod": "^4.1.12" + }, + "peerDependencies": { + "typescript": "^5" + } + }, + "node_modules/@augmentcode/auggie-sdk/node_modules/zod": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.948.0.tgz", + "integrity": "sha512-uvEjds8aYA9SzhBS8RKDtsDUhNV9VhqKiHTcmvhM7gJO92q0WTn8/QeFTdNyLc6RxpiDyz+uBxS7PcdNiZzqfA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/credential-provider-node": "3.948.0", + "@aws-sdk/middleware-bucket-endpoint": "3.936.0", + "@aws-sdk/middleware-expect-continue": "3.936.0", + "@aws-sdk/middleware-flexible-checksums": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-location-constraint": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.948.0", + "@aws-sdk/middleware-sdk-s3": "3.947.0", + "@aws-sdk/middleware-ssec": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/signature-v4-multi-region": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/eventstream-serde-browser": "^4.2.5", + "@smithy/eventstream-serde-config-resolver": "^4.3.5", + "@smithy/eventstream-serde-node": "^4.2.5", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-blob-browser": "^4.2.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/hash-stream-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/md5-js": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.948.0.tgz", + "integrity": "sha512-iWjchXy8bIAVBUsKnbfKYXRwhLgRg3EqCQ5FTr3JbR+QR75rZm4ZOYXlvHGztVTmtAZ+PQVA1Y4zO7v7N87C0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.948.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.947.0.tgz", + "integrity": "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.947.0.tgz", + "integrity": "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.947.0.tgz", + "integrity": "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.948.0.tgz", + "integrity": "sha512-Cl//Qh88e8HBL7yYkJNpF5eq76IO6rq8GsatKcfVBm7RFVxCqYEPSSBtkHdbtNwQdRQqAMXc6E/lEB/CZUDxnA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/credential-provider-env": "3.947.0", + "@aws-sdk/credential-provider-http": "3.947.0", + "@aws-sdk/credential-provider-login": "3.948.0", + "@aws-sdk/credential-provider-process": "3.947.0", + "@aws-sdk/credential-provider-sso": "3.948.0", + "@aws-sdk/credential-provider-web-identity": "3.948.0", + "@aws-sdk/nested-clients": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.948.0.tgz", + "integrity": "sha512-gcKO2b6eeTuZGp3Vvgr/9OxajMrD3W+FZ2FCyJox363ZgMoYJsyNid1vuZrEuAGkx0jvveLXfwiVS0UXyPkgtw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.948.0.tgz", + "integrity": "sha512-ep5vRLnrRdcsP17Ef31sNN4g8Nqk/4JBydcUJuFRbGuyQtrZZrVT81UeH2xhz6d0BK6ejafDB9+ZpBjXuWT5/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.947.0", + "@aws-sdk/credential-provider-http": "3.947.0", + "@aws-sdk/credential-provider-ini": "3.948.0", + "@aws-sdk/credential-provider-process": "3.947.0", + "@aws-sdk/credential-provider-sso": "3.948.0", + "@aws-sdk/credential-provider-web-identity": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.947.0.tgz", + "integrity": "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.948.0.tgz", + "integrity": "sha512-gqLhX1L+zb/ZDnnYbILQqJ46j735StfWV5PbDjxRzBKS7GzsiYoaf6MyHseEopmWrez5zl5l6aWzig7UpzSeQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.948.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/token-providers": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.948.0.tgz", + "integrity": "sha512-MvYQlXVoJyfF3/SmnNzOVEtANRAiJIObEUYYyjTqKZTmcRIVVky0tPuG26XnB8LmTYgtESwJIZJj/Eyyc9WURQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.936.0.tgz", + "integrity": "sha512-XLSVVfAorUxZh6dzF+HTOp4R1B5EQcdpGcPliWr0KUj2jukgjZEcqbBmjyMF/p9bmyQsONX80iURF1HLAlW0qg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.936.0.tgz", + "integrity": "sha512-Eb4ELAC23bEQLJmUMYnPWcjD3FZIsmz2svDiXEcxRkQU9r7NRID7pM7C5NPH94wOfiCk0b2Y8rVyFXW0lGQwbA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.947.0.tgz", + "integrity": "sha512-kXXxS2raNESNO+zR0L4YInVjhcGGNI2Mx0AE1ThRhDkAt2se3a+rGf9equ9YvOqA1m8Jl/GSI8cXYvSxXmS9Ag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.936.0.tgz", + "integrity": "sha512-SCMPenDtQMd9o5da9JzkHz838w3327iqXk3cbNnXWqnNRx6unyW8FL0DZ84gIY12kAyVHz5WEqlWuekc15ehfw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.948.0.tgz", + "integrity": "sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.947.0.tgz", + "integrity": "sha512-DS2tm5YBKhPW2PthrRBDr6eufChbwXe0NjtTZcYDfUCXf0OR+W6cIqyKguwHMJ+IyYdey30AfVw9/Lb5KB8U8A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.936.0.tgz", + "integrity": "sha512-/GLC9lZdVp05ozRik5KsuODR/N7j+W+2TbfdFL3iS+7un+gnP6hC8RDOZd6WhpZp7drXQ9guKiTAxkZQwzS8DA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.947.0.tgz", + "integrity": "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.948.0.tgz", + "integrity": "sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.947.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.948.0", + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.947.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.947.0.tgz", + "integrity": "sha512-UaYmzoxf9q3mabIA2hc4T6x5YSFUG2BpNjAZ207EA1bnQMiK+d6vZvb83t7dIWL/U1de1sGV19c1C81Jf14rrA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.948.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.948.0.tgz", + "integrity": "sha512-V487/kM4Teq5dcr1t5K6eoUKuqlGr9FRWL3MIMukMERJXHZvio6kox60FZ/YtciRHRI75u14YUqm2Dzddcu3+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.947.0", + "@aws-sdk/nested-clients": "3.948.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", + "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", + "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.947.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.947.0.tgz", + "integrity": "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.947.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.930.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", + "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/ttlcache": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", + "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT", + "peer": true + }, + "node_modules/@mastra/core": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/@mastra/core/-/core-0.24.8.tgz", + "integrity": "sha512-7NW4duc26RfCXbi4ViT0/deG8kWZX4HAJKC6UNHUS5jSLgIof0iwS1yU5yA8NaPXe14I0WG1pEmnkEVYnXgGLw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@a2a-js/sdk": "~0.2.4", + "@ai-sdk/anthropic-v5": "npm:@ai-sdk/anthropic@2.0.33", + "@ai-sdk/google-v5": "npm:@ai-sdk/google@2.0.40", + "@ai-sdk/mistral-v5": "npm:@ai-sdk/mistral@2.0.23", + "@ai-sdk/openai-compatible-v5": "npm:@ai-sdk/openai-compatible@1.0.22", + "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.53", + "@ai-sdk/provider": "^1.1.3", + "@ai-sdk/provider-utils": "^2.2.8", + "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.12", + "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.0", + "@ai-sdk/ui-utils": "^1.2.11", + "@ai-sdk/xai-v5": "npm:@ai-sdk/xai@2.0.26", + "@isaacs/ttlcache": "^1.4.1", + "@mastra/schema-compat": "0.11.9", + "@openrouter/ai-sdk-provider-v5": "npm:@openrouter/ai-sdk-provider@1.2.3", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.62.1", + "@opentelemetry/core": "^2.0.1", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", + "@opentelemetry/otlp-exporter-base": "^0.203.0", + "@opentelemetry/otlp-transformer": "^0.203.0", + "@opentelemetry/resources": "^2.0.1", + "@opentelemetry/sdk-metrics": "^2.0.1", + "@opentelemetry/sdk-node": "^0.203.0", + "@opentelemetry/sdk-trace-base": "^2.0.1", + "@opentelemetry/sdk-trace-node": "^2.0.1", + "@opentelemetry/semantic-conventions": "^1.36.0", + "@sindresorhus/slugify": "^2.2.1", + "ai": "^4.3.19", + "ai-v5": "npm:ai@5.0.97", + "date-fns": "^3.6.0", + "dotenv": "^16.6.1", + "hono": "^4.9.7", + "hono-openapi": "^0.4.8", + "js-tiktoken": "^1.0.20", + "json-schema": "^0.4.0", + "lru-cache": "^11.2.2", + "p-map": "^7.0.3", + "p-retry": "^7.1.0", + "pino": "^9.7.0", + "pino-pretty": "^13.0.0", + "radash": "^12.1.1", + "sift": "^17.1.3", + "xstate": "^5.20.1", + "zod-to-json-schema": "^3.24.6" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/@mastra/core/node_modules/ai": { + "version": "4.3.19", + "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.19.tgz", + "integrity": "sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/react": "1.2.12", + "@ai-sdk/ui-utils": "1.2.11", + "@opentelemetry/api": "1.9.0", + "jsondiffpatch": "0.6.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/@mastra/mcp": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@mastra/mcp/-/mcp-0.14.5.tgz", + "integrity": "sha512-PNVLzD9XY2zs9fRnYOSGz1nf9hKlDU9dYo+EMJVy15wLdx9ZW72j0iuv4m3Aicdp5Y61l5LPSDPbDTSlO2Y3CA==", + "license": "Apache-2.0", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^14.2.1", + "@modelcontextprotocol/sdk": "^1.17.5", + "date-fns": "^4.1.0", + "exit-hook": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "uuid": "^11.1.0", + "zod-from-json-schema": "^0.5.0", + "zod-from-json-schema-v3": "npm:zod-from-json-schema@^0.0.5" + }, + "peerDependencies": { + "@mastra/core": ">=0.20.1-0 <0.25.0-0", + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/@mastra/mcp/node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/@mastra/schema-compat": { + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/@mastra/schema-compat/-/schema-compat-0.11.9.tgz", + "integrity": "sha512-LXEChx5n3bcuSFWQ5Wn9K2spLEpzHGf+DCnAeryuecpOo8VGLJ2QCK9Ugsnfjuc6hC0Ha73HvL1AD8zDhjmYOg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0", + "json-schema-to-zod": "^2.7.0", + "zod-from-json-schema": "^0.5.0", + "zod-from-json-schema-v3": "npm:zod-from-json-schema@^0.0.5", + "zod-to-json-schema": "^3.24.6" + }, + "peerDependencies": { + "ai": "^4.0.0 || ^5.0.0", + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.24.3.tgz", + "integrity": "sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==", + "license": "MIT", + "dependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/rest": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, + "node_modules/@openrouter/ai-sdk-provider-v5": { + "name": "@openrouter/ai-sdk-provider", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-1.2.3.tgz", + "integrity": "sha512-a6Nc8dPRHakRH9966YJ/HZJhLOds7DuPTscNZDoAr+Aw+tEFUlacSJMvb/b3gukn74mgbuaJRji9YOn62ipfVg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@openrouter/sdk": "^0.1.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "ai": "^5.0.0", + "zod": "^3.24.1 || ^v4" + } + }, + "node_modules/@openrouter/sdk": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/@openrouter/sdk/-/sdk-0.1.27.tgz", + "integrity": "sha512-RH//L10bSmc81q25zAZudiI4kNkLgxF2E+WU42vghp3N6TEvZ6F0jK7uT3tOxkEn91gzmMw9YVmDENy7SJsajQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", + "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node": { + "version": "0.62.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.62.2.tgz", + "integrity": "sha512-Ipe6X7ddrCiRsuewyTU83IvKiSFT4piqmv9z8Ovg1E7v98pdTj1pUE6sDrHV50zl7/ypd+cONBgt+EYSZu4u9Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/instrumentation-amqplib": "^0.50.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.54.1", + "@opentelemetry/instrumentation-aws-sdk": "^0.58.0", + "@opentelemetry/instrumentation-bunyan": "^0.49.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.49.0", + "@opentelemetry/instrumentation-connect": "^0.47.0", + "@opentelemetry/instrumentation-cucumber": "^0.19.0", + "@opentelemetry/instrumentation-dataloader": "^0.21.1", + "@opentelemetry/instrumentation-dns": "^0.47.0", + "@opentelemetry/instrumentation-express": "^0.52.0", + "@opentelemetry/instrumentation-fastify": "^0.48.0", + "@opentelemetry/instrumentation-fs": "^0.23.0", + "@opentelemetry/instrumentation-generic-pool": "^0.47.0", + "@opentelemetry/instrumentation-graphql": "^0.51.0", + "@opentelemetry/instrumentation-grpc": "^0.203.0", + "@opentelemetry/instrumentation-hapi": "^0.50.0", + "@opentelemetry/instrumentation-http": "^0.203.0", + "@opentelemetry/instrumentation-ioredis": "^0.51.0", + "@opentelemetry/instrumentation-kafkajs": "^0.13.0", + "@opentelemetry/instrumentation-knex": "^0.48.0", + "@opentelemetry/instrumentation-koa": "^0.51.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.48.0", + "@opentelemetry/instrumentation-memcached": "^0.47.0", + "@opentelemetry/instrumentation-mongodb": "^0.56.0", + "@opentelemetry/instrumentation-mongoose": "^0.50.0", + "@opentelemetry/instrumentation-mysql": "^0.49.0", + "@opentelemetry/instrumentation-mysql2": "^0.50.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.49.0", + "@opentelemetry/instrumentation-net": "^0.47.0", + "@opentelemetry/instrumentation-oracledb": "^0.29.0", + "@opentelemetry/instrumentation-pg": "^0.56.1", + "@opentelemetry/instrumentation-pino": "^0.50.1", + "@opentelemetry/instrumentation-redis": "^0.52.0", + "@opentelemetry/instrumentation-restify": "^0.49.0", + "@opentelemetry/instrumentation-router": "^0.48.0", + "@opentelemetry/instrumentation-runtime-node": "^0.17.1", + "@opentelemetry/instrumentation-socket.io": "^0.50.0", + "@opentelemetry/instrumentation-tedious": "^0.22.0", + "@opentelemetry/instrumentation-undici": "^0.14.0", + "@opentelemetry/instrumentation-winston": "^0.48.1", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.31.3", + "@opentelemetry/resource-detector-aws": "^2.3.0", + "@opentelemetry/resource-detector-azure": "^0.10.0", + "@opentelemetry/resource-detector-container": "^0.7.3", + "@opentelemetry/resource-detector-gcp": "^0.37.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/sdk-node": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^2.0.0" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", + "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", + "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.203.0.tgz", + "integrity": "sha512-g/2Y2noc/l96zmM+g0LdeuyYKINyBwN6FJySoU15LHPLcMN/1a0wNk2SegwKcxrRdE7Xsm7fkIR5n6XFe3QpPw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/sdk-logs": "0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.203.0.tgz", + "integrity": "sha512-s0hys1ljqlMTbXx2XiplmMJg9wG570Z5lH7wMvrZX6lcODI56sG4HL03jklF63tBeyNwK2RV1/ntXGo3HgG4Qw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/sdk-logs": "0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.203.0.tgz", + "integrity": "sha512-nl/7S91MXn5R1aIzoWtMKGvqxgJgepB/sH9qW0rZvZtabnsjbf8OQ1uSx3yogtvLr0GzwD596nQKz2fV7q2RBw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.203.0", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.203.0.tgz", + "integrity": "sha512-FCCj9nVZpumPQSEI57jRAA89hQQgONuoC35Lt+rayWY/mzCAc6BQT7RFyFaZKJ2B7IQ8kYjOCPsF/HGFWjdQkQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.203.0.tgz", + "integrity": "sha512-HFSW10y8lY6BTZecGNpV3GpoSy7eaO0Z6GATwZasnT4bEsILp8UJXNG5OmEsz4SdwCSYvyCbTJdNbZP3/8LGCQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.203.0.tgz", + "integrity": "sha512-OZnhyd9npU7QbyuHXFEPVm3LnjZYifuKpT3kTnF84mXeEQ84pJJZgyLBpU4FSkSwUkt/zbMyNAI7y5+jYTWGIg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.203.0.tgz", + "integrity": "sha512-2jLuNuw5m4sUj/SncDf/mFPabUxMZmmYetx5RKIMIQyPnl6G6ooFzfeE8aXNRf8YD1ZXNlCnRPcISxjveGJHNg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.203.0.tgz", + "integrity": "sha512-322coOTf81bm6cAA8+ML6A+m4r2xTCdmAZzGNTboPXRzhwPt4JEmovsFAs+grpdarObd68msOJ9FfH3jxM6wqA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.203.0.tgz", + "integrity": "sha512-ZDiaswNYo0yq/cy1bBLJFe691izEJ6IgNmkjm4C6kE9ub/OMQqDXORx2D2j8fzTBTxONyzusbaZlqtfmyqURPw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.203.0.tgz", + "integrity": "sha512-1xwNTJ86L0aJmWRwENCJlH4LULMG2sOXWIVw+Szta4fkqKVY50Eo4HoVKKq6U9QEytrWCr8+zjw0q/ZOeXpcAQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.0.1.tgz", + "integrity": "sha512-a9eeyHIipfdxzCfc2XPrE+/TI3wmrZUDFtG2RRXHSbZZULAny7SyybSvaDvS77a7iib5MPiAvluwVvbGTsHxsw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", + "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.50.0.tgz", + "integrity": "sha512-kwNs/itehHG/qaQBcVrLNcvXVPW0I4FCOVtw3LHMLdYIqD7GJ6Yv2nX+a4YHjzbzIeRYj8iyMp0Bl7tlkidq5w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda": { + "version": "0.54.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.54.1.tgz", + "integrity": "sha512-qm8pGSAM1mXk7unbrGktWWGJc6IFI58ZsaHJ+i420Fp5VO3Vf7GglIgaXTS8CKBrVB4LHFj3NvzJg31PtsAQcA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/aws-lambda": "8.10.152" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk": { + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.58.0.tgz", + "integrity": "sha512-9vFH7gU686dsAeLMCkqUj9y0MQZ1xrTtStSpNV2UaGWtDnRjJrAdJLu9Y545oKEaDTeVaob4UflyZvvpZnw3Xw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.34.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.49.0.tgz", + "integrity": "sha512-ky5Am1y6s3Ex/3RygHxB/ZXNG07zPfg9Z6Ora+vfeKcr/+I6CJbWXWhSBJor3gFgKN3RvC11UWVURnmDpBS6Pg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "^0.203.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@types/bunyan": "1.8.11" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.49.0.tgz", + "integrity": "sha512-BNIvqldmLkeikfI5w5Rlm9vG5NnQexfPoxOgEMzfDVOEF+vS6351I6DzWLLgWWR9CNF/jQJJi/lr6am2DLp0Rw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.47.0.tgz", + "integrity": "sha512-pjenvjR6+PMRb6/4X85L4OtkQCootgb/Jzh/l/Utu3SJHBid1F+gk9sTGU2FWuhhEfV6P7MZ7BmCdHXQjgJ42g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.19.0.tgz", + "integrity": "sha512-99ms8kQWRuPt5lkDqbJJzD+7Tq5TMUlBZki4SA2h6CgK4ncX+tyep9XFY1e+XTBLJIWmuFMGbWqBLJ4fSKIQNQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.21.1.tgz", + "integrity": "sha512-hNAm/bwGawLM8VDjKR0ZUDJ/D/qKR3s6lA5NV+btNaPVm2acqhPcT47l2uCVi+70lng2mywfQncor9v8/ykuyw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns": { + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.47.0.tgz", + "integrity": "sha512-775fOnewWkTF4iXMGKgwvOGqEmPrU1PZpXjjqvTrEErYBJe7Fz1WlEeUStHepyKOdld7Ghv7TOF/kE3QDctvrg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.52.0.tgz", + "integrity": "sha512-W7pizN0Wh1/cbNhhTf7C62NpyYw7VfCFTYg0DYieSTrtPBT1vmoSZei19wfKLnrMsz3sHayCg0HxCVL2c+cz5w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.48.0.tgz", + "integrity": "sha512-3zQlE/DoVfVH6/ycuTv7vtR/xib6WOa0aLFfslYcvE62z0htRu/ot8PV/zmMZfnzpTQj8S/4ULv36R6UIbpJIg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.23.0.tgz", + "integrity": "sha512-Puan+QopWHA/KNYvDfOZN6M/JtF6buXEyD934vrb8WhsX1/FuM7OtoMlQyIqAadnE8FqqDL4KDPiEfCQH6pQcQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.47.0.tgz", + "integrity": "sha512-UfHqf3zYK+CwDwEtTjaD12uUqGGTswZ7ofLBEdQ4sEJp9GHSSJMQ2hT3pgBxyKADzUdoxQAv/7NqvL42ZI+Qbw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.51.0.tgz", + "integrity": "sha512-LchkOu9X5DrXAnPI1+Z06h/EH/zC7D6sA86hhPrk3evLlsJTz0grPrkL/yUJM9Ty0CL/y2HSvmWQCjbJEz/ADg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.203.0.tgz", + "integrity": "sha512-Qmjx2iwccHYRLoE4RFS46CvQE9JG9Pfeae4EPaNZjvIuJxb/pZa2R9VWzRlTehqQWpAvto/dGhtkw8Tv+o0LTg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "0.203.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.50.0.tgz", + "integrity": "sha512-5xGusXOFQXKacrZmDbpHQzqYD1gIkrMWuwvlrEPkYOsjUqGUjl1HbxCsn5Y9bUXOCgP1Lj6A4PcKt1UiJ2MujA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.203.0.tgz", + "integrity": "sha512-y3uQAcCOAwnO6vEuNVocmpVzG3PER6/YZqbPbbffDdJ9te5NkHEkfSMNzlC3+v7KlE+WinPGc3N7MR30G1HY2g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/instrumentation": "0.203.0", + "@opentelemetry/semantic-conventions": "^1.29.0", + "forwarded-parse": "2.1.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.51.0.tgz", + "integrity": "sha512-9IUws0XWCb80NovS+17eONXsw1ZJbHwYYMXiwsfR9TSurkLV5UNbRSKb9URHO+K+pIJILy9wCxvyiOneMr91Ig==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/redis-common": "^0.38.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.13.0.tgz", + "integrity": "sha512-FPQyJsREOaGH64hcxlzTsIEQC4DYANgTwHjiB7z9lldmvua1LRMVn3/FfBlzXoqF179B0VGYviz6rn75E9wsDw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.48.0.tgz", + "integrity": "sha512-V5wuaBPv/lwGxuHjC6Na2JFRjtPgstw19jTFl1B1b6zvaX8zVDYUDaR5hL7glnQtUSCMktPttQsgK4dhXpddcA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.33.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.51.0.tgz", + "integrity": "sha512-XNLWeMTMG1/EkQBbgPYzCeBD0cwOrfnn8ao4hWgLv0fNCFQu1kCsJYygz2cvKuCs340RlnG4i321hX7R8gj3Rg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.48.0.tgz", + "integrity": "sha512-KUW29wfMlTPX1wFz+NNrmE7IzN7NWZDrmFWHM/VJcmFEuQGnnBuTIdsP55CnBDxKgQ/qqYFp4udQFNtjeFosPw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached": { + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.47.0.tgz", + "integrity": "sha512-vXDs/l4hlWy1IepPG1S6aYiIZn+tZDI24kAzwKKJmR2QEJRL84PojmALAEJGazIOLl/VdcCPZdMb0U2K0VzojA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/memcached": "^2.2.6" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.56.0.tgz", + "integrity": "sha512-YG5IXUUmxX3Md2buVMvxm9NWlKADrnavI36hbJsihqqvBGsWnIfguf0rUP5Srr0pfPqhQjUP+agLMsvu0GmUpA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.50.0.tgz", + "integrity": "sha512-Am8pk1Ct951r4qCiqkBcGmPIgGhoDiFcRtqPSLbJrUZqEPUsigjtMjoWDRLG1Ki1NHgOF7D0H7d+suWz1AAizw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.49.0.tgz", + "integrity": "sha512-QU9IUNqNsrlfE3dJkZnFHqLjlndiU39ll/YAAEvWE40sGOCi9AtOF6rmEGzJ1IswoZ3oyePV7q2MP8SrhJfVAA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/mysql": "2.15.27" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.50.0.tgz", + "integrity": "sha512-PoOMpmq73rOIE3nlTNLf3B1SyNYGsp7QXHYKmeTZZnJ2Ou7/fdURuOhWOI0e6QZ5gSem18IR1sJi6GOULBQJ9g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sql-common": "^0.41.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.49.0.tgz", + "integrity": "sha512-1R/JFwdmZIk3T/cPOCkVvFQeKYzbbUvDxVH3ShXamUwBlGkdEu5QJitlRMyVNZaHkKZKWgYrBarGQsqcboYgaw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net": { + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.47.0.tgz", + "integrity": "sha512-csoJ++Njpf7C09JH+0HNGenuNbDZBqO1rFhMRo6s0rAmJwNh9zY3M/urzptmKlqbKnf4eH0s+CKHy/+M8fbFsQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-oracledb": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.29.0.tgz", + "integrity": "sha512-2aHLiJdkyiUbooIUm7FaZf+O4jyqEl+RfFpgud1dxT87QeeYM216wi+xaMNzsb5yKtRBqbA3qeHBCyenYrOZwA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/oracledb": "6.5.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.56.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.56.1.tgz", + "integrity": "sha512-0/PiHDPVaLdcXNw6Gqb3JBdMxComMEwh444X8glwiynJKJHRTR49+l2cqJfoOVzB8Sl1XRl3Yaqw6aDi3s8e9w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/sql-common": "^0.41.0", + "@types/pg": "8.15.5", + "@types/pg-pool": "2.0.6" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pino": { + "version": "0.50.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.50.1.tgz", + "integrity": "sha512-pBbvuWiHA9iAumAuQ0SKYOXK7NRlbnVTf/qBV0nMdRnxBPrc/GZTbh0f7Y59gZfYsbCLhXLL1oRTEnS+PwS3CA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "^0.203.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.52.0.tgz", + "integrity": "sha512-R8Y7cCZlJ2Vl31S2i7bl5SqyC/aul54ski4wCFip/Tp9WGtLK1xVATi2rwy2wkc8ZCtjdEe9eEVR+QFG6gGZxg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/redis-common": "^0.38.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.49.0.tgz", + "integrity": "sha512-tsGZZhS4mVZH7omYxw5jpsrD3LhWizqWc0PYtAnzpFUvL5ZINHE+cm57bssTQ2AK/GtZMxu9LktwCvIIf3dSmw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.48.0.tgz", + "integrity": "sha512-Wixrc8CchuJojXpaS/dCQjFOMc+3OEil1H21G+WLYQb8PcKt5kzW9zDBT19nyjjQOx/D/uHPfgbrT+Dc7cfJ9w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-runtime-node": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.17.1.tgz", + "integrity": "sha512-c1FlAk+bB2uF9a8YneGmNPTl7c/xVaan4mmWvbkWcOmH/ipKqR1LaKUlz/BMzLrJLjho1EJlG2NrS2w2Arg+nw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.50.0.tgz", + "integrity": "sha512-6JN6lnKN9ZuZtZdMQIR+no1qHzQvXSZUsNe3sSWMgqmNRyEXuDUWBIyKKeG0oHRHtR4xE4QhJyD4D5kKRPWZFA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.22.0.tgz", + "integrity": "sha512-XrrNSUCyEjH1ax9t+Uo6lv0S2FCCykcF7hSxBMxKf7Xn0bPRxD3KyFUZy25aQXzbbbUHhtdxj3r2h88SfEM3aA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.14.0.tgz", + "integrity": "sha512-2HN+7ztxAReXuxzrtA3WboAKlfP5OsPA57KQn2AdYZbJ3zeRPcLXyW4uO/jpLE6PLm0QRtmeGCmfYpqRlwgSwg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.48.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.48.1.tgz", + "integrity": "sha512-XyOuVwdziirHHYlsw+BWrvdI/ymjwnexupKA787zQQ+D5upaE/tseZxjfQa7+t4+FdVLxHICaMTmkSD4yZHpzQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "^0.203.0", + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.203.0.tgz", + "integrity": "sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-transformer": "0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.203.0.tgz", + "integrity": "sha512-te0Ze1ueJF+N/UOFl5jElJW4U0pZXQ8QklgSfJ2linHN0JJsuaHG8IabEUi2iqxY8ZBDlSiz1Trfv5JcjWWWwQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.203.0.tgz", + "integrity": "sha512-Y8I6GgoCna0qDQ2W6GCRtaF24SnvqvA8OfeTi7fqigD23u8Jpb4R5KFv/pRvrlGagcCLICMIyh9wiejp4TXu/A==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.203.0", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.0.1.tgz", + "integrity": "sha512-Hc09CaQ8Tf5AGLmf449H726uRoBNGPBL4bjr7AnnUpzWMvhdn61F78z9qb6IqB737TffBsokGAK1XykFEZ1igw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.0.1.tgz", + "integrity": "sha512-7PMdPBmGVH2eQNb/AtSJizQNgeNTfh6jQFqys6lfhd6P4r+m/nTh3gKPPpaCXVdRQ+z93vfKk+4UGty390283w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.38.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz", + "integrity": "sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.19.0 || >=20.6.0" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.31.11", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.31.11.tgz", + "integrity": "sha512-R/asn6dAOWMfkLeEwqHCUz0cNbb9oiHVyd11iwlypeT/p9bR1lCX5juu5g/trOwxo62dbuFcDbBdKCJd3O2Edg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.8.0.tgz", + "integrity": "sha512-L8K5L3bsDKboX7sDofZyRonyK8dfS+CF7ho8YbZ6OrH+d5uyRBsrjuokPzcju1jP2ZzgtpYzhLwzi9zPXyRLlA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.10.0.tgz", + "integrity": "sha512-5cNAiyPBg53Uxe/CW7hsCq8HiKNAUGH+gi65TtgpzSR9bhJG4AEbuZhbJDFwe97tn2ifAD1JTkbc/OFuaaFWbA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.7.11.tgz", + "integrity": "sha512-XUxnGuANa/EdxagipWMXKYFC7KURwed9/V0+NtYjFmwWHzV9/J4IYVGTK8cWDpyUvAQf/vE4sMa3rnS025ivXQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.37.0.tgz", + "integrity": "sha512-LGpJBECIMsVKhiulb4nxUw++m1oF4EiDDPmFGW2aqYaAF0oUvJNv8Z/55CAzcZ7SxvlTgUwzewXDBsuCup7iqw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "gcp-metadata": "^6.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", + "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.203.0.tgz", + "integrity": "sha512-vM2+rPq0Vi3nYA5akQD2f3QwossDnTDLvKbea6u/A2NZ3XDkPxMfo/PNrDoXhDUD/0pPo2CdH5ce/thn9K0kLw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz", + "integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.203.0.tgz", + "integrity": "sha512-zRMvrZGhGVMvAbbjiNQW3eKzW/073dlrSiAKPVWmkoQzah9wfynpVPeL55f9fVIm0GaBxTLcPeukWGy0/Wj7KQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-logs-otlp-grpc": "0.203.0", + "@opentelemetry/exporter-logs-otlp-http": "0.203.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.203.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.203.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.203.0", + "@opentelemetry/exporter-prometheus": "0.203.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.203.0", + "@opentelemetry/exporter-trace-otlp-http": "0.203.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.203.0", + "@opentelemetry/exporter-zipkin": "2.0.1", + "@opentelemetry/instrumentation": "0.203.0", + "@opentelemetry/propagator-b3": "2.0.1", + "@opentelemetry/propagator-jaeger": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.203.0", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "@opentelemetry/sdk-trace-node": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.1.tgz", + "integrity": "sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.1.tgz", + "integrity": "sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/context-async-hooks": "2.0.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", + "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.2.0.tgz", + "integrity": "sha512-+OaRja3f0IqGG2kptVeYsrZQK9nKRSpfFrKtRBq4uh6nIB8bTBgaGvYQrQoRrQWQMA5dK5yLhDMDc0dvYvCOIQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/context-async-hooks": "2.2.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", + "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", + "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@opentelemetry/core": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT", + "peer": true + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.41", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", + "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@sindresorhus/slugify": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz", + "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sindresorhus/transliterate": "^1.0.0", + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/transliterate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz", + "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz", + "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz", + "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.18.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.7.tgz", + "integrity": "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.6", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz", + "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.5.tgz", + "integrity": "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.9.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.5.tgz", + "integrity": "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.5.tgz", + "integrity": "sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.5.tgz", + "integrity": "sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.5.tgz", + "integrity": "sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz", + "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.6.tgz", + "integrity": "sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz", + "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.5.tgz", + "integrity": "sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz", + "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.5.tgz", + "integrity": "sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz", + "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.14.tgz", + "integrity": "sha512-v0q4uTKgBM8dsqGjqsabZQyH85nFaTnFcgpWU1uydKFsdyyMzfvOkNum9G7VK+dOP01vUnoZxIeRiJ6uD0kjIg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.18.7", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-middleware": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.14.tgz", + "integrity": "sha512-Z2DG8Ej7FyWG1UA+7HceINtSLzswUgs2np3sZX0YBBxCt+CXG4QUxv88ZDS3+2/1ldW7LqtSY1UO/6VQ1pND8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz", + "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz", + "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz", + "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz", + "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz", + "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz", + "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz", + "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz", + "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz", + "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz", + "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz", + "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.9.10", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.10.tgz", + "integrity": "sha512-Jaoz4Jw1QYHc1EFww/E6gVtNjhoDU+gwRKqXP6C3LKYqqH2UQhP8tMP3+t/ePrhaze7fhLE8vS2q6vVxBANFTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.18.7", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz", + "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz", + "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.13.tgz", + "integrity": "sha512-hlVLdAGrVfyNei+pKIgqDTxfu/ZI2NSyqj4IDxKd5bIsIqwR/dSlkxlPaYxFiIaDVrBy0he8orsFy+Cz119XvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.16.tgz", + "integrity": "sha512-F1t22IUiJLHrxW9W1CQ6B9PN+skZ9cqSuzB18Eh06HrJPbjsyZ7ZHecAKw80DQtyGTRcVfeukKaCRYebFwclbg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.3", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz", + "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz", + "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz", + "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz", + "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.5.tgz", + "integrity": "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.152", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.152.tgz", + "integrity": "sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/diff-match-patch": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", + "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", + "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/mysql": { + "version": "2.15.27", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", + "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.19.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.26.tgz", + "integrity": "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/oracledb": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@types/oracledb/-/oracledb-6.5.2.tgz", + "integrity": "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pg": { + "version": "8.15.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", + "integrity": "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz", + "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "minipass": "^4.0.0" + } + }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vercel/oidc": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.5.tgz", + "integrity": "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@vitest/expect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", + "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", + "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", + "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", + "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ai": { + "version": "5.0.113", + "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.113.tgz", + "integrity": "sha512-26vivpSO/mzZj0k1Si2IpsFspp26ttQICHRySQiMrtWcRd5mnJMX2a8sG28vmZ38C+JUn1cWmfZrsLMxkSMw9g==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/gateway": "2.0.21", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.19", + "@opentelemetry/api": "1.9.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/ai-v5": { + "name": "ai", + "version": "5.0.97", + "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.97.tgz", + "integrity": "sha512-8zBx0b/owis4eJI2tAlV8a1Rv0BANmLxontcAelkLNwEHhgfgXeKpDkhNB6OgV+BJSwboIUDkgd9312DdJnCOQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/gateway": "2.0.12", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.17", + "@opentelemetry/api": "1.9.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/ai-v5/node_modules/@ai-sdk/gateway": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.12.tgz", + "integrity": "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.17", + "@vercel/oidc": "3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/ai-v5/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ai-v5/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.17.tgz", + "integrity": "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/ai/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ai/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.19.tgz", + "integrity": "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT", + "peer": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/cheerio": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", + "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.0.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.12.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "license": "MIT", + "peer": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT", + "peer": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT", + "peer": true + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT", + "peer": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "peer": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/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/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "peer": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/exit-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-4.0.0.tgz", + "integrity": "sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "peer": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "peer": true, + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "peer": true, + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fast-copy": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.1.tgz", + "integrity": "sha512-+uUOQlhsaswsizHFmEFAQhB3lSiQ+lisxl50N6ZP0wywlZeWsIESxSi9ftPEps8UGfiBzyYP7x27zA674WUvXw==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT", + "peer": true + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "peer": true, + "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/gaxios/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", + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "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", + "peer": true, + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "peer": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "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", + "peer": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT", + "peer": true + }, + "node_modules/hono": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.0.tgz", + "integrity": "sha512-Jg8uZzN2ul8/qlyid5FO8O624F3AK0wKtkgoeEON1qBum1rM1itYBxoMKu/1SPJC7F1+xlIZsJMmc4HHhJ1AWg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hono-openapi": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/hono-openapi/-/hono-openapi-0.4.8.tgz", + "integrity": "sha512-LYr5xdtD49M7hEAduV1PftOMzuT8ZNvkyWfh1DThkLsIr4RkvDb12UxgIiFbwrJB6FLtFXLoOZL9x4IeDk2+VA==", + "license": "MIT", + "peer": true, + "dependencies": { + "json-schema-walker": "^2.0.0" + }, + "peerDependencies": { + "@hono/arktype-validator": "^2.0.0", + "@hono/effect-validator": "^1.2.0", + "@hono/typebox-validator": "^0.2.0 || ^0.3.0", + "@hono/valibot-validator": "^0.5.1", + "@hono/zod-validator": "^0.4.1", + "@sinclair/typebox": "^0.34.9", + "@valibot/to-json-schema": "^1.0.0-beta.3", + "arktype": "^2.0.0", + "effect": "^3.11.3", + "hono": "^4.6.13", + "openapi-types": "^12.1.3", + "valibot": "^1.0.0-beta.9", + "zod": "^3.23.8", + "zod-openapi": "^4.0.0" + }, + "peerDependenciesMeta": { + "@hono/arktype-validator": { + "optional": true + }, + "@hono/effect-validator": { + "optional": true + }, + "@hono/typebox-validator": { + "optional": true + }, + "@hono/valibot-validator": { + "optional": true + }, + "@hono/zod-validator": { + "optional": true + }, + "@sinclair/typebox": { + "optional": true + }, + "@valibot/to-json-schema": { + "optional": true + }, + "arktype": { + "optional": true + }, + "effect": { + "optional": true + }, + "hono": { + "optional": true + }, + "valibot": { + "optional": true + }, + "zod": { + "optional": true + }, + "zod-openapi": { + "optional": true + } + } + }, + "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/htmlparser2/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/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz", + "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "peer": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-network-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", + "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tiktoken": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", + "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", + "license": "MIT", + "peer": true, + "dependencies": { + "base64-js": "^1.5.1" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-to-zod": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/json-schema-to-zod/-/json-schema-to-zod-2.7.0.tgz", + "integrity": "sha512-eW59l3NQ6sa3HcB+Ahf7pP6iGU7MY4we5JsPqXQ2ZcIPF8QxSg/lkY8lN0Js/AG0NjMbk+nZGUfHlceiHF+bwQ==", + "license": "ISC", + "peer": true, + "bin": { + "json-schema-to-zod": "dist/cjs/cli.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-walker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-schema-walker/-/json-schema-walker-2.0.0.tgz", + "integrity": "sha512-nXN2cMky0Iw7Af28w061hmxaPDaML5/bQD9nwm1lOoIKEGjHcRGxqWe4MfrkYThYAPjSUhmsp4bJNoLAyVn9Xw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^11.1.0", + "clone": "^2.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/json-schema-walker/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", + "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/jsondiffpatch": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", + "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/diff-match-patch": "^1.0.36", + "chalk": "^5.3.0", + "diff-match-patch": "^1.0.5" + }, + "bin": { + "jsondiffpatch": "bin/jsondiffpatch.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT", + "peer": true + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "license": "BlueOak-1.0.0", + "peer": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "peer": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT", + "peer": true + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "peer": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT", + "peer": true + }, + "node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz", + "integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-network-error": "^1.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/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/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT", + "peer": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT", + "peer": true + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT", + "peer": true + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "peer": true, + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "peer": true, + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz", + "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", + "license": "MIT", + "peer": true, + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^4.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^4.0.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^5.0.2" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "peer": true, + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty/node_modules/secure-json-parse": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT", + "peer": true + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "peer": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT", + "peer": true + }, + "node_modules/radash": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/radash/-/radash-12.1.1.tgz", + "integrity": "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/send": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz", + "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/send/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "peer": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-static/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/serve-static/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT", + "peer": true + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "peer": true, + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swr": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.7.tgz", + "integrity": "sha512-ZEquQ82QvalqTxhBVv/DlAg2mbmUjF4UgpPg9wwk4ufb9rQnZXh1iKyyKBqV6bQGu1Ie7L1QwSYO07qFIa1p+g==", + "license": "MIT", + "peer": true, + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "peer": true, + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/throttleit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", + "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT", + "peer": true + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", + "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", + "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", + "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.1", + "@vitest/runner": "1.6.1", + "@vitest/snapshot": "1.6.1", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.1", + "@vitest/ui": "1.6.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/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/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "peer": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xstate": { + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/xstate/-/xstate-5.25.0.tgz", + "integrity": "sha512-yyWzfhVRoTHNLjLoMmdwZGagAYfmnzpm9gPjlX2MhJZsDojXGqRxODDOi4BsgGRKD46NZRAdcLp6CKOyvQS0Bw==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/xstate" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-from-json-schema": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/zod-from-json-schema/-/zod-from-json-schema-0.5.2.tgz", + "integrity": "sha512-/dNaicfdhJTOuUd4RImbLUE2g5yrSzzDjI/S6C2vO2ecAGZzn9UcRVgtyLSnENSmAOBRiSpUdzDS6fDWX3Z35g==", + "license": "MIT", + "dependencies": { + "zod": "^4.0.17" + } + }, + "node_modules/zod-from-json-schema-v3": { + "name": "zod-from-json-schema", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/zod-from-json-schema/-/zod-from-json-schema-0.0.5.tgz", + "integrity": "sha512-zYEoo86M1qpA1Pq6329oSyHLS785z/mTwfr9V1Xf/ZLhuuBGaMlDGu/pDVGVUe4H4oa1EFgWZT53DP0U3oT9CQ==", + "license": "MIT", + "dependencies": { + "zod": "^3.24.2" + } + }, + "node_modules/zod-from-json-schema/node_modules/zod": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + } + } +} diff --git a/context-connectors/package.json b/context-connectors/package.json new file mode 100644 index 0000000..36079c6 --- /dev/null +++ b/context-connectors/package.json @@ -0,0 +1,124 @@ +{ + "name": "@augmentcode/context-connectors", + "version": "0.1.0", + "description": "Modular system for indexing any data source and making it searchable via Augment's context engine", + "type": "module", + "bin": { + "context-connectors": "./dist/bin/index.js" + }, + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "test": "vitest run", + "cli": "tsx src/bin/index.ts", + "cli:index": "tsx src/bin/index.ts index", + "cli:search": "tsx src/bin/index.ts search" + }, + "keywords": [ + "augment", + "context", + "indexing", + "sdk" + ], + "author": "Augment Code", + "license": "MIT", + "exports": { + ".": { + "types": "./dist/core/index.d.ts", + "import": "./dist/core/index.js" + }, + "./sources": { + "types": "./dist/sources/index.d.ts", + "import": "./dist/sources/index.js" + }, + "./stores": { + "types": "./dist/stores/index.d.ts", + "import": "./dist/stores/index.js" + }, + "./tools": { + "types": "./dist/tools/index.d.ts", + "import": "./dist/tools/index.js" + }, + "./clients": { + "types": "./dist/clients/index.d.ts", + "import": "./dist/clients/index.js" + }, + "./integrations": { + "types": "./dist/integrations/index.d.ts", + "import": "./dist/integrations/index.js" + }, + "./integrations/vercel": { + "types": "./dist/integrations/github-webhook-vercel.d.ts", + "import": "./dist/integrations/github-webhook-vercel.js" + }, + "./integrations/express": { + "types": "./dist/integrations/github-webhook-express.d.ts", + "import": "./dist/integrations/github-webhook-express.js" + } + }, + "dependencies": { + "@augmentcode/auggie-sdk": "^0.1.11", + "cheerio": "^1.1.2", + "commander": "^12.0.0", + "ignore": "^5.3.0", + "minimatch": "^9.0.0", + "tar": "^6.2.0" + }, + "devDependencies": { + "@ai-sdk/anthropic": "^2.0.0", + "@ai-sdk/google": "^2.0.0", + "@ai-sdk/openai": "^2.0.86", + "@aws-sdk/client-s3": "^3.948.0", + "@modelcontextprotocol/sdk": "^1.24.3", + "@octokit/rest": "^22.0.1", + "@types/node": "^20.10.0", + "@types/tar": "^6.1.10", + "tsx": "^4.7.0", + "typescript": "^5.3.3", + "vitest": "^1.1.0" + }, + "peerDependencies": { + "@ai-sdk/anthropic": ">=1.0.0", + "@ai-sdk/google": ">=1.0.0", + "@ai-sdk/openai": ">=1.0.0", + "@anthropic-ai/sdk": ">=0.30.0", + "@aws-sdk/client-s3": ">=3.0.0", + "@modelcontextprotocol/sdk": ">=1.0.0", + "@octokit/rest": ">=20.0.0", + "ai": ">=4.0.0", + "ioredis": ">=5.0.0", + "zod": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@ai-sdk/anthropic": { + "optional": true + }, + "@ai-sdk/google": { + "optional": true + }, + "@ai-sdk/openai": { + "optional": true + }, + "@anthropic-ai/sdk": { + "optional": true + }, + "@aws-sdk/client-s3": { + "optional": true + }, + "@modelcontextprotocol/sdk": { + "optional": true + }, + "@octokit/rest": { + "optional": true + }, + "ai": { + "optional": true + }, + "zod": { + "optional": true + }, + "ioredis": { + "optional": true + } + } +} diff --git a/context-connectors/src/bin/cmd-agent.ts b/context-connectors/src/bin/cmd-agent.ts new file mode 100644 index 0000000..85c5e07 --- /dev/null +++ b/context-connectors/src/bin/cmd-agent.ts @@ -0,0 +1,163 @@ +/** + * Agent command - Interactive AI agent for codebase Q&A + */ + +import { Command } from "commander"; +import * as readline from "readline"; +import { SearchClient } from "../clients/search-client.js"; +import { CLIAgent, type Provider } from "../clients/cli-agent.js"; +import { FilesystemStore } from "../stores/filesystem.js"; +import { FilesystemSource } from "../sources/filesystem.js"; + +const PROVIDER_DEFAULTS: Record = { + openai: "gpt-5-mini", + anthropic: "claude-haiku-4-5", + google: "gemini-3-flash-preview", +}; + +export const agentCommand = new Command("agent") + .description("Interactive AI agent for codebase Q&A") + .requiredOption("-k, --key ", "Index key/name") + .requiredOption( + "--provider ", + "LLM provider (openai, anthropic, google)" + ) + .option("--store ", "Store type (filesystem, s3)", "filesystem") + .option("--store-path ", "Store base path", ".context-connectors") + .option("--bucket ", "S3 bucket name (for s3 store)") + .option("--search-only", "Disable listFiles/readFile tools (search only)") + .option("-p, --path ", "Path override for filesystem source") + .option("--model ", "Model to use (defaults based on provider)") + .option("--max-steps ", "Maximum agent steps", (val) => parseInt(val, 10), 10) + .option("-v, --verbose", "Show tool calls") + .option("-q, --query ", "Single query (non-interactive)") + .action(async (options) => { + try { + // Validate provider + const provider = options.provider as Provider; + if (!["openai", "anthropic", "google"].includes(provider)) { + console.error( + `Unknown provider: ${provider}. Use: openai, anthropic, or google` + ); + process.exit(1); + } + + // Get model (use provider default if not specified) + const model = options.model ?? PROVIDER_DEFAULTS[provider]; + + // Create store + let store; + if (options.store === "filesystem") { + store = new FilesystemStore({ basePath: options.storePath }); + } else if (options.store === "s3") { + const { S3Store } = await import("../stores/s3.js"); + store = new S3Store({ bucket: options.bucket }); + } else { + console.error(`Unknown store type: ${options.store}`); + process.exit(1); + } + + // Load state for source type detection + const state = await store.load(options.key); + if (!state) { + console.error(`Index "${options.key}" not found`); + process.exit(1); + } + + // Create source unless --search-only is specified + let source; + if (!options.searchOnly) { + if (state.source.type === "filesystem") { + const path = options.path ?? state.source.identifier; + source = new FilesystemSource({ rootPath: path }); + } else if (state.source.type === "github") { + const [owner, repo] = state.source.identifier.split("/"); + const { GitHubSource } = await import("../sources/github.js"); + source = new GitHubSource({ owner, repo, ref: state.source.ref }); + } else if (state.source.type === "gitlab") { + const { GitLabSource } = await import("../sources/gitlab.js"); + source = new GitLabSource({ + projectId: state.source.identifier, + ref: state.source.ref, + }); + } else if (state.source.type === "website") { + const { WebsiteSource } = await import("../sources/website.js"); + source = new WebsiteSource({ + url: `https://${state.source.identifier}`, + }); + } + } + + // Create client + const client = new SearchClient({ store, source, key: options.key }); + await client.initialize(); + + const meta = client.getMetadata(); + console.log(`\x1b[36mConnected to: ${meta.type}://${meta.identifier}\x1b[0m`); + console.log(`\x1b[36mUsing: ${provider}/${model}\x1b[0m`); + console.log(`\x1b[36mLast synced: ${meta.syncedAt}\x1b[0m\n`); + + // Create and initialize agent + const agent = new CLIAgent({ + client, + provider, + model, + maxSteps: options.maxSteps, + verbose: options.verbose, + }); + await agent.initialize(); + + // Single query mode + if (options.query) { + await agent.ask(options.query); + return; + } + + // Interactive mode + console.log("Ask questions about your codebase. Type 'exit' to quit.\n"); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const prompt = () => { + rl.question("\x1b[32m> \x1b[0m", async (input) => { + const query = input.trim(); + + if (query.toLowerCase() === "exit" || query.toLowerCase() === "quit") { + rl.close(); + return; + } + + if (query.toLowerCase() === "reset") { + agent.reset(); + console.log("Conversation reset.\n"); + prompt(); + return; + } + + if (!query) { + prompt(); + return; + } + + try { + console.log(); + await agent.ask(query); + console.log(); + } catch (error) { + console.error("\x1b[31mError:\x1b[0m", error); + } + + prompt(); + }); + }; + + prompt(); + } catch (error) { + console.error("Agent failed:", error); + process.exit(1); + } + }); + diff --git a/context-connectors/src/bin/cmd-delete.ts b/context-connectors/src/bin/cmd-delete.ts new file mode 100644 index 0000000..febb7bb --- /dev/null +++ b/context-connectors/src/bin/cmd-delete.ts @@ -0,0 +1,56 @@ +/** + * Delete command - Delete an index from a store + */ + +import { Command } from "commander"; +import { FilesystemStore } from "../stores/filesystem.js"; + +export const deleteCommand = new Command("delete") + .description("Delete an index from a store") + .argument("", "Index key/name to delete") + .option("--store ", "Store type (filesystem, s3)", "filesystem") + .option("--store-path ", "Store base path", ".context-connectors") + .option("--bucket ", "S3 bucket name (for s3 store)") + .option("--s3-prefix ", "S3 key prefix", "context-connectors/") + .option("--s3-region ", "S3 region") + .option("--s3-endpoint ", "S3-compatible endpoint URL (for MinIO, R2, etc.)") + .option("--s3-force-path-style", "Use path-style S3 URLs (for some S3-compatible services)") + .action(async (key, options) => { + try { + // Create store + let store; + if (options.store === "filesystem") { + store = new FilesystemStore({ basePath: options.storePath }); + } else if (options.store === "s3") { + if (!options.bucket) { + console.error("S3 store requires --bucket option"); + process.exit(1); + } + const { S3Store } = await import("../stores/s3.js"); + store = new S3Store({ + bucket: options.bucket, + prefix: options.s3Prefix, + region: options.s3Region, + endpoint: options.s3Endpoint, + forcePathStyle: options.s3ForcePathStyle, + }); + } else { + console.error(`Unknown store type: ${options.store}`); + process.exit(1); + } + + // Check if index exists + const state = await store.load(key); + if (!state) { + console.error(`Index "${key}" not found.`); + process.exit(1); + } + + await store.delete(key); + console.log(`Index "${key}" deleted successfully.`); + } catch (error) { + console.error("Delete failed:", error); + process.exit(1); + } + }); + diff --git a/context-connectors/src/bin/cmd-index.ts b/context-connectors/src/bin/cmd-index.ts new file mode 100644 index 0000000..edf886f --- /dev/null +++ b/context-connectors/src/bin/cmd-index.ts @@ -0,0 +1,137 @@ +/** + * Index command - Index a data source + */ + +import { Command } from "commander"; +import { Indexer } from "../core/indexer.js"; +import { FilesystemSource } from "../sources/filesystem.js"; +import { FilesystemStore } from "../stores/filesystem.js"; + +export const indexCommand = new Command("index") + .description("Index a data source") + .requiredOption("-s, --source ", "Source type (filesystem, github, gitlab, bitbucket, website)") + .requiredOption("-k, --key ", "Index key/name") + .option("-p, --path ", "Path for filesystem source", ".") + .option("--owner ", "GitHub repository owner") + .option("--repo ", "GitHub/BitBucket repository name") + .option("--ref ", "GitHub/GitLab/BitBucket ref (branch/tag/commit)", "HEAD") + // GitLab options + .option("--gitlab-url ", "GitLab base URL (for self-hosted)", "https://gitlab.com") + .option("--project ", "GitLab project ID or path (e.g., group/project)") + // BitBucket options + .option("--workspace ", "BitBucket workspace slug") + .option("--bitbucket-url ", "BitBucket base URL (for Server/Data Center)", "https://api.bitbucket.org/2.0") + // Website options + .option("--url ", "Website URL to crawl") + .option("--max-depth ", "Maximum crawl depth (website)", (v) => parseInt(v, 10), 3) + .option("--max-pages ", "Maximum pages to crawl (website)", (v) => parseInt(v, 10), 100) + .option("--include ", "URL path patterns to include (website, glob)") + .option("--exclude ", "URL path patterns to exclude (website, glob)") + // Store options + .option("--store ", "Store type (filesystem, memory, s3)", "filesystem") + .option("--store-path ", "Store base path (for filesystem store)", ".context-connectors") + .option("--bucket ", "S3 bucket name (for s3 store)") + .option("--s3-prefix ", "S3 key prefix", "context-connectors/") + .option("--s3-region ", "S3 region") + .option("--s3-endpoint ", "S3-compatible endpoint URL (for MinIO, R2, etc.)") + .option("--s3-force-path-style", "Use path-style S3 URLs (for some S3-compatible services)") + .action(async (options) => { + try { + // Create source + let source; + if (options.source === "filesystem") { + source = new FilesystemSource({ rootPath: options.path }); + } else if (options.source === "github") { + if (!options.owner || !options.repo) { + console.error("GitHub source requires --owner and --repo options"); + process.exit(1); + } + const { GitHubSource } = await import("../sources/github.js"); + source = new GitHubSource({ + owner: options.owner, + repo: options.repo, + ref: options.ref, + }); + } else if (options.source === "gitlab") { + if (!options.project) { + console.error("GitLab source requires --project option"); + process.exit(1); + } + const { GitLabSource } = await import("../sources/gitlab.js"); + source = new GitLabSource({ + baseUrl: options.gitlabUrl, + projectId: options.project, + ref: options.ref, + }); + } else if (options.source === "bitbucket") { + if (!options.workspace || !options.repo) { + console.error("BitBucket source requires --workspace and --repo options"); + process.exit(1); + } + const { BitBucketSource } = await import("../sources/bitbucket.js"); + source = new BitBucketSource({ + baseUrl: options.bitbucketUrl, + workspace: options.workspace, + repo: options.repo, + ref: options.ref, + }); + } else if (options.source === "website") { + if (!options.url) { + console.error("Website source requires --url option"); + process.exit(1); + } + const { WebsiteSource } = await import("../sources/website.js"); + source = new WebsiteSource({ + url: options.url, + maxDepth: options.maxDepth, + maxPages: options.maxPages, + includePaths: options.include, + excludePaths: options.exclude, + }); + } else { + console.error(`Unknown source type: ${options.source}`); + process.exit(1); + } + + // Create store + let store; + if (options.store === "filesystem") { + store = new FilesystemStore({ basePath: options.storePath }); + } else if (options.store === "memory") { + const { MemoryStore } = await import("../stores/memory.js"); + store = new MemoryStore(); + console.warn("Warning: Using MemoryStore - data will be lost when process exits"); + } else if (options.store === "s3") { + if (!options.bucket) { + console.error("S3 store requires --bucket option"); + process.exit(1); + } + const { S3Store } = await import("../stores/s3.js"); + store = new S3Store({ + bucket: options.bucket, + prefix: options.s3Prefix, + region: options.s3Region, + endpoint: options.s3Endpoint, + forcePathStyle: options.s3ForcePathStyle, + }); + } else { + console.error(`Unknown store type: ${options.store}`); + process.exit(1); + } + + // Run indexer + console.log(`Indexing ${options.source} source...`); + const indexer = new Indexer(); + const result = await indexer.index(source, store, options.key); + + console.log(`\nIndexing complete!`); + console.log(` Type: ${result.type}`); + console.log(` Files indexed: ${result.filesIndexed}`); + console.log(` Files removed: ${result.filesRemoved}`); + console.log(` Duration: ${result.duration}ms`); + } catch (error) { + console.error("Indexing failed:", error); + process.exit(1); + } + }); + diff --git a/context-connectors/src/bin/cmd-init.ts b/context-connectors/src/bin/cmd-init.ts new file mode 100644 index 0000000..1456f70 --- /dev/null +++ b/context-connectors/src/bin/cmd-init.ts @@ -0,0 +1,206 @@ +/** + * CLI command: init + * Creates GitHub workflow for repository indexing + */ + +import { Command } from "commander"; +import { execSync } from "child_process"; +import { promises as fs } from "fs"; +import { join } from "path"; + +// Colors for console output +const colors = { + reset: "\x1b[0m", + bright: "\x1b[1m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + cyan: "\x1b[36m", +}; + +function colorize(color: keyof typeof colors, text: string): string { + return `${colors[color]}${text}${colors.reset}`; +} + +interface GitInfo { + owner: string; + repo: string; + defaultBranch: string; +} + +/** + * Try to detect git remote info from the current directory + */ +function detectGitInfo(): GitInfo | null { + try { + const remoteUrl = execSync("git remote get-url origin", { + encoding: "utf-8", + stdio: ["pipe", "pipe", "pipe"], + }).trim(); + + // Parse GitHub URL (https or ssh) + // https://github.com/owner/repo.git + // git@github.com:owner/repo.git + const httpsMatch = remoteUrl.match( + /github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/ + ); + const sshMatch = remoteUrl.match(/github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/); + const match = httpsMatch || sshMatch; + + if (!match) { + return null; + } + + // Try to get default branch + let defaultBranch = "main"; + try { + const branch = execSync("git symbolic-ref refs/remotes/origin/HEAD", { + encoding: "utf-8", + stdio: ["pipe", "pipe", "pipe"], + }).trim(); + defaultBranch = branch.replace("refs/remotes/origin/", ""); + } catch { + // Fall back to main + } + + return { + owner: match[1], + repo: match[2], + defaultBranch, + }; + } catch { + return null; + } +} + +function generateWorkflow( + owner: string, + repo: string, + branch: string, + indexKey: string +): string { + return `name: Index Repository + +on: + push: + branches: [${branch}] + workflow_dispatch: + +jobs: + index: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install context-connectors + run: npm install -g @augmentcode/context-connectors + + - name: Restore index cache + uses: actions/cache@v4 + with: + path: .context-connectors + key: index-\${{ github.repository }}-\${{ github.ref_name }} + restore-keys: | + index-\${{ github.repository }}- + + - name: Index repository + run: | + context-connectors index \\ + -s github \\ + --owner ${owner} \\ + --repo ${repo} \\ + --ref \${{ github.sha }} \\ + -k ${indexKey} + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + AUGMENT_API_TOKEN: \${{ secrets.AUGMENT_API_TOKEN }} + AUGMENT_API_URL: \${{ secrets.AUGMENT_API_URL }} +`; +} + +async function runInit(options: { + branch?: string; + key?: string; + force?: boolean; +}): Promise { + console.log(colorize("bright", "\n🚀 Augment Context Connectors - GitHub Setup\n")); + + // Detect git info + const gitInfo = detectGitInfo(); + if (!gitInfo) { + console.error( + "❌ Could not detect GitHub repository. Make sure you're in a git repo with a GitHub remote." + ); + process.exit(1); + } + + const { owner, repo, defaultBranch } = gitInfo; + const branch = options.branch || defaultBranch; + const indexKey = options.key || `${owner}/${repo}`; + + console.log(colorize("cyan", "Detected repository:")); + console.log(` Owner: ${owner}`); + console.log(` Repo: ${repo}`); + console.log(` Branch: ${branch}`); + console.log(` Index key: ${indexKey}\n`); + + // Create workflow directory + const workflowDir = join(process.cwd(), ".github", "workflows"); + const workflowPath = join(workflowDir, "augment-index.yml"); + + // Check if workflow already exists + try { + await fs.access(workflowPath); + if (!options.force) { + console.error( + `❌ Workflow already exists at ${workflowPath}\n Use --force to overwrite.` + ); + process.exit(1); + } + } catch { + // File doesn't exist, that's fine + } + + // Create directory and write workflow + await fs.mkdir(workflowDir, { recursive: true }); + const workflowContent = generateWorkflow(owner, repo, branch, indexKey); + await fs.writeFile(workflowPath, workflowContent); + + console.log(colorize("green", "✅ Created .github/workflows/augment-index.yml\n")); + + // Print next steps + console.log(colorize("bright", "📋 Next Steps:\n")); + + console.log(colorize("yellow", "1. Set up GitHub repository secrets:")); + console.log(" Go to your repository Settings > Secrets and variables > Actions"); + console.log(" Add the following secrets:"); + console.log(" • AUGMENT_API_TOKEN - Your Augment API token"); + console.log(" • AUGMENT_API_URL - Your tenant-specific Augment API URL\n"); + + console.log(colorize("yellow", "2. Commit and push:")); + console.log(" git add .github/workflows/augment-index.yml"); + console.log(' git commit -m "Add Augment indexing workflow"'); + console.log(" git push\n"); + + console.log(colorize("yellow", "3. Test locally (optional):")); + console.log(' export AUGMENT_API_TOKEN="your-token"'); + console.log(' export AUGMENT_API_URL="https://your-tenant.api.augmentcode.com/"'); + console.log(' export GITHUB_TOKEN="your-github-token"'); + console.log(` npx @augmentcode/context-connectors index -s github --owner ${owner} --repo ${repo} -k ${indexKey}\n`); + + console.log( + colorize("green", "The workflow will automatically run on pushes to the " + branch + " branch!") + ); +} + +export const initCommand = new Command("init") + .description("Initialize GitHub Actions workflow for repository indexing") + .option("-b, --branch ", "Branch to index (default: auto-detect)") + .option("-k, --key ", "Index key (default: owner/repo)") + .option("-f, --force", "Overwrite existing workflow file") + .action(runInit); + diff --git a/context-connectors/src/bin/cmd-list.ts b/context-connectors/src/bin/cmd-list.ts new file mode 100644 index 0000000..0d63497 --- /dev/null +++ b/context-connectors/src/bin/cmd-list.ts @@ -0,0 +1,58 @@ +/** + * List command - List all indexed keys in a store + */ + +import { Command } from "commander"; +import { FilesystemStore } from "../stores/filesystem.js"; + +export const listCommand = new Command("list") + .description("List all indexed keys in a store") + .option("--store ", "Store type (filesystem, s3)", "filesystem") + .option("--store-path ", "Store base path", ".context-connectors") + .option("--bucket ", "S3 bucket name (for s3 store)") + .option("--s3-prefix ", "S3 key prefix", "context-connectors/") + .option("--s3-region ", "S3 region") + .option("--s3-endpoint ", "S3-compatible endpoint URL (for MinIO, R2, etc.)") + .option("--s3-force-path-style", "Use path-style S3 URLs (for some S3-compatible services)") + .action(async (options) => { + try { + // Create store + let store; + if (options.store === "filesystem") { + store = new FilesystemStore({ basePath: options.storePath }); + } else if (options.store === "s3") { + if (!options.bucket) { + console.error("S3 store requires --bucket option"); + process.exit(1); + } + const { S3Store } = await import("../stores/s3.js"); + store = new S3Store({ + bucket: options.bucket, + prefix: options.s3Prefix, + region: options.s3Region, + endpoint: options.s3Endpoint, + forcePathStyle: options.s3ForcePathStyle, + }); + } else { + console.error(`Unknown store type: ${options.store}`); + process.exit(1); + } + + const keys = await store.list(); + + if (keys.length === 0) { + console.log("No indexes found."); + return; + } + + console.log("Available indexes:\n"); + for (const key of keys) { + console.log(` - ${key}`); + } + console.log(`\nTotal: ${keys.length} index(es)`); + } catch (error) { + console.error("List failed:", error); + process.exit(1); + } + }); + diff --git a/context-connectors/src/bin/cmd-mcp-serve.ts b/context-connectors/src/bin/cmd-mcp-serve.ts new file mode 100644 index 0000000..99d6c1c --- /dev/null +++ b/context-connectors/src/bin/cmd-mcp-serve.ts @@ -0,0 +1,116 @@ +/** + * MCP Serve command - Start MCP HTTP server for remote access + */ + +import { Command } from "commander"; +import { FilesystemStore } from "../stores/filesystem.js"; +import { FilesystemSource } from "../sources/filesystem.js"; +import { runMCPHttpServer } from "../clients/mcp-http-server.js"; + +export const mcpServeCommand = new Command("mcp-serve") + .description("Start MCP HTTP server for remote client access") + .requiredOption("-k, --key ", "Index key/name") + .option("--port ", "Port to listen on", "3000") + .option("--host ", "Host to bind to", "localhost") + .option("--cors ", "CORS origins (comma-separated, or '*' for any)") + .option("--base-path ", "Base path for MCP endpoint", "/mcp") + .option("--store ", "Store type (filesystem, s3)", "filesystem") + .option("--store-path ", "Store base path", ".context-connectors") + .option("--bucket ", "S3 bucket name (for s3 store)") + .option("--search-only", "Disable list_files/read_file tools (search only)") + .option("-p, --path ", "Path override for filesystem source") + .option( + "--api-key ", + "API key for authentication (or set MCP_API_KEY env var)" + ) + .action(async (options) => { + try { + // Create store + let store; + if (options.store === "filesystem") { + store = new FilesystemStore({ basePath: options.storePath }); + } else if (options.store === "s3") { + const { S3Store } = await import("../stores/s3.js"); + store = new S3Store({ bucket: options.bucket }); + } else { + console.error(`Unknown store type: ${options.store}`); + process.exit(1); + } + + // Load state to determine source type + const state = await store.load(options.key); + if (!state) { + console.error(`Index "${options.key}" not found`); + process.exit(1); + } + + // Create source unless --search-only is specified + let source; + if (!options.searchOnly) { + if (state.source.type === "filesystem") { + const path = options.path ?? state.source.identifier; + source = new FilesystemSource({ rootPath: path }); + } else if (state.source.type === "github") { + const [owner, repo] = state.source.identifier.split("/"); + const { GitHubSource } = await import("../sources/github.js"); + source = new GitHubSource({ owner, repo, ref: state.source.ref }); + } else if (state.source.type === "gitlab") { + const { GitLabSource } = await import("../sources/gitlab.js"); + source = new GitLabSource({ + projectId: state.source.identifier, + ref: state.source.ref, + }); + } else if (state.source.type === "website") { + const { WebsiteSource } = await import("../sources/website.js"); + source = new WebsiteSource({ + url: `https://${state.source.identifier}`, + }); + } + } + + // Parse CORS option + let cors: string | string[] | undefined; + if (options.cors) { + cors = + options.cors === "*" + ? "*" + : options.cors.split(",").map((s: string) => s.trim()); + } + + // Get API key from option or environment + const apiKey = options.apiKey ?? process.env.MCP_API_KEY; + + // Start HTTP server + const server = await runMCPHttpServer({ + store, + source, + key: options.key, + port: parseInt(options.port, 10), + host: options.host, + cors, + basePath: options.basePath, + apiKey, + }); + + console.log(`MCP HTTP server listening at ${server.getUrl()}`); + console.log(`Connect with MCP clients using Streamable HTTP transport`); + if (apiKey) { + console.log(`Authentication: API key required (Authorization: Bearer )`); + } else { + console.log(`Authentication: None (open access)`); + } + + // Handle shutdown + const shutdown = async () => { + console.log("\nShutting down..."); + await server.stop(); + process.exit(0); + }; + process.on("SIGINT", shutdown); + process.on("SIGTERM", shutdown); + } catch (error) { + console.error("Failed to start MCP HTTP server:", error); + process.exit(1); + } + }); + diff --git a/context-connectors/src/bin/cmd-mcp.ts b/context-connectors/src/bin/cmd-mcp.ts new file mode 100644 index 0000000..83ca2de --- /dev/null +++ b/context-connectors/src/bin/cmd-mcp.ts @@ -0,0 +1,75 @@ +/** + * MCP command - Start MCP server for Claude Desktop integration + */ + +import { Command } from "commander"; +import { FilesystemStore } from "../stores/filesystem.js"; +import { FilesystemSource } from "../sources/filesystem.js"; +import { runMCPServer } from "../clients/mcp-server.js"; + +export const mcpCommand = new Command("mcp") + .description("Start MCP server for Claude Desktop integration") + .requiredOption("-k, --key ", "Index key/name") + .option("--store ", "Store type (filesystem, s3)", "filesystem") + .option("--store-path ", "Store base path", ".context-connectors") + .option("--bucket ", "S3 bucket name (for s3 store)") + .option("--search-only", "Disable list_files/read_file tools (search only)") + .option("-p, --path ", "Path override for filesystem source") + .action(async (options) => { + try { + // Create store + let store; + if (options.store === "filesystem") { + store = new FilesystemStore({ basePath: options.storePath }); + } else if (options.store === "s3") { + const { S3Store } = await import("../stores/s3.js"); + store = new S3Store({ bucket: options.bucket }); + } else { + console.error(`Unknown store type: ${options.store}`); + process.exit(1); + } + + // Load state to determine source type + const state = await store.load(options.key); + if (!state) { + console.error(`Index "${options.key}" not found`); + process.exit(1); + } + + // Create source unless --search-only is specified + let source; + if (!options.searchOnly) { + if (state.source.type === "filesystem") { + const path = options.path ?? state.source.identifier; + source = new FilesystemSource({ rootPath: path }); + } else if (state.source.type === "github") { + const [owner, repo] = state.source.identifier.split("/"); + const { GitHubSource } = await import("../sources/github.js"); + source = new GitHubSource({ owner, repo, ref: state.source.ref }); + } else if (state.source.type === "gitlab") { + const { GitLabSource } = await import("../sources/gitlab.js"); + source = new GitLabSource({ + projectId: state.source.identifier, + ref: state.source.ref, + }); + } else if (state.source.type === "website") { + const { WebsiteSource } = await import("../sources/website.js"); + source = new WebsiteSource({ + url: `https://${state.source.identifier}`, + }); + } + } + + // Start MCP server (writes to stdout, reads from stdin) + await runMCPServer({ + store, + source, + key: options.key, + }); + } catch (error) { + // Write errors to stderr (stdout is for MCP protocol) + console.error("MCP server failed:", error); + process.exit(1); + } + }); + diff --git a/context-connectors/src/bin/cmd-search.ts b/context-connectors/src/bin/cmd-search.ts new file mode 100644 index 0000000..2265215 --- /dev/null +++ b/context-connectors/src/bin/cmd-search.ts @@ -0,0 +1,113 @@ +/** + * Search command - Search indexed content + */ + +import { Command } from "commander"; +import { SearchClient } from "../clients/search-client.js"; +import { FilesystemStore } from "../stores/filesystem.js"; +import { FilesystemSource } from "../sources/filesystem.js"; + +export const searchCommand = new Command("search") + .description("Search indexed content") + .argument("", "Search query") + .requiredOption("-k, --key ", "Index key/name") + .option("--store ", "Store type (filesystem, s3)", "filesystem") + .option("--store-path ", "Store base path", ".context-connectors") + .option("--bucket ", "S3 bucket name (for s3 store)") + .option("--s3-prefix ", "S3 key prefix", "context-connectors/") + .option("--s3-region ", "S3 region") + .option("--s3-endpoint ", "S3-compatible endpoint URL (for MinIO, R2, etc.)") + .option("--s3-force-path-style", "Use path-style S3 URLs (for some S3-compatible services)") + .option("--max-chars ", "Max output characters", parseInt) + .option("--search-only", "Disable file access (search only)") + .option("-p, --path ", "Path override for filesystem source") + .action(async (query, options) => { + try { + // Create store + let store; + if (options.store === "filesystem") { + store = new FilesystemStore({ basePath: options.storePath }); + } else if (options.store === "s3") { + if (!options.bucket) { + console.error("S3 store requires --bucket option"); + process.exit(1); + } + const { S3Store } = await import("../stores/s3.js"); + store = new S3Store({ + bucket: options.bucket, + prefix: options.s3Prefix, + region: options.s3Region, + endpoint: options.s3Endpoint, + forcePathStyle: options.s3ForcePathStyle, + }); + } else { + console.error(`Unknown store type: ${options.store}`); + process.exit(1); + } + + // Load state to get source metadata + const state = await store.load(options.key); + if (!state) { + console.error(`Index "${options.key}" not found`); + process.exit(1); + } + + // Create source unless --search-only is specified + let source; + if (!options.searchOnly) { + if (state.source.type === "filesystem") { + const path = options.path ?? state.source.identifier; + source = new FilesystemSource({ rootPath: path }); + } else if (state.source.type === "github") { + const [owner, repo] = state.source.identifier.split("/"); + const { GitHubSource } = await import("../sources/github.js"); + source = new GitHubSource({ + owner, + repo, + ref: state.source.ref, + }); + } else if (state.source.type === "gitlab") { + const { GitLabSource } = await import("../sources/gitlab.js"); + source = new GitLabSource({ + projectId: state.source.identifier, + ref: state.source.ref, + }); + } else if (state.source.type === "website") { + const { WebsiteSource } = await import("../sources/website.js"); + source = new WebsiteSource({ + url: `https://${state.source.identifier}`, + }); + } + } + + // Create client + const client = new SearchClient({ + store, + source, + key: options.key, + }); + + await client.initialize(); + + const meta = client.getMetadata(); + console.log(`Searching index: ${options.key}`); + console.log(`Source: ${meta.type}://${meta.identifier}`); + console.log(`Last synced: ${meta.syncedAt}\n`); + + const result = await client.search(query, { + maxOutputLength: options.maxChars, + }); + + if (!result.results || result.results.trim().length === 0) { + console.log("No results found."); + return; + } + + console.log("Results:\n"); + console.log(result.results); + } catch (error) { + console.error("Search failed:", error); + process.exit(1); + } + }); + diff --git a/context-connectors/src/bin/index.ts b/context-connectors/src/bin/index.ts new file mode 100644 index 0000000..06415c0 --- /dev/null +++ b/context-connectors/src/bin/index.ts @@ -0,0 +1,34 @@ +#!/usr/bin/env node +/** + * CLI entry point for context-connectors + */ + +import { Command } from "commander"; +import { indexCommand } from "./cmd-index.js"; +import { searchCommand } from "./cmd-search.js"; +import { listCommand } from "./cmd-list.js"; +import { deleteCommand } from "./cmd-delete.js"; +import { initCommand } from "./cmd-init.js"; +import { mcpCommand } from "./cmd-mcp.js"; +import { agentCommand } from "./cmd-agent.js"; +import { mcpServeCommand } from "./cmd-mcp-serve.js"; + +const program = new Command(); + +program + .name("context-connectors") + .description("Index and search any data source with Augment's context engine") + .version("0.1.0"); + +// Add subcommands +program.addCommand(indexCommand); +program.addCommand(searchCommand); +program.addCommand(listCommand); +program.addCommand(deleteCommand); +program.addCommand(initCommand); +program.addCommand(mcpCommand); +program.addCommand(mcpServeCommand); +program.addCommand(agentCommand); + +program.parse(); + diff --git a/context-connectors/src/clients/cli-agent.test.ts b/context-connectors/src/clients/cli-agent.test.ts new file mode 100644 index 0000000..5aeacea --- /dev/null +++ b/context-connectors/src/clients/cli-agent.test.ts @@ -0,0 +1,87 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { CLIAgent } from "./cli-agent.js"; + +// Mock the AI SDK +vi.mock("ai", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + generateText: vi.fn(), + streamText: vi.fn(), + }; +}); + +// Mock all provider packages +vi.mock("@ai-sdk/openai", () => ({ + openai: vi.fn(() => "mock-openai-model"), +})); + +vi.mock("@ai-sdk/anthropic", () => ({ + anthropic: vi.fn(() => "mock-anthropic-model"), +})); + +vi.mock("@ai-sdk/google", () => ({ + google: vi.fn(() => "mock-google-model"), +})); + +describe("CLIAgent", () => { + let mockClient: any; + + beforeEach(() => { + mockClient = { + hasSource: vi.fn().mockReturnValue(true), + getMetadata: vi.fn().mockReturnValue({ type: "filesystem", identifier: "/test" }), + search: vi.fn(), + listFiles: vi.fn(), + readFile: vi.fn(), + }; + }); + + it("creates agent with openai provider", () => { + const agent = new CLIAgent({ + client: mockClient, + provider: "openai", + model: "gpt-5.2", + }); + expect(agent).toBeDefined(); + }); + + it("creates agent with anthropic provider", () => { + const agent = new CLIAgent({ + client: mockClient, + provider: "anthropic", + model: "claude-sonnet-4-5", + }); + expect(agent).toBeDefined(); + }); + + it("creates agent with google provider", () => { + const agent = new CLIAgent({ + client: mockClient, + provider: "google", + model: "gemini-3-pro", + }); + expect(agent).toBeDefined(); + }); + + it("resets conversation history", () => { + const agent = new CLIAgent({ + client: mockClient, + provider: "openai", + model: "gpt-5.2", + }); + agent.reset(); + expect(agent.getHistory()).toHaveLength(0); + }); + + it("uses custom system prompt", () => { + const agent = new CLIAgent({ + client: mockClient, + provider: "openai", + model: "gpt-5.2", + systemPrompt: "Custom prompt", + }); + expect(agent).toBeDefined(); + }); +}); + diff --git a/context-connectors/src/clients/cli-agent.ts b/context-connectors/src/clients/cli-agent.ts new file mode 100644 index 0000000..27c6b04 --- /dev/null +++ b/context-connectors/src/clients/cli-agent.ts @@ -0,0 +1,418 @@ +/** + * CLI Agent - Interactive AI agent for codebase Q&A. + * + * Uses AI SDK tools in an agentic loop for answering questions about + * indexed codebases. Supports multiple LLM providers and both + * interactive (REPL) and single-query modes. + * + * @module clients/cli-agent + * + * @example + * ```typescript + * import { CLIAgent } from "@augmentcode/context-connectors"; + * + * const agent = new CLIAgent({ + * client: searchClient, + * provider: "openai", + * model: "gpt-4o", + * }); + * await agent.initialize(); + * + * const response = await agent.ask("How does authentication work?"); + * console.log(response); + * ``` + */ + +import { + generateText, + streamText, + CoreMessage, + ToolSet, + stepCountIs, + LanguageModel, + tool, +} from "ai"; +import { z } from "zod"; +import type { SearchClient } from "./search-client.js"; + +/** + * Supported LLM providers. + * Each requires its corresponding AI SDK provider package to be installed. + */ +export type Provider = "openai" | "anthropic" | "google"; + +/** + * Configuration for the CLI agent. + */ +export interface CLIAgentConfig { + /** Initialized SearchClient instance */ + client: SearchClient; + /** LLM provider to use */ + provider: Provider; + /** Model name (e.g., "gpt-4o", "claude-3-opus", "gemini-pro") */ + model: string; + /** + * Maximum number of agent steps (tool calls + responses). + * @default 10 + */ + maxSteps?: number; + /** + * Log tool calls to stderr for debugging. + * @default false + */ + verbose?: boolean; + /** + * Stream responses token by token. + * @default true + */ + stream?: boolean; + /** Custom system prompt. Uses a sensible default if not provided. */ + systemPrompt?: string; +} + +const DEFAULT_SYSTEM_PROMPT = `You are a helpful coding assistant with access to a codebase. + +Available tools: +- search: Find relevant code using natural language queries +- listFiles: List files in the project (with optional glob filter) +- readFile: Read the contents of a specific file + +When answering questions: +1. Use the search tool to find relevant code +2. Use listFiles to understand project structure if needed +3. Use readFile to examine specific files in detail +4. Provide clear, actionable answers based on the actual code + +Be concise but thorough. Reference specific files and line numbers when helpful.`; + +/** + * Load a model from the specified provider. + * Provider packages are optional - users only need to install the one they use. + */ +async function loadModel( + provider: Provider, + modelName: string +): Promise { + switch (provider) { + case "openai": { + try { + const { openai } = await import("@ai-sdk/openai"); + // Use openai.chat() instead of openai() to use the Chat Completions API + // rather than the Responses API. The Responses API is stateful and doesn't + // work with Zero Data Retention (ZDR) organizations because function call + // IDs (fc_...) are not persisted server-side. + return openai.chat(modelName); + } catch { + throw new Error( + `OpenAI provider not installed. Run: npm install @ai-sdk/openai` + ); + } + } + case "anthropic": { + try { + const { anthropic } = await import("@ai-sdk/anthropic"); + return anthropic(modelName); + } catch { + throw new Error( + `Anthropic provider not installed. Run: npm install @ai-sdk/anthropic` + ); + } + } + case "google": { + try { + const { google } = await import("@ai-sdk/google"); + return google(modelName); + } catch { + throw new Error( + `Google provider not installed. Run: npm install @ai-sdk/google` + ); + } + } + default: + throw new Error(`Unknown provider: ${provider}`); + } +} + +/** + * Interactive AI agent for codebase Q&A. + * + * The agent maintains conversation history, allowing for follow-up + * questions. It uses the configured LLM to answer questions by + * automatically calling search, listFiles, and readFile tools. + * + * @example + * ```typescript + * const agent = new CLIAgent({ + * client: searchClient, + * provider: "openai", + * model: "gpt-4o", + * verbose: true, // Show tool calls + * }); + * + * await agent.initialize(); + * + * // Ask questions + * await agent.ask("What does this project do?"); + * await agent.ask("Show me the main entry point"); + * + * // Reset for new conversation + * agent.reset(); + * ``` + */ +export class CLIAgent { + private readonly client: SearchClient; + private model: LanguageModel | null = null; + private readonly provider: Provider; + private readonly modelName: string; + private readonly maxSteps: number; + private readonly verbose: boolean; + private readonly stream: boolean; + private readonly systemPrompt: string; + private readonly tools: ToolSet; + private messages: CoreMessage[] = []; + + /** + * Create a new CLI agent. + * + * Note: You must call `initialize()` before using the agent. + * + * @param config - Agent configuration + */ + constructor(config: CLIAgentConfig) { + this.client = config.client; + this.provider = config.provider; + this.modelName = config.model; + this.maxSteps = config.maxSteps ?? 10; + this.verbose = config.verbose ?? false; + this.stream = config.stream ?? true; + this.systemPrompt = config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT; + this.tools = this.createTools(); + } + + /** + * Create AI SDK tools for the SearchClient. + */ + private createTools(): ToolSet { + const client = this.client; + const hasSource = client.hasSource(); + + // Tool descriptions adapted from Auggie CLI + const searchDescription = `Search the indexed content using natural language. +Returns relevant snippets organized by file path with line numbers. + +Features: +- Takes a natural language description of what you're looking for +- Returns snippets ranked by relevance +- Works across different file types +- Reflects the indexed state of the content`; + + const listFilesDescription = `List files and directories with type annotations. +* \`directory\` is a path relative to the source root +* Lists files and subdirectories up to 2 levels deep by default +* Hidden files (starting with \`.\`) are excluded by default +* Supports glob pattern filtering (e.g., \`*.ts\`, \`src/*.json\`) +* If the output is long, it will be truncated`; + + const readFileDescription = `Read file contents with line numbers (cat -n format). +* \`path\` is a file path relative to the source root +* Displays output with 6-character padded line numbers +* Use \`startLine\` and \`endLine\` to read a specific range (1-based, inclusive) +* Use \`searchPattern\` for regex search - only matching lines and context will be shown +* Large files are automatically truncated + +Regex search: +* Use \`searchPattern\` to search for patterns in the file +* Non-matching sections between matches are replaced with \`...\` +* Supported: \`.\`, \`[abc]\`, \`[a-z]\`, \`^\`, \`$\`, \`*\`, \`+\`, \`?\`, \`{n,m}\`, \`|\`, \`\\t\` +* Not supported: \`\\n\`, \`\\d\`, \`\\s\`, \`\\w\`, look-ahead/behind, back-references`; + + const searchSchema = z.object({ + query: z.string().describe("Natural language description of what you're looking for."), + maxChars: z.number().optional().describe("Maximum characters in response."), + }); + + const searchTool = tool({ + description: searchDescription, + inputSchema: searchSchema, + execute: async ({ query, maxChars }: z.infer) => { + const result = await client.search(query, { maxOutputLength: maxChars }); + return result.results || "No results found."; + }, + }); + + if (hasSource) { + const listFilesSchema = z.object({ + directory: z.string().optional().describe("Directory to list (default: root)."), + pattern: z.string().optional().describe("Glob pattern to filter results (e.g., '*.ts', 'src/*.json')."), + depth: z.number().optional().describe("Maximum depth to recurse (default: 2). Use 1 for immediate children only."), + showHidden: z.boolean().optional().describe("Include hidden files starting with '.' (default: false)."), + }); + + const listFilesTool = tool({ + description: listFilesDescription, + inputSchema: listFilesSchema, + execute: async ({ directory, pattern, depth, showHidden }: z.infer) => { + const opts = { directory, pattern, depth, showHidden }; + const entries = await client.listFiles(opts); + const { formatListOutput } = await import("../tools/list-files.js"); + return formatListOutput(entries, opts); + }, + }); + + const readFileSchema = z.object({ + path: z.string().describe("Path to the file to read, relative to the source root."), + startLine: z.number().optional().describe("First line to read (1-based, inclusive). Default: 1."), + endLine: z.number().optional().describe("Last line to read (1-based, inclusive). Use -1 for end of file. Default: -1."), + searchPattern: z.string().optional().describe("Regex pattern to search for. Only matching lines and context will be shown."), + contextLinesBefore: z.number().optional().describe("Lines of context before each regex match (default: 5)."), + contextLinesAfter: z.number().optional().describe("Lines of context after each regex match (default: 5)."), + includeLineNumbers: z.boolean().optional().describe("Include line numbers in output (default: true)."), + }); + + const readFileTool = tool({ + description: readFileDescription, + inputSchema: readFileSchema, + execute: async (args: z.infer) => { + const result = await client.readFile(args.path, { + startLine: args.startLine, + endLine: args.endLine, + searchPattern: args.searchPattern, + contextLinesBefore: args.contextLinesBefore, + contextLinesAfter: args.contextLinesAfter, + includeLineNumbers: args.includeLineNumbers, + }); + if (result.error) { + let errorText = `Error: ${result.error}`; + if (result.suggestions && result.suggestions.length > 0) { + errorText += `\n\nDid you mean one of these?\n${result.suggestions.map(s => ` - ${s}`).join("\n")}`; + } + return errorText; + } + return result.contents ?? ""; + }, + }); + + return { + search: searchTool, + listFiles: listFilesTool, + readFile: readFileTool, + } as ToolSet; + } + + return { + search: searchTool, + } as ToolSet; + } + + /** + * Initialize the agent by loading the model from the provider. + * + * Must be called before using `ask()`. + * + * @throws Error if the provider package is not installed + */ + async initialize(): Promise { + this.model = await loadModel(this.provider, this.modelName); + } + + /** + * Ask a question and get a response. + * + * The response is generated by the LLM, which may call tools + * (search, listFiles, readFile) to gather information before + * answering. + * + * The question and response are added to conversation history, + * enabling follow-up questions. + * + * @param query - The question to ask + * @returns The agent's response text + * @throws Error if agent not initialized + * + * @example + * ```typescript + * const response = await agent.ask("How is authentication implemented?"); + * console.log(response); + * ``` + */ + async ask(query: string): Promise { + if (!this.model) { + throw new Error("Agent not initialized. Call initialize() first."); + } + + this.messages.push({ role: "user", content: query }); + + if (this.stream) { + return this.streamResponse(); + } else { + return this.generateResponse(); + } + } + + private async generateResponse(): Promise { + const result = await generateText({ + model: this.model!, + tools: this.tools, + stopWhen: stepCountIs(this.maxSteps), + system: this.systemPrompt, + messages: this.messages, + onStepFinish: this.verbose ? this.logStep.bind(this) : undefined, + }); + + this.messages.push({ role: "assistant", content: result.text }); + return result.text; + } + + private async streamResponse(): Promise { + const result = streamText({ + model: this.model!, + tools: this.tools, + stopWhen: stepCountIs(this.maxSteps), + system: this.systemPrompt, + messages: this.messages, + onStepFinish: this.verbose ? this.logStep.bind(this) : undefined, + }); + + let fullText = ""; + for await (const chunk of result.textStream) { + process.stdout.write(chunk); + fullText += chunk; + } + process.stdout.write("\n"); + + this.messages.push({ role: "assistant", content: fullText }); + return fullText; + } + + private logStep(step: { + toolCalls?: Array<{ toolName: string; args?: unknown }>; + }) { + if (step.toolCalls) { + for (const call of step.toolCalls) { + console.error( + `\x1b[90m[tool] ${call.toolName}(${JSON.stringify(call.args ?? {})})\x1b[0m` + ); + } + } + } + + /** + * Reset conversation history. + * + * Use this to start a fresh conversation without tool context + * from previous questions. + */ + reset(): void { + this.messages = []; + } + + /** + * Get a copy of the conversation history. + * + * @returns Array of messages (user and assistant turns) + */ + getHistory(): CoreMessage[] { + return [...this.messages]; + } +} + diff --git a/context-connectors/src/clients/index.ts b/context-connectors/src/clients/index.ts new file mode 100644 index 0000000..e91e3fe --- /dev/null +++ b/context-connectors/src/clients/index.ts @@ -0,0 +1,17 @@ +/** + * Clients module exports + */ + +export { SearchClient, type SearchClientConfig } from "./search-client.js"; +export { CLIAgent, type CLIAgentConfig, type Provider } from "./cli-agent.js"; +export { + createMCPServer, + runMCPServer, + type MCPServerConfig, +} from "./mcp-server.js"; +export { + createMCPHttpServer, + runMCPHttpServer, + type MCPHttpServerConfig, + type MCPHttpServer, +} from "./mcp-http-server.js"; diff --git a/context-connectors/src/clients/mcp-http-server.ts b/context-connectors/src/clients/mcp-http-server.ts new file mode 100644 index 0000000..ab2b239 --- /dev/null +++ b/context-connectors/src/clients/mcp-http-server.ts @@ -0,0 +1,438 @@ +/** + * MCP HTTP Server - Exposes context-connector tools over HTTP transport. + * + * Provides HTTP (Streamable HTTP transport) access to the MCP server, + * allowing remote clients to connect over the network. + * + * @module clients/mcp-http-server + * @see https://modelcontextprotocol.io/ + * + * @example + * ```typescript + * import { runMCPHttpServer } from "@augmentcode/context-connectors/clients"; + * import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + * + * const server = await runMCPHttpServer({ + * store: new FilesystemStore(), + * key: "my-project", + * port: 3000, + * }); + * + * console.log(`MCP server listening at ${server.getUrl()}`); + * ``` + */ + +import type { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; +import { createServer, IncomingMessage, ServerResponse } from "node:http"; +import { randomUUID, timingSafeEqual } from "node:crypto"; +import { createMCPServer, MCPServerConfig } from "./mcp-server.js"; + +/** + * Timing-safe string comparison to prevent timing attacks. + */ +function safeCompare(a: string, b: string): boolean { + const bufA = Buffer.from(a); + const bufB = Buffer.from(b); + if (bufA.length !== bufB.length) return false; + return timingSafeEqual(bufA, bufB); +} + +/** + * Authentication result from auth middleware. + */ +interface AuthResult { + authorized: boolean; + error?: string; +} + +/** + * Create authentication middleware for API key validation. + */ +function createAuthMiddleware( + apiKey: string | undefined +): (req: IncomingMessage) => AuthResult { + return (req: IncomingMessage): AuthResult => { + if (!apiKey) { + // No auth configured, allow all requests + return { authorized: true }; + } + + const authHeader = req.headers["authorization"]; + if (!authHeader) { + return { authorized: false, error: "Missing Authorization header" }; + } + + // Support "Bearer " format + const match = authHeader.match(/^Bearer\s+(.+)$/i); + if (!match) { + return { authorized: false, error: "Invalid Authorization header format" }; + } + + const token = match[1]; + if (!safeCompare(token, apiKey)) { + return { authorized: false, error: "Invalid API key" }; + } + + return { authorized: true }; + }; +} + +/** + * Configuration for the MCP HTTP server. + */ +export interface MCPHttpServerConfig extends MCPServerConfig { + /** Port to listen on. @default 3000 */ + port?: number; + + /** Host to bind to. @default "localhost" */ + host?: string; + + /** + * CORS origin(s) to allow. + * Set to "*" for any origin, or specific origin(s). + * @default undefined (no CORS headers) + */ + cors?: string | string[]; + + /** + * Base path for MCP endpoint. + * @default "/mcp" + */ + basePath?: string; + + /** + * API key for authentication. + * When set, clients must provide this key in the Authorization header. + * Format: "Authorization: Bearer " + */ + apiKey?: string; +} + +/** + * Interface for the MCP HTTP server instance. + */ +export interface MCPHttpServer { + /** Start the HTTP server */ + start(): Promise; + + /** Stop the HTTP server */ + stop(): Promise; + + /** Get the server URL */ + getUrl(): string; +} + +/** + * Create an MCP HTTP server instance. + * + * Creates but does not start the server. Call `start()` to begin listening. + * + * @param config - Server configuration + * @returns MCP HTTP server instance + */ +export async function createMCPHttpServer( + config: MCPHttpServerConfig +): Promise { + const port = config.port ?? 3000; + const host = config.host ?? "localhost"; + const basePath = config.basePath ?? "/mcp"; + const cors = config.cors; + const apiKey = config.apiKey; + + // Create auth middleware + const checkAuth = createAuthMiddleware(apiKey); + + // Store transports by session ID + const transports: Map = new Map(); + + // Create the underlying MCP server factory (creates new instance per session) + const createServerInstance = async (): Promise => { + return createMCPServer(config); + }; + + /** + * Set CORS headers if configured. + */ + const setCorsHeaders = (req: IncomingMessage, res: ServerResponse): void => { + if (!cors) return; + + const origin = req.headers.origin; + if (!origin) return; + + if (cors === "*") { + res.setHeader("Access-Control-Allow-Origin", "*"); + } else if (Array.isArray(cors)) { + if (cors.includes(origin)) { + res.setHeader("Access-Control-Allow-Origin", origin); + } + } else if (cors === origin) { + res.setHeader("Access-Control-Allow-Origin", origin); + } + + res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS"); + res.setHeader( + "Access-Control-Allow-Headers", + "Content-Type, Mcp-Session-Id, Authorization" + ); + res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id"); + }; + + /** + * Parse JSON body from request. + */ + const parseBody = (req: IncomingMessage): Promise => { + return new Promise((resolve, reject) => { + let body = ""; + req.on("data", (chunk) => (body += chunk)); + req.on("end", () => { + if (!body) { + resolve(undefined); + return; + } + try { + resolve(JSON.parse(body)); + } catch (e) { + reject(new Error("Invalid JSON body")); + } + }); + req.on("error", reject); + }); + }; + + /** + * Handle HTTP requests. + */ + const handleRequest = async ( + req: IncomingMessage, + res: ServerResponse + ): Promise => { + const url = new URL(req.url ?? "/", `http://${host}:${port}`); + + // Set CORS headers + setCorsHeaders(req, res); + + // Handle CORS preflight + if (req.method === "OPTIONS") { + res.writeHead(204); + res.end(); + return; + } + + // Check if request is for MCP endpoint + if (!url.pathname.startsWith(basePath)) { + res.writeHead(404, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ error: "Not found" })); + return; + } + + // Check authentication + const authResult = checkAuth(req); + if (!authResult.authorized) { + res.writeHead(401, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ error: authResult.error })); + return; + } + + const sessionId = req.headers["mcp-session-id"] as string | undefined; + + try { + if (req.method === "POST") { + await handlePost(req, res, sessionId); + } else if (req.method === "GET") { + await handleGet(req, res, sessionId); + } else if (req.method === "DELETE") { + await handleDelete(req, res, sessionId); + } else { + res.writeHead(405, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ error: "Method not allowed" })); + } + } catch (error) { + if (!res.headersSent) { + res.writeHead(500, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + jsonrpc: "2.0", + error: { code: -32603, message: "Internal server error" }, + id: null, + }) + ); + } + } + }; + + /** + * Handle POST requests (JSON-RPC messages). + */ + const handlePost = async ( + req: IncomingMessage, + res: ServerResponse, + sessionId: string | undefined + ): Promise => { + const body = await parseBody(req); + + let transport: StreamableHTTPServerTransport; + + if (sessionId && transports.has(sessionId)) { + // Reuse existing transport for this session + transport = transports.get(sessionId)!; + } else if (!sessionId && isInitializeRequest(body)) { + // New initialization request - create new transport and server + transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + onsessioninitialized: (newSessionId: string) => { + transports.set(newSessionId, transport); + }, + onsessionclosed: (closedSessionId: string) => { + transports.delete(closedSessionId); + }, + }); + + // Set up cleanup on close + transport.onclose = () => { + const sid = transport.sessionId; + if (sid) { + transports.delete(sid); + } + }; + + // Connect the transport to a new MCP server instance + const server = await createServerInstance(); + await server.connect(transport); + } else { + // Invalid request - no session ID or not initialization + res.writeHead(400, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + jsonrpc: "2.0", + error: { + code: -32000, + message: "Bad Request: No valid session ID provided", + }, + id: null, + }) + ); + return; + } + + await transport.handleRequest(req, res, body); + }; + + /** + * Handle GET requests (SSE streams). + */ + const handleGet = async ( + req: IncomingMessage, + res: ServerResponse, + sessionId: string | undefined + ): Promise => { + if (!sessionId || !transports.has(sessionId)) { + res.writeHead(400, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ error: "Invalid or missing session ID" })); + return; + } + + const transport = transports.get(sessionId)!; + await transport.handleRequest(req, res); + }; + + /** + * Handle DELETE requests (session termination). + */ + const handleDelete = async ( + req: IncomingMessage, + res: ServerResponse, + sessionId: string | undefined + ): Promise => { + if (!sessionId || !transports.has(sessionId)) { + res.writeHead(400, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ error: "Invalid or missing session ID" })); + return; + } + + const transport = transports.get(sessionId)!; + await transport.handleRequest(req, res); + }; + + // Create the HTTP server + const httpServer = createServer(handleRequest); + + let started = false; + + return { + async start(): Promise { + if (started) return; + + return new Promise((resolve, reject) => { + httpServer.on("error", reject); + httpServer.listen(port, host, () => { + started = true; + resolve(); + }); + }); + }, + + async stop(): Promise { + if (!started) return; + + // Close all active transports + for (const [sessionId, transport] of transports) { + try { + await transport.close(); + } catch { + // Ignore errors during cleanup + } + transports.delete(sessionId); + } + + // Close the HTTP server + return new Promise((resolve, reject) => { + httpServer.close((err) => { + if (err) reject(err); + else { + started = false; + resolve(); + } + }); + }); + }, + + getUrl(): string { + return `http://${host}:${port}${basePath}`; + }, + }; +} + +/** + * Run an MCP server with HTTP transport. + * + * Convenience function that creates and starts the server. + * Returns when server is listening. + * + * @param config - Server configuration + * @returns Running MCP HTTP server instance + * + * @example + * ```typescript + * const server = await runMCPHttpServer({ + * store: new FilesystemStore(), + * key: "my-project", + * port: 3000, + * cors: "*", + * }); + * + * console.log(`Server running at ${server.getUrl()}`); + * + * // Later, to shut down: + * await server.stop(); + * ``` + */ +export async function runMCPHttpServer( + config: MCPHttpServerConfig +): Promise { + const server = await createMCPHttpServer(config); + await server.start(); + return server; +} + diff --git a/context-connectors/src/clients/mcp-server.test.ts b/context-connectors/src/clients/mcp-server.test.ts new file mode 100644 index 0000000..422b36a --- /dev/null +++ b/context-connectors/src/clients/mcp-server.test.ts @@ -0,0 +1,134 @@ +/** + * Tests for MCP Server + */ + +import { describe, it, expect, vi, beforeEach } from "vitest"; +import type { IndexState } from "../core/types.js"; +import type { IndexStoreReader } from "../stores/types.js"; +import type { Source } from "../sources/types.js"; + +// Try to import SDK-dependent modules +let createMCPServer: typeof import("./mcp-server.js").createMCPServer; +let sdkLoadError: Error | null = null; + +try { + const mcpMod = await import("./mcp-server.js"); + createMCPServer = mcpMod.createMCPServer; +} catch (e) { + sdkLoadError = e as Error; +} + +// Create mock IndexState +const createMockState = (): IndexState => ({ + contextState: { + blobs: [], + version: 1, + } as any, + source: { + type: "filesystem", + identifier: "/test/path", + syncedAt: new Date().toISOString(), + }, +}); + +// Create mock Store +const createMockStore = (state: IndexState | null): IndexStoreReader => ({ + load: vi.fn().mockResolvedValue(state), + list: vi.fn().mockResolvedValue(state ? ["test-key"] : []), +}); + +// Create mock Source +const createMockSource = (): Source => + ({ + type: "filesystem" as const, + listFiles: vi.fn().mockResolvedValue([ + { path: "src/index.ts" }, + { path: "src/utils.ts" }, + { path: "README.md" }, + ]), + readFile: vi.fn().mockImplementation((path: string) => { + if (path === "src/index.ts") { + return Promise.resolve("export const version = '1.0.0';"); + } + if (path === "not-found.ts") { + return Promise.reject(new Error("File not found")); + } + return Promise.resolve("file content"); + }), + fetchAll: vi.fn(), + fetchChanges: vi.fn(), + getMetadata: vi.fn().mockResolvedValue({ + type: "filesystem", + identifier: "/test/path", + syncedAt: new Date().toISOString(), + }), + }) as unknown as Source; + +// Check if API credentials are available for tests +const hasApiCredentials = !!( + process.env.AUGMENT_API_TOKEN && process.env.AUGMENT_API_URL +); + +describe.skipIf(sdkLoadError !== null || !hasApiCredentials)( + "MCP Server", + () => { + describe("createMCPServer", () => { + it("creates server with search tool only when no source", async () => { + const store = createMockStore(createMockState()); + const server = await createMCPServer({ + store, + key: "test-key", + }); + + expect(server).toBeDefined(); + }); + + it("creates server with file tools when source provided", async () => { + const store = createMockStore(createMockState()); + const source = createMockSource(); + + const server = await createMCPServer({ + store, + source, + key: "test-key", + }); + + expect(server).toBeDefined(); + }); + + it("uses custom name and version", async () => { + const store = createMockStore(createMockState()); + + const server = await createMCPServer({ + store, + key: "test-key", + name: "custom-server", + version: "2.0.0", + }); + + expect(server).toBeDefined(); + }); + + it("throws error when index not found", async () => { + const store = createMockStore(null); + + await expect( + createMCPServer({ + store, + key: "missing-key", + }) + ).rejects.toThrow('Index "missing-key" not found'); + }); + }); + } +); + +// Unit tests that don't need API credentials +describe.skipIf(sdkLoadError !== null)("MCP Server Unit Tests", () => { + describe("module loading", () => { + it("exports createMCPServer function", () => { + expect(typeof createMCPServer).toBe("function"); + }); + }); +}); + diff --git a/context-connectors/src/clients/mcp-server.ts b/context-connectors/src/clients/mcp-server.ts new file mode 100644 index 0000000..deb20f9 --- /dev/null +++ b/context-connectors/src/clients/mcp-server.ts @@ -0,0 +1,346 @@ +/** + * MCP Server - Exposes context-connector tools to AI assistants. + * + * Implements the Model Context Protocol (MCP) to enable integration with: + * - Claude Desktop + * - Other MCP-compatible AI assistants + * + * The server exposes these tools: + * - `search`: Always available + * - `list_files`: Available when Source is configured + * - `read_file`: Available when Source is configured + * + * @module clients/mcp-server + * @see https://modelcontextprotocol.io/ + * + * @example + * ```typescript + * import { runMCPServer } from "@augmentcode/context-connectors"; + * import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + * + * await runMCPServer({ + * store: new FilesystemStore(), + * key: "my-project", + * }); + * ``` + */ + +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; +import type { IndexStoreReader } from "../stores/types.js"; +import type { Source } from "../sources/types.js"; +import { SearchClient } from "./search-client.js"; + +/** + * Configuration for the MCP server. + */ +export interface MCPServerConfig { + /** Store to load index from */ + store: IndexStoreReader; + /** + * Optional source for file operations. + * When provided, enables list_files and read_file tools. + */ + source?: Source; + /** Index key/name to serve */ + key: string; + /** + * Server name reported to MCP clients. + * @default "context-connectors" + */ + name?: string; + /** + * Server version reported to MCP clients. + * @default "0.1.0" + */ + version?: string; +} + +/** + * Create an MCP server instance. + * + * Creates but does not start the server. Use `runMCPServer()` for + * the common case of running with stdio transport. + * + * @param config - Server configuration + * @returns Configured MCP Server instance + * + * @example + * ```typescript + * const server = await createMCPServer({ + * store: new FilesystemStore(), + * key: "my-project", + * }); + * + * // Connect with custom transport + * await server.connect(myTransport); + * ``` + */ +export async function createMCPServer( + config: MCPServerConfig +): Promise { + // Initialize SearchClient + const client = new SearchClient({ + store: config.store, + source: config.source, + key: config.key, + }); + await client.initialize(); + + const meta = client.getMetadata(); + const hasSource = !!config.source; + + // Create MCP server + const server = new Server( + { + name: config.name ?? "context-connectors", + version: config.version ?? "0.1.0", + }, + { + capabilities: { + tools: {}, + }, + } + ); + + // Define tool type for type safety + type Tool = { + name: string; + description: string; + inputSchema: { + type: "object"; + properties: Record; + required?: string[]; + }; + }; + + // Tool descriptions adapted from Auggie CLI + const searchDescription = `Search the indexed content (${meta.type}://${meta.identifier}) using natural language. +Returns relevant snippets organized by file path with line numbers. + +Features: +- Takes a natural language description of what you're looking for +- Returns snippets ranked by relevance +- Works across different file types +- Reflects the indexed state of the content`; + + const listFilesDescription = `List files and directories with type annotations. +* \`directory\` is a path relative to the source root +* Lists files and subdirectories up to 2 levels deep by default +* Hidden files (starting with \`.\`) are excluded by default +* Supports glob pattern filtering (e.g., \`*.ts\`, \`src/*.json\`) +* If the output is long, it will be truncated`; + + const readFileDescription = `Read file contents with line numbers (cat -n format). +* \`path\` is a file path relative to the source root +* Displays output with 6-character padded line numbers +* Use \`startLine\` and \`endLine\` to read a specific range (1-based, inclusive) +* Use \`searchPattern\` for regex search - only matching lines and context will be shown +* Large files are automatically truncated + +Regex search: +* Use \`searchPattern\` to search for patterns in the file +* Non-matching sections between matches are replaced with \`...\` +* Supported: \`.\`, \`[abc]\`, \`[a-z]\`, \`^\`, \`$\`, \`*\`, \`+\`, \`?\`, \`{n,m}\`, \`|\`, \`\\t\` +* Not supported: \`\\n\`, \`\\d\`, \`\\s\`, \`\\w\`, look-ahead/behind, back-references`; + + // List available tools + server.setRequestHandler(ListToolsRequestSchema, async () => { + const tools: Tool[] = [ + { + name: "search", + description: searchDescription, + inputSchema: { + type: "object", + properties: { + query: { + type: "string", + description: "Natural language description of what you're looking for.", + }, + maxChars: { + type: "number", + description: "Maximum characters in response (optional).", + }, + }, + required: ["query"], + }, + }, + ]; + + // Only advertise file tools if source is configured + if (hasSource) { + tools.push( + { + name: "list_files", + description: listFilesDescription, + inputSchema: { + type: "object", + properties: { + directory: { + type: "string", + description: "Directory to list (default: root).", + }, + pattern: { + type: "string", + description: "Glob pattern to filter results (e.g., '*.ts', 'src/*.json').", + }, + depth: { + type: "number", + description: "Maximum depth to recurse (default: 2). Use 1 for immediate children only.", + }, + showHidden: { + type: "boolean", + description: "Include hidden files starting with '.' (default: false).", + }, + }, + required: [], + }, + }, + { + name: "read_file", + description: readFileDescription, + inputSchema: { + type: "object", + properties: { + path: { + type: "string", + description: "Path to the file to read, relative to the source root.", + }, + startLine: { + type: "number", + description: "First line to read (1-based, inclusive). Default: 1.", + }, + endLine: { + type: "number", + description: "Last line to read (1-based, inclusive). Use -1 for end of file. Default: -1.", + }, + searchPattern: { + type: "string", + description: "Regex pattern to search for. Only matching lines and context will be shown.", + }, + contextLinesBefore: { + type: "number", + description: "Lines of context before each regex match (default: 5).", + }, + contextLinesAfter: { + type: "number", + description: "Lines of context after each regex match (default: 5).", + }, + includeLineNumbers: { + type: "boolean", + description: "Include line numbers in output (default: true).", + }, + }, + required: ["path"], + }, + } + ); + } + + return { tools }; + }); + + // Handle tool calls + server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + try { + switch (name) { + case "search": { + const result = await client.search(args?.query as string, { + maxOutputLength: args?.maxChars as number | undefined, + }); + return { + content: [ + { type: "text", text: result.results || "No results found." }, + ], + }; + } + + case "list_files": { + const listOpts = { + directory: args?.directory as string | undefined, + pattern: args?.pattern as string | undefined, + depth: args?.depth as number | undefined, + showHidden: args?.showHidden as boolean | undefined, + }; + const entries = await client.listFiles(listOpts); + const { formatListOutput } = await import("../tools/list-files.js"); + const text = formatListOutput(entries, listOpts); + return { + content: [{ type: "text", text }], + }; + } + + case "read_file": { + const result = await client.readFile(args?.path as string, { + startLine: args?.startLine as number | undefined, + endLine: args?.endLine as number | undefined, + searchPattern: args?.searchPattern as string | undefined, + contextLinesBefore: args?.contextLinesBefore as number | undefined, + contextLinesAfter: args?.contextLinesAfter as number | undefined, + includeLineNumbers: args?.includeLineNumbers as boolean | undefined, + }); + if (result.error) { + let errorText = `Error: ${result.error}`; + if (result.suggestions && result.suggestions.length > 0) { + errorText += `\n\nDid you mean one of these?\n${result.suggestions.map(s => ` - ${s}`).join("\n")}`; + } + return { + content: [{ type: "text", text: errorText }], + isError: true, + }; + } + return { + content: [{ type: "text", text: result.contents ?? "" }], + }; + } + + default: + return { + content: [{ type: "text", text: `Unknown tool: ${name}` }], + isError: true, + }; + } + } catch (error) { + return { + content: [{ type: "text", text: `Error: ${error}` }], + isError: true, + }; + } + }); + + return server; +} + +/** + * Run an MCP server with stdio transport. + * + * This is the main entry point for running the MCP server. + * It creates the server and connects it to stdin/stdout for + * communication with the MCP client (e.g., Claude Desktop). + * + * This function does not return until the server is stopped. + * + * @param config - Server configuration + * + * @example + * ```typescript + * // Typically called from CLI + * await runMCPServer({ + * store: new FilesystemStore(), + * source: new FilesystemSource({ rootPath: "./project" }), + * key: "my-project", + * }); + * ``` + */ +export async function runMCPServer(config: MCPServerConfig): Promise { + const server = await createMCPServer(config); + const transport = new StdioServerTransport(); + await server.connect(transport); +} + diff --git a/context-connectors/src/clients/search-client.test.ts b/context-connectors/src/clients/search-client.test.ts new file mode 100644 index 0000000..064880a --- /dev/null +++ b/context-connectors/src/clients/search-client.test.ts @@ -0,0 +1,151 @@ +/** + * Tests for SearchClient + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { promises as fs } from "node:fs"; +import { join } from "node:path"; +import type { IndexState } from "../core/types.js"; +import type { IndexStoreReader } from "../stores/types.js"; +import type { Source } from "../sources/types.js"; + +// Try to import SDK-dependent modules +let SearchClient: typeof import("./search-client.js").SearchClient; +let sdkLoadError: Error | null = null; + +try { + const clientMod = await import("./search-client.js"); + SearchClient = clientMod.SearchClient; +} catch (e) { + sdkLoadError = e as Error; +} + +// Check if API credentials are available for integration tests +const hasApiCredentials = !!( + process.env.AUGMENT_API_TOKEN && process.env.AUGMENT_API_URL +); + +const TEST_STORE_DIR = "/tmp/context-connectors-test-search-client"; + +describe.skipIf(sdkLoadError !== null)("SearchClient", () => { + // Create mock IndexState + const createMockState = (): IndexState => ({ + contextState: { + blobs: [], + version: 1, + } as any, + source: { + type: "filesystem", + identifier: "/test/path", + syncedAt: new Date().toISOString(), + }, + }); + + // Create mock Store + const createMockStore = (state: IndexState | null): IndexStoreReader => ({ + load: vi.fn().mockResolvedValue(state), + list: vi.fn().mockResolvedValue(state ? ["test-key"] : []), + }); + + // Create mock Source + const createMockSource = (): Source => + ({ + type: "filesystem" as const, + listFiles: vi.fn().mockResolvedValue([{ path: "test.ts" }]), + readFile: vi.fn().mockResolvedValue("content"), + fetchAll: vi.fn(), + fetchChanges: vi.fn(), + getMetadata: vi.fn().mockResolvedValue({ + type: "filesystem", + identifier: "/test/path", + syncedAt: new Date().toISOString(), + }), + }) as unknown as Source; + + describe("constructor", () => { + it("creates client with required config", () => { + const store = createMockStore(createMockState()); + const client = new SearchClient({ + store, + key: "test-key", + }); + expect(client).toBeDefined(); + }); + + it("creates client with optional source", () => { + const store = createMockStore(createMockState()); + const source = createMockSource(); + const client = new SearchClient({ + store, + source, + key: "test-key", + }); + expect(client).toBeDefined(); + }); + }); + + describe("initialize", () => { + it("throws error when index not found", async () => { + const store = createMockStore(null); + const client = new SearchClient({ + store, + key: "missing-key", + }); + + await expect(client.initialize()).rejects.toThrow( + 'Index "missing-key" not found' + ); + }); + + it("throws error when source type mismatches", async () => { + const state = createMockState(); + const store = createMockStore(state); + const source = { + ...createMockSource(), + type: "github" as const, + getMetadata: vi.fn().mockResolvedValue({ + type: "github", + identifier: "owner/repo", + syncedAt: new Date().toISOString(), + }), + } as unknown as Source; + + const client = new SearchClient({ + store, + source, + key: "test-key", + }); + + await expect(client.initialize()).rejects.toThrow("Source type mismatch"); + }); + }); + + describe("getMetadata", () => { + it("throws error when not initialized", () => { + const store = createMockStore(createMockState()); + const client = new SearchClient({ + store, + key: "test-key", + }); + + expect(() => client.getMetadata()).toThrow("Client not initialized"); + }); + }); + + describe("listFiles without source", () => { + it("throws error when source not configured", async () => { + // This test would need API credentials to initialize + // Just verify the type signature works + const store = createMockStore(createMockState()); + const client = new SearchClient({ + store, + key: "test-key", + }); + + // Can't call listFiles without initializing first + // and can't initialize without API credentials + expect(typeof client.listFiles).toBe("function"); + }); + }); +}); + diff --git a/context-connectors/src/clients/search-client.ts b/context-connectors/src/clients/search-client.ts new file mode 100644 index 0000000..f0d5132 --- /dev/null +++ b/context-connectors/src/clients/search-client.ts @@ -0,0 +1,276 @@ +/** + * SearchClient - Client for searching indexed content. + * + * The SearchClient provides a high-level API for: + * - Semantic search across indexed content + * - File listing (when Source is provided) + * - File reading (when Source is provided) + * + * @module clients/search-client + * + * @example + * ```typescript + * import { SearchClient } from "@augmentcode/context-connectors"; + * import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + * import { FilesystemSource } from "@augmentcode/context-connectors/sources"; + * + * // Search-only mode (no file operations) + * const client = new SearchClient({ + * store: new FilesystemStore(), + * key: "my-project", + * }); + * await client.initialize(); + * const results = await client.search("authentication"); + * + * // Full mode (with file operations) + * const fullClient = new SearchClient({ + * store: new FilesystemStore(), + * source: new FilesystemSource({ rootPath: "./my-project" }), + * key: "my-project", + * }); + * await fullClient.initialize(); + * const files = await fullClient.listFiles({ pattern: "**\/*.ts" }); + * ``` + */ + +import { promises as fs } from "node:fs"; +import { DirectContext } from "@augmentcode/auggie-sdk"; +import type { IndexStoreReader } from "../stores/types.js"; +import type { Source } from "../sources/types.js"; +import type { IndexState } from "../core/types.js"; +import type { ToolContext, SearchOptions } from "../tools/types.js"; +import type { ListFilesOptions } from "../tools/list-files.js"; +import type { ReadFileOptions } from "../tools/read-file.js"; +import { search, listFiles, readFile } from "../tools/index.js"; + +/** + * Configuration for SearchClient. + */ +export interface SearchClientConfig { + /** Store to load index from (read-only access sufficient) */ + store: IndexStoreReader; + /** + * Optional source for file operations. + * When provided, enables listFiles() and readFile() methods. + * When omitted, client operates in search-only mode. + */ + source?: Source; + /** Index key/name to load */ + key: string; + /** + * Augment API key. + * @default process.env.AUGMENT_API_TOKEN + */ + apiKey?: string; + /** + * Augment API URL. + * @default process.env.AUGMENT_API_URL + */ + apiUrl?: string; +} + +/** + * Client for searching indexed content and accessing source files. + * + * The SearchClient operates in two modes: + * + * **Search-only mode** (no Source provided): + * - `search()` works + * - `listFiles()` and `readFile()` throw errors + * + * **Full mode** (Source provided): + * - All methods work + * - Source type must match the stored index + * + * @example + * ```typescript + * const client = new SearchClient({ + * store: new FilesystemStore(), + * source: new FilesystemSource({ rootPath: "." }), + * key: "my-project", + * }); + * + * await client.initialize(); + * + * // Search + * const { results } = await client.search("database connection"); + * + * // List files + * if (client.hasSource()) { + * const files = await client.listFiles({ pattern: "**\/*.sql" }); + * } + * ``` + */ +export class SearchClient { + private store: IndexStoreReader; + private source: Source | null; + private key: string; + private apiKey: string; + private apiUrl: string; + + private context: DirectContext | null = null; + private state: IndexState | null = null; + + /** + * Create a new SearchClient. + * + * Note: You must call `initialize()` before using the client. + * + * @param config - Client configuration + */ + constructor(config: SearchClientConfig) { + this.store = config.store; + this.source = config.source ?? null; + this.key = config.key; + this.apiKey = config.apiKey ?? process.env.AUGMENT_API_TOKEN ?? ""; + this.apiUrl = config.apiUrl ?? process.env.AUGMENT_API_URL ?? ""; + } + + /** + * Initialize the client by loading the index from the store. + * + * Must be called before using any other methods. + * Validates that the provided Source matches the stored index type. + * + * @throws Error if index not found or Source type mismatch + * + * @example + * ```typescript + * const client = new SearchClient({ store, key: "my-project" }); + * await client.initialize(); // Required! + * const results = await client.search("query"); + * ``` + */ + async initialize(): Promise { + // Load state from store + this.state = await this.store.load(this.key); + if (!this.state) { + throw new Error(`Index "${this.key}" not found`); + } + + // Validate source matches if provided + if (this.source) { + const sourceMeta = await this.source.getMetadata(); + if (sourceMeta.type !== this.state.source.type) { + throw new Error( + `Source type mismatch: expected ${this.state.source.type}, got ${sourceMeta.type}` + ); + } + // Note: identifier check could be relaxed (paths may differ slightly) + } + + // Import DirectContext from state (write to temp file, import, delete) + const tempFile = `/tmp/cc-state-${Date.now()}.json`; + await fs.writeFile(tempFile, JSON.stringify(this.state.contextState)); + this.context = await DirectContext.importFromFile(tempFile, { + apiKey: this.apiKey, + apiUrl: this.apiUrl, + }); + await fs.unlink(tempFile); + } + + private getToolContext(): ToolContext { + if (!this.context || !this.state) { + throw new Error("Client not initialized. Call initialize() first."); + } + return { context: this.context, source: this.source, state: this.state }; + } + + /** + * Search the indexed content using natural language. + * + * @param query - Natural language search query + * @param options - Optional search options + * @returns Search results with matching code snippets + * + * @example + * ```typescript + * const { results } = await client.search("user authentication", { + * maxOutputLength: 5000, + * }); + * console.log(results); + * ``` + */ + async search(query: string, options?: SearchOptions) { + return search(this.getToolContext(), query, options); + } + + /** + * List files and directories in the source. + * + * Requires a Source to be configured (full mode). + * By default, lists up to 2 levels deep (like Auggie CLI). + * + * @param options - Optional filter and depth options + * @returns Array of file/directory info objects with paths and types + * @throws Error if no Source is configured + * + * @example + * ```typescript + * // List with default depth (2 levels) + * const files = await client.listFiles(); + * + * // List only immediate children + * const shallow = await client.listFiles({ depth: 1 }); + * + * // List with pattern filter + * const tsFiles = await client.listFiles({ directory: "src", pattern: "*.ts" }); + * ``` + */ + async listFiles(options?: ListFilesOptions) { + return listFiles(this.getToolContext(), options); + } + + /** + * Read a file from the source. + * + * Requires a Source to be configured (full mode). + * Returns formatted output with line numbers by default. + * + * @param path - Relative path to the file + * @param options - Optional reading options (range, search, formatting) + * @returns File contents with formatting, or error + * @throws Error if no Source is configured + * + * @example + * ```typescript + * // Read entire file with line numbers + * const result = await client.readFile("src/index.ts"); + * + * // Read specific range + * const result = await client.readFile("src/index.ts", { + * startLine: 10, + * endLine: 50, + * }); + * + * // Search within file + * const result = await client.readFile("src/index.ts", { + * searchPattern: "export.*function", + * }); + * ``` + */ + async readFile(path: string, options?: ReadFileOptions) { + return readFile(this.getToolContext(), path, options); + } + + /** + * Get metadata about the indexed source. + * + * @returns Source metadata (type, identifier, ref, syncedAt) + * @throws Error if client not initialized + */ + getMetadata() { + if (!this.state) throw new Error("Client not initialized"); + return this.state.source; + } + + /** + * Check if a Source is available for file operations. + * + * @returns true if listFiles/readFile are available + */ + hasSource(): boolean { + return this.source !== null; + } +} + diff --git a/context-connectors/src/core/file-filter.test.ts b/context-connectors/src/core/file-filter.test.ts new file mode 100644 index 0000000..22d8105 --- /dev/null +++ b/context-connectors/src/core/file-filter.test.ts @@ -0,0 +1,151 @@ +/** + * Tests for file-filter module + */ + +import { describe, it, expect } from "vitest"; +import { + shouldFilterFile, + alwaysIgnorePath, + isKeyishPath, + isValidFileSize, + isValidUtf8, + DEFAULT_MAX_FILE_SIZE, +} from "./file-filter.js"; + +describe("shouldFilterFile", () => { + it("filters files with '..' in path", () => { + const result = shouldFilterFile({ + path: "../secret/file.txt", + content: Buffer.from("hello"), + }); + expect(result.filtered).toBe(true); + expect(result.reason).toBe("path_contains_dotdot"); + }); + + it("filters keyish files (.pem)", () => { + const result = shouldFilterFile({ + path: "certs/server.pem", + content: Buffer.from("-----BEGIN CERTIFICATE-----"), + }); + expect(result.filtered).toBe(true); + expect(result.reason).toBe("keyish_pattern"); + }); + + it("filters keyish files (.key)", () => { + const result = shouldFilterFile({ + path: "keys/private.key", + content: Buffer.from("-----BEGIN PRIVATE KEY-----"), + }); + expect(result.filtered).toBe(true); + expect(result.reason).toBe("keyish_pattern"); + }); + + it("filters keyish files (id_rsa)", () => { + const result = shouldFilterFile({ + path: ".ssh/id_rsa", + content: Buffer.from("-----BEGIN RSA PRIVATE KEY-----"), + }); + expect(result.filtered).toBe(true); + expect(result.reason).toBe("keyish_pattern"); + }); + + it("filters oversized files", () => { + const largeContent = Buffer.alloc(DEFAULT_MAX_FILE_SIZE + 1, "a"); + const result = shouldFilterFile({ + path: "large-file.txt", + content: largeContent, + }); + expect(result.filtered).toBe(true); + expect(result.reason).toContain("file_too_large"); + }); + + it("filters binary files", () => { + // Create content with invalid UTF-8 bytes + const binaryContent = Buffer.from([0x80, 0x81, 0x82, 0xff, 0xfe]); + const result = shouldFilterFile({ + path: "binary.dat", + content: binaryContent, + }); + expect(result.filtered).toBe(true); + expect(result.reason).toBe("binary_file"); + }); + + it("allows valid text files", () => { + const result = shouldFilterFile({ + path: "src/index.ts", + content: Buffer.from("export function hello() { return 'world'; }"), + }); + expect(result.filtered).toBe(false); + expect(result.reason).toBeUndefined(); + }); + + it("allows files with unicode content", () => { + const result = shouldFilterFile({ + path: "i18n/messages.json", + content: Buffer.from('{"greeting": "こんにちは", "emoji": "👋"}'), + }); + expect(result.filtered).toBe(false); + }); + + it("respects custom maxFileSize", () => { + const content = Buffer.alloc(100, "a"); + const result = shouldFilterFile({ + path: "file.txt", + content, + maxFileSize: 50, + }); + expect(result.filtered).toBe(true); + expect(result.reason).toContain("file_too_large"); + }); +}); + +describe("alwaysIgnorePath", () => { + it("returns true for paths with '..'", () => { + expect(alwaysIgnorePath("../file.txt")).toBe(true); + expect(alwaysIgnorePath("foo/../bar")).toBe(true); + expect(alwaysIgnorePath("foo/..")).toBe(true); + }); + + it("returns false for normal paths", () => { + expect(alwaysIgnorePath("foo/bar.txt")).toBe(false); + expect(alwaysIgnorePath("src/index.ts")).toBe(false); + }); +}); + +describe("isKeyishPath", () => { + it("matches key files", () => { + expect(isKeyishPath("private.key")).toBe(true); + expect(isKeyishPath("cert.pem")).toBe(true); + expect(isKeyishPath("keystore.jks")).toBe(true); + expect(isKeyishPath("id_rsa")).toBe(true); + expect(isKeyishPath("id_ed25519")).toBe(true); + }); + + it("does not match normal files", () => { + expect(isKeyishPath("index.ts")).toBe(false); + expect(isKeyishPath("README.md")).toBe(false); + }); +}); + +describe("isValidFileSize", () => { + it("returns true for files under limit", () => { + expect(isValidFileSize(1000)).toBe(true); + expect(isValidFileSize(DEFAULT_MAX_FILE_SIZE)).toBe(true); + }); + + it("returns false for files over limit", () => { + expect(isValidFileSize(DEFAULT_MAX_FILE_SIZE + 1)).toBe(false); + }); +}); + +describe("isValidUtf8", () => { + it("returns true for valid UTF-8", () => { + expect(isValidUtf8(Buffer.from("hello world"))).toBe(true); + expect(isValidUtf8(Buffer.from("こんにちは"))).toBe(true); + }); + + it("returns false for invalid UTF-8", () => { + expect(isValidUtf8(Buffer.from([0x80, 0x81, 0x82]))).toBe(false); + }); +}); + diff --git a/context-connectors/src/core/file-filter.ts b/context-connectors/src/core/file-filter.ts new file mode 100644 index 0000000..e1259ed --- /dev/null +++ b/context-connectors/src/core/file-filter.ts @@ -0,0 +1,102 @@ +/** + * File filtering logic for repository indexing + */ + +/** + * Keyish pattern regex - matches files that likely contain secrets/keys + */ +const KEYISH_PATTERN = + /^(\.git|.*\.pem|.*\.key|.*\.pfx|.*\.p12|.*\.jks|.*\.keystore|.*\.pkcs12|.*\.crt|.*\.cer|id_rsa|id_ed25519|id_ecdsa|id_dsa)$/; + +/** + * Default max file size in bytes (1 MB) + */ +export const DEFAULT_MAX_FILE_SIZE = 1024 * 1024; // 1 MB + +/** + * Check if a path should always be ignored (security measure) + */ +export function alwaysIgnorePath(path: string): boolean { + return path.includes(".."); +} + +/** + * Check if a path matches the keyish pattern (secrets/keys) + */ +export function isKeyishPath(path: string): boolean { + // Extract filename from path + const filename = path.split("/").pop() || ""; + return KEYISH_PATTERN.test(filename); +} + +/** + * Check if file size is valid for upload + */ +export function isValidFileSize( + sizeBytes: number, + maxFileSize = DEFAULT_MAX_FILE_SIZE +): boolean { + return sizeBytes <= maxFileSize; +} + +/** + * Check if file content is valid UTF-8 (not binary) + */ +export function isValidUtf8(content: Buffer): boolean { + try { + // Try to decode as UTF-8 + const decoded = content.toString("utf-8"); + // Re-encode and compare to detect invalid UTF-8 + const reencoded = Buffer.from(decoded, "utf-8"); + return content.equals(reencoded); + } catch { + return false; + } +} + +/** + * Check if a file should be filtered out + * Returns { filtered: true, reason: string } if file should be skipped + * Returns { filtered: false } if file should be included + * + * Priority order: + * 1. Path validation (contains "..") + * 2. File size check + * 3. .augmentignore rules (checked by caller) + * 4. Keyish patterns + * 5. .gitignore rules (checked by caller) + * 6. UTF-8 validation + */ +export function shouldFilterFile(params: { + path: string; + content: Buffer; + maxFileSize?: number; +}): { filtered: boolean; reason?: string } { + const { path, content, maxFileSize } = params; + + // 1. Check for ".." in path (security) + if (alwaysIgnorePath(path)) { + return { filtered: true, reason: "path_contains_dotdot" }; + } + + // 2. Check file size + if (!isValidFileSize(content.length, maxFileSize)) { + return { + filtered: true, + reason: `file_too_large (${content.length} bytes)`, + }; + } + + // 3. Check keyish patterns (secrets/keys) + if (isKeyishPath(path)) { + return { filtered: true, reason: "keyish_pattern" }; + } + + // 4. Check UTF-8 validity (binary detection) + if (!isValidUtf8(content)) { + return { filtered: true, reason: "binary_file" }; + } + + return { filtered: false }; +} + diff --git a/context-connectors/src/core/index.ts b/context-connectors/src/core/index.ts new file mode 100644 index 0000000..f8b5bf3 --- /dev/null +++ b/context-connectors/src/core/index.ts @@ -0,0 +1,26 @@ +/** + * Core module exports + */ + +export type { + FileEntry, + FileInfo, + SourceMetadata, + IndexState, + IndexResult, +} from "./types.js"; + +export { + DEFAULT_MAX_FILE_SIZE, + alwaysIgnorePath, + isKeyishPath, + isValidFileSize, + isValidUtf8, + shouldFilterFile, +} from "./file-filter.js"; + +export { sanitizeKey, isoTimestamp } from "./utils.js"; + +export { Indexer } from "./indexer.js"; +export type { IndexerConfig } from "./indexer.js"; + diff --git a/context-connectors/src/core/indexer.test.ts b/context-connectors/src/core/indexer.test.ts new file mode 100644 index 0000000..9b55fdf --- /dev/null +++ b/context-connectors/src/core/indexer.test.ts @@ -0,0 +1,160 @@ +/** + * Tests for Indexer + * + * Note: Integration tests that use DirectContext require AUGMENT_API_TOKEN + * and AUGMENT_API_URL environment variables to be set. + * + * These tests depend on @augmentcode/auggie-sdk being properly installed. + * If the SDK fails to load, tests will be skipped. + */ + +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { promises as fs } from "node:fs"; +import { join } from "node:path"; + +// Try to import SDK-dependent modules +let Indexer: typeof import("./indexer.js").Indexer; +let FilesystemSource: typeof import("../sources/filesystem.js").FilesystemSource; +let FilesystemStore: typeof import("../stores/filesystem.js").FilesystemStore; +let sdkLoadError: Error | null = null; + +try { + // These imports will fail if SDK is not properly installed + const indexerMod = await import("./indexer.js"); + const sourceMod = await import("../sources/filesystem.js"); + const storeMod = await import("../stores/filesystem.js"); + Indexer = indexerMod.Indexer; + FilesystemSource = sourceMod.FilesystemSource; + FilesystemStore = storeMod.FilesystemStore; +} catch (e) { + sdkLoadError = e as Error; +} + +const TEST_SOURCE_DIR = "/tmp/context-connectors-test-indexer-source"; +const TEST_STORE_DIR = "/tmp/context-connectors-test-indexer-store"; + +// Check if API credentials are available for integration tests +// Note: AUGMENT_API_URL must be a valid URL (not "null" or empty) +const hasApiCredentials = !!( + process.env.AUGMENT_API_TOKEN && + process.env.AUGMENT_API_URL && + process.env.AUGMENT_API_URL !== "null" && + process.env.AUGMENT_API_URL.startsWith("http") +); + +// Skip all tests if SDK failed to load +describe.skipIf(sdkLoadError !== null)("Indexer", () => { + beforeEach(async () => { + // Create test directories + await fs.mkdir(TEST_SOURCE_DIR, { recursive: true }); + await fs.mkdir(join(TEST_SOURCE_DIR, "src"), { recursive: true }); + + // Create test files + await fs.writeFile( + join(TEST_SOURCE_DIR, "src/index.ts"), + "export const hello = 'world';" + ); + await fs.writeFile( + join(TEST_SOURCE_DIR, "README.md"), + "# Test Project\nThis is a test." + ); + }); + + afterEach(async () => { + // Clean up test directories + await fs.rm(TEST_SOURCE_DIR, { recursive: true, force: true }); + await fs.rm(TEST_STORE_DIR, { recursive: true, force: true }); + }); + + describe("Indexer configuration", () => { + it("creates with default config", () => { + const indexer = new Indexer(); + expect(indexer).toBeDefined(); + }); + + it("creates with custom config", () => { + const indexer = new Indexer({ + apiKey: "test-key", + apiUrl: "https://api.test.com", + }); + expect(indexer).toBeDefined(); + }); + }); + + describe.skipIf(!hasApiCredentials)("Integration tests (require API credentials)", () => { + it("performs full index end-to-end", async () => { + const source = new FilesystemSource({ rootPath: TEST_SOURCE_DIR }); + const store = new FilesystemStore({ basePath: TEST_STORE_DIR }); + const indexer = new Indexer(); + + const result = await indexer.index(source, store, "test-project"); + + expect(result.type).toBe("full"); + expect(result.filesIndexed).toBeGreaterThan(0); + expect(result.duration).toBeGreaterThan(0); + + // Verify state was saved + const state = await store.load("test-project"); + expect(state).not.toBeNull(); + expect(state!.source.type).toBe("filesystem"); + expect(state!.contextState).toBeDefined(); + }); + + it("returns unchanged when re-indexing same content", async () => { + const source = new FilesystemSource({ rootPath: TEST_SOURCE_DIR }); + const store = new FilesystemStore({ basePath: TEST_STORE_DIR }); + const indexer = new Indexer(); + + // First index + const result1 = await indexer.index(source, store, "test-project"); + expect(result1.type).toBe("full"); + + // Second index - should still be full since fetchChanges returns null + // (incremental not supported in Phase 2) + const result2 = await indexer.index(source, store, "test-project"); + expect(result2.type).toBe("full"); + }); + + it("correctly handles empty directory", async () => { + const emptyDir = "/tmp/context-connectors-test-empty"; + await fs.mkdir(emptyDir, { recursive: true }); + + try { + const source = new FilesystemSource({ rootPath: emptyDir }); + const store = new FilesystemStore({ basePath: TEST_STORE_DIR }); + const indexer = new Indexer(); + + const result = await indexer.index(source, store, "empty-project"); + + expect(result.type).toBe("full"); + expect(result.filesIndexed).toBe(0); + } finally { + await fs.rm(emptyDir, { recursive: true, force: true }); + } + }); + }); + + describe("Unit tests (no API required)", () => { + it("FilesystemSource can be passed to index method signature", async () => { + const source = new FilesystemSource({ rootPath: TEST_SOURCE_DIR }); + const store = new FilesystemStore({ basePath: TEST_STORE_DIR }); + const indexer = new Indexer(); + + // Just verify the types work together - don't actually call index without API + expect(source.type).toBe("filesystem"); + expect(typeof indexer.index).toBe("function"); + expect(typeof store.save).toBe("function"); + }); + + it("source fetchAll returns expected files", async () => { + const source = new FilesystemSource({ rootPath: TEST_SOURCE_DIR }); + const files = await source.fetchAll(); + + expect(files.length).toBe(2); + const paths = files.map((f) => f.path); + expect(paths).toContain("src/index.ts"); + expect(paths).toContain("README.md"); + }); + }); +}); + diff --git a/context-connectors/src/core/indexer.ts b/context-connectors/src/core/indexer.ts new file mode 100644 index 0000000..9208c94 --- /dev/null +++ b/context-connectors/src/core/indexer.ts @@ -0,0 +1,240 @@ +/** + * Indexer - Main orchestrator for indexing operations. + * + * The Indexer connects Sources to Stores, handling: + * - Full indexing (first run or forced) + * - Incremental indexing (only changed files) + * - DirectContext creation and management + * + * @module core/indexer + * + * @example + * ```typescript + * import { Indexer } from "@augmentcode/context-connectors"; + * import { FilesystemSource } from "@augmentcode/context-connectors/sources"; + * import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + * + * const source = new FilesystemSource({ rootPath: "./my-project" }); + * const store = new FilesystemStore(); + * const indexer = new Indexer(); + * + * const result = await indexer.index(source, store, "my-project"); + * console.log(`Indexed ${result.filesIndexed} files`); + * ``` + */ + +import { promises as fs } from "node:fs"; +import { DirectContext } from "@augmentcode/auggie-sdk"; +import type { FileEntry, IndexResult, IndexState } from "./types.js"; +import type { FileChanges, Source } from "../sources/types.js"; +import type { IndexStore } from "../stores/types.js"; + +/** + * Configuration options for the Indexer. + */ +export interface IndexerConfig { + /** + * Augment API key for DirectContext operations. + * @default process.env.AUGMENT_API_TOKEN + */ + apiKey?: string; + /** + * Augment API URL. + * @default process.env.AUGMENT_API_URL + */ + apiUrl?: string; +} + +/** + * Main indexer class that orchestrates indexing operations. + * + * The Indexer: + * 1. Fetches files from a Source + * 2. Creates/updates a DirectContext index + * 3. Persists the result to a Store + * + * @example + * ```typescript + * const indexer = new Indexer({ + * apiKey: "your-api-key", + * apiUrl: "https://api.augmentcode.com/", + * }); + * + * // First run: full index + * const result1 = await indexer.index(source, store, "my-project"); + * // result1.type === "full" + * + * // Subsequent run: incremental if possible + * const result2 = await indexer.index(source, store, "my-project"); + * // result2.type === "incremental" or "unchanged" + * ``` + */ +export class Indexer { + private readonly apiKey?: string; + private readonly apiUrl?: string; + + /** + * Create a new Indexer instance. + * + * @param config - Optional configuration (API credentials) + */ + constructor(config: IndexerConfig = {}) { + this.apiKey = config.apiKey ?? process.env.AUGMENT_API_TOKEN; + this.apiUrl = config.apiUrl ?? process.env.AUGMENT_API_URL; + } + + /** + * Index a source and save the result to a store. + * + * This is the main entry point for indexing. It automatically: + * - Does a full index if no previous state exists + * - Attempts incremental update if previous state exists + * - Falls back to full index if incremental isn't possible + * + * @param source - The data source to index + * @param store - The store to save the index to + * @param key - Unique key/name for this index + * @returns Result containing type, files indexed/removed, and duration + * + * @example + * ```typescript + * const result = await indexer.index(source, store, "my-project"); + * if (result.type === "unchanged") { + * console.log("No changes detected"); + * } else { + * console.log(`${result.type}: ${result.filesIndexed} files`); + * } + * ``` + */ + async index(source: Source, store: IndexStore, key: string): Promise { + const startTime = Date.now(); + + // Load previous state + const previousState = await store.load(key); + + // If no previous state, do full index + if (!previousState) { + return this.fullIndex(source, store, key, startTime, "first_run"); + } + + // Try to get incremental changes + const changes = await source.fetchChanges(previousState.source); + + // If source can't provide incremental changes, do full index + if (changes === null) { + return this.fullIndex(source, store, key, startTime, "incremental_not_supported"); + } + + // Check if there are any changes + if (changes.added.length === 0 && changes.modified.length === 0 && changes.removed.length === 0) { + return { + type: "unchanged", + filesIndexed: 0, + filesRemoved: 0, + duration: Date.now() - startTime, + }; + } + + // Perform incremental update + return this.incrementalIndex(source, store, key, previousState, changes, startTime); + } + + /** + * Perform full re-index + */ + private async fullIndex( + source: Source, + store: IndexStore, + key: string, + startTime: number, + _reason: string + ): Promise { + // Create new DirectContext + const context = await DirectContext.create({ + apiKey: this.apiKey, + apiUrl: this.apiUrl, + }); + + // Fetch all files from source + const files = await source.fetchAll(); + + // Add files to index + if (files.length > 0) { + await context.addToIndex(files); + } + + // Get source metadata + const metadata = await source.getMetadata(); + + // Export context state and save + const contextState = context.export(); + const state: IndexState = { + contextState, + source: metadata, + }; + await store.save(key, state); + + return { + type: "full", + filesIndexed: files.length, + filesRemoved: 0, + duration: Date.now() - startTime, + }; + } + + /** + * Perform incremental update + */ + private async incrementalIndex( + source: Source, + store: IndexStore, + key: string, + previousState: IndexState, + changes: FileChanges, + startTime: number + ): Promise { + // Import previous context state via temp file + const tempStateFile = `/tmp/context-connectors-${Date.now()}.json`; + await fs.writeFile(tempStateFile, JSON.stringify(previousState.contextState, null, 2)); + + let context: DirectContext; + try { + context = await DirectContext.importFromFile(tempStateFile, { + apiKey: this.apiKey, + apiUrl: this.apiUrl, + }); + } finally { + await fs.unlink(tempStateFile).catch(() => {}); // Clean up temp file + } + + // Remove deleted files + if (changes.removed.length > 0) { + await context.removeFromIndex(changes.removed); + } + + // Add new and modified files + const filesToAdd: FileEntry[] = [...changes.added, ...changes.modified]; + if (filesToAdd.length > 0) { + await context.addToIndex(filesToAdd); + } + + // Get updated source metadata + const metadata = await source.getMetadata(); + + // Export and save updated state + const contextState = context.export(); + const state: IndexState = { + contextState, + source: metadata, + }; + await store.save(key, state); + + return { + type: "incremental", + filesIndexed: filesToAdd.length, + filesRemoved: changes.removed.length, + duration: Date.now() - startTime, + }; + } +} + diff --git a/context-connectors/src/core/types.ts b/context-connectors/src/core/types.ts new file mode 100644 index 0000000..f35d941 --- /dev/null +++ b/context-connectors/src/core/types.ts @@ -0,0 +1,131 @@ +/** + * Core shared types used throughout the Context Connectors system. + * + * These types define the fundamental data structures for: + * - File entries and metadata + * - Source information + * - Index state persistence + * - Indexing operation results + * + * @module core/types + */ + +import type { DirectContextState } from "@augmentcode/auggie-sdk"; + +/** + * A file with its contents, used for indexing operations. + * + * @example + * ```typescript + * const file: FileEntry = { + * path: "src/index.ts", + * contents: "export * from './core';" + * }; + * ``` + */ +export interface FileEntry { + /** Relative path to the file from the source root */ + path: string; + /** Full text contents of the file (UTF-8 encoded) */ + contents: string; +} + +/** + * File information returned by listFiles operations. + * Contains path and type (no contents) for efficiency. + * + * @example + * ```typescript + * const entries: FileInfo[] = await source.listFiles(); + * const dirs = entries.filter(e => e.type === "directory"); + * const files = entries.filter(e => e.type === "file"); + * ``` + */ +export interface FileInfo { + /** Relative path to the file or directory from the source root */ + path: string; + /** Whether this entry is a file or directory */ + type: "file" | "directory"; +} + +/** + * Metadata about a data source, stored alongside the index state. + * + * Used to: + * - Identify the source type and location + * - Track the indexed version/ref for VCS sources + * - Record when the index was last synced + * + * @example + * ```typescript + * const metadata: SourceMetadata = { + * type: "github", + * identifier: "microsoft/vscode", + * ref: "a1b2c3d4e5f6", + * syncedAt: "2024-01-15T10:30:00Z" + * }; + * ``` + */ +export interface SourceMetadata { + /** The type of data source */ + type: "github" | "gitlab" | "bitbucket" | "website" | "filesystem"; + /** + * Source-specific identifier: + * - GitHub/GitLab/BitBucket: "owner/repo" or "workspace/repo" + * - Website: base URL + * - Filesystem: absolute path + */ + identifier: string; + /** Git ref (commit SHA) for VCS sources. Used for incremental updates. */ + ref?: string; + /** ISO 8601 timestamp of when the index was last synced */ + syncedAt: string; +} + +/** + * Complete index state that gets persisted to an IndexStore. + * + * Contains: + * - The DirectContext state (embeddings, file index) + * - Source metadata for tracking the indexed version + * + * @example + * ```typescript + * const state = await store.load("my-project"); + * if (state) { + * console.log(`Last synced: ${state.source.syncedAt}`); + * } + * ``` + */ +export interface IndexState { + /** The DirectContext state from auggie-sdk (embeddings, index data) */ + contextState: DirectContextState; + /** Metadata about the source that was indexed */ + source: SourceMetadata; +} + +/** + * Result of an indexing operation. + * + * @example + * ```typescript + * const result = await indexer.index(source, store, "my-project"); + * console.log(`Indexed ${result.filesIndexed} files in ${result.duration}ms`); + * ``` + */ +export interface IndexResult { + /** + * Type of index operation performed: + * - "full": Complete re-index of all files + * - "incremental": Only changed files were updated + * - "unchanged": No changes detected, index not modified + */ + type: "full" | "incremental" | "unchanged"; + /** Number of files added or modified in the index */ + filesIndexed: number; + /** Number of files removed from the index */ + filesRemoved: number; + /** Total duration of the operation in milliseconds */ + duration: number; +} + diff --git a/context-connectors/src/core/utils.ts b/context-connectors/src/core/utils.ts new file mode 100644 index 0000000..8230444 --- /dev/null +++ b/context-connectors/src/core/utils.ts @@ -0,0 +1,22 @@ +/** + * Shared utility functions + */ + +/** + * Sanitize a key for use in filenames/paths. + * Replaces unsafe characters with underscores. + */ +export function sanitizeKey(key: string): string { + return key + .replace(/[^a-zA-Z0-9_-]/g, "_") + .replace(/__+/g, "_") + .replace(/^_+|_+$/g, ""); +} + +/** + * Get current timestamp in ISO format + */ +export function isoTimestamp(): string { + return new Date().toISOString(); +} + diff --git a/context-connectors/src/index.ts b/context-connectors/src/index.ts new file mode 100644 index 0000000..f4e2497 --- /dev/null +++ b/context-connectors/src/index.ts @@ -0,0 +1,24 @@ +/** + * Context Connectors - Main package entry point + * + * Modular system for indexing any data source and making it + * searchable via Augment's context engine. + */ + +// Core types and utilities +export * from "./core/index.js"; + +// Sources +export * from "./sources/index.js"; +export { FilesystemSource } from "./sources/filesystem.js"; +export type { FilesystemSourceConfig } from "./sources/filesystem.js"; + +// Stores +export * from "./stores/index.js"; +export { FilesystemStore } from "./stores/filesystem.js"; +export type { FilesystemStoreConfig } from "./stores/filesystem.js"; + +// Indexer +export { Indexer } from "./core/indexer.js"; +export type { IndexerConfig } from "./core/indexer.js"; + diff --git a/context-connectors/src/integrations/github-webhook-express.ts b/context-connectors/src/integrations/github-webhook-express.ts new file mode 100644 index 0000000..d040fa0 --- /dev/null +++ b/context-connectors/src/integrations/github-webhook-express.ts @@ -0,0 +1,59 @@ +import type { Request, Response, NextFunction } from "express"; +import { + createGitHubWebhookHandler, + verifyWebhookSignature, + type GitHubWebhookConfig, + type PushEvent, +} from "./github-webhook.js"; + +export function createExpressHandler(config: GitHubWebhookConfig) { + const handler = createGitHubWebhookHandler(config); + + return async function middleware( + req: Request, + res: Response, + next: NextFunction + ) { + try { + const signature = req.headers["x-hub-signature-256"] as string; + const eventType = req.headers["x-github-event"] as string; + + if (!signature || !eventType) { + res.status(400).json({ error: "Missing required headers" }); + return; + } + + // Requires raw body - use express.raw() middleware + // Handle Buffer (from express.raw()), string, or object + let body: string; + if (Buffer.isBuffer(req.body)) { + body = req.body.toString("utf-8"); + } else if (typeof req.body === "string") { + body = req.body; + } else { + body = JSON.stringify(req.body); + } + + const valid = await verifyWebhookSignature(body, signature, config.secret); + if (!valid) { + res.status(401).json({ error: "Invalid signature" }); + return; + } + + // Parse payload from the body string (handles Buffer, string, and object) + const payload = ( + Buffer.isBuffer(req.body) || typeof req.body === "string" + ? JSON.parse(body) + : req.body + ) as PushEvent; + + const result = await handler(eventType, payload); + + const status = result.status === "error" ? 500 : 200; + res.status(status).json(result); + } catch (error) { + next(error); + } + }; +} + diff --git a/context-connectors/src/integrations/github-webhook-vercel.ts b/context-connectors/src/integrations/github-webhook-vercel.ts new file mode 100644 index 0000000..88fd094 --- /dev/null +++ b/context-connectors/src/integrations/github-webhook-vercel.ts @@ -0,0 +1,44 @@ +import { + createGitHubWebhookHandler, + verifyWebhookSignature, + type GitHubWebhookConfig, + type PushEvent, +} from "./github-webhook.js"; + +type VercelRequest = { + headers: { get(name: string): string | null }; + text(): Promise; + json(): Promise; +}; + +type VercelResponse = Response; + +export function createVercelHandler(config: GitHubWebhookConfig) { + const handler = createGitHubWebhookHandler(config); + + return async function POST(request: VercelRequest): Promise { + const signature = request.headers.get("x-hub-signature-256"); + const eventType = request.headers.get("x-github-event"); + + if (!signature || !eventType) { + return Response.json( + { error: "Missing required headers" }, + { status: 400 } + ); + } + + const body = await request.text(); + + const valid = await verifyWebhookSignature(body, signature, config.secret); + if (!valid) { + return Response.json({ error: "Invalid signature" }, { status: 401 }); + } + + const payload = JSON.parse(body) as PushEvent; + const result = await handler(eventType, payload); + + const status = result.status === "error" ? 500 : 200; + return Response.json(result, { status }); + }; +} + diff --git a/context-connectors/src/integrations/github-webhook.test.ts b/context-connectors/src/integrations/github-webhook.test.ts new file mode 100644 index 0000000..c2ff00c --- /dev/null +++ b/context-connectors/src/integrations/github-webhook.test.ts @@ -0,0 +1,141 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import crypto from "crypto"; +import type { IndexStore } from "../stores/types.js"; + +// Mock the core/indexer module before importing github-webhook +vi.mock("../core/indexer.js", () => ({ + Indexer: vi.fn().mockImplementation(() => ({ + index: vi.fn().mockResolvedValue({ + type: "full", + filesIndexed: 10, + filesRemoved: 0, + duration: 100, + }), + })), +})); + +// Mock the sources/github module +vi.mock("../sources/github.js", () => ({ + GitHubSource: vi.fn().mockImplementation(() => ({})), +})); + +// Now import the module under test +import { + createGitHubWebhookHandler, + verifyWebhookSignature, + type PushEvent, +} from "./github-webhook.js"; + +describe("verifyWebhookSignature", () => { + it("verifies valid signature", async () => { + const payload = '{"test": true}'; + const secret = "test-secret"; + // Compute expected signature + const expectedSignature = + "sha256=" + crypto.createHmac("sha256", secret).update(payload).digest("hex"); + + const valid = await verifyWebhookSignature(payload, expectedSignature, secret); + expect(valid).toBe(true); + }); + + it("rejects invalid signature", async () => { + const valid = await verifyWebhookSignature( + "payload", + "sha256=invalid", + "secret" + ); + expect(valid).toBe(false); + }); +}); + +describe("createGitHubWebhookHandler", () => { + let mockStore: IndexStore; + + beforeEach(() => { + mockStore = { + save: vi.fn().mockResolvedValue(undefined), + load: vi.fn().mockResolvedValue(null), + delete: vi.fn().mockResolvedValue(undefined), + list: vi.fn().mockResolvedValue([]), + }; + }); + + const pushEvent: PushEvent = { + ref: "refs/heads/main", + before: "abc123", + after: "def456", + deleted: false, + forced: false, + repository: { + full_name: "owner/repo", + owner: { login: "owner" }, + name: "repo", + default_branch: "main", + }, + pusher: { name: "user" }, + }; + + it("skips non-push events", async () => { + const handler = createGitHubWebhookHandler({ store: mockStore, secret: "s" }); + const result = await handler("pull_request", pushEvent); + expect(result.status).toBe("skipped"); + }); + + it("skips deleted branches", async () => { + const handler = createGitHubWebhookHandler({ store: mockStore, secret: "s" }); + const result = await handler("push", { ...pushEvent, deleted: true }); + expect(result.status).toBe("skipped"); + }); + + it("deletes index when deleteOnBranchDelete is true", async () => { + const handler = createGitHubWebhookHandler({ + store: mockStore, + secret: "s", + deleteOnBranchDelete: true, + }); + const result = await handler("push", { ...pushEvent, deleted: true }); + expect(result.status).toBe("deleted"); + expect(mockStore.delete).toHaveBeenCalled(); + }); + + it("uses custom getKey function", async () => { + const getKey = vi.fn((repo: string) => `custom-${repo}`); + const handler = createGitHubWebhookHandler({ + store: mockStore, + secret: "s", + getKey, + shouldIndex: () => false, // Skip indexing to just test getKey + }); + await handler("push", pushEvent); + expect(getKey).toHaveBeenCalledWith("owner/repo", "refs/heads/main"); + }); + + it("respects shouldIndex filter", async () => { + const handler = createGitHubWebhookHandler({ + store: mockStore, + secret: "s", + shouldIndex: () => false, + }); + const result = await handler("push", pushEvent); + expect(result.status).toBe("skipped"); + expect(result.message).toContain("shouldIndex"); + }); + + it("skips tag pushes by default", async () => { + const handler = createGitHubWebhookHandler({ store: mockStore, secret: "s" }); + const tagEvent = { ...pushEvent, ref: "refs/tags/v1.0.0" }; + const result = await handler("push", tagEvent); + expect(result.status).toBe("skipped"); + }); + + it("generates correct default key", async () => { + const handler = createGitHubWebhookHandler({ + store: mockStore, + secret: "s", + shouldIndex: () => false, // Skip indexing to check key + }); + const result = await handler("push", pushEvent); + expect(result.key).toBe("owner/repo/main"); + }); +}); + diff --git a/context-connectors/src/integrations/github-webhook.ts b/context-connectors/src/integrations/github-webhook.ts new file mode 100644 index 0000000..bbf2114 --- /dev/null +++ b/context-connectors/src/integrations/github-webhook.ts @@ -0,0 +1,147 @@ +import { Indexer } from "../core/indexer.js"; +import { GitHubSource } from "../sources/github.js"; +import type { IndexStore } from "../stores/types.js"; +import type { IndexResult } from "../core/types.js"; + +export interface PushEvent { + ref: string; + before: string; + after: string; + repository: { + full_name: string; + owner: { login: string }; + name: string; + default_branch: string; + }; + pusher: { name: string }; + deleted: boolean; + forced: boolean; +} + +export interface GitHubWebhookConfig { + store: IndexStore; + secret: string; + + /** Generate index key from repo/ref. Default: "owner/repo/branch" */ + getKey?: (repo: string, ref: string) => string; + + /** Filter which pushes trigger indexing. Default: all non-delete pushes */ + shouldIndex?: (event: PushEvent) => boolean; + + /** Called after successful indexing */ + onIndexed?: (key: string, result: IndexResult) => void | Promise; + + /** Called on errors */ + onError?: (error: Error, event: PushEvent) => void | Promise; + + /** Delete index when branch is deleted. Default: false */ + deleteOnBranchDelete?: boolean; +} + +export interface WebhookResult { + status: "indexed" | "deleted" | "skipped" | "error"; + key?: string; + message: string; + filesIndexed?: number; +} + +/** + * Verify GitHub webhook signature + */ +export async function verifyWebhookSignature( + payload: string, + signature: string, + secret: string +): Promise { + const crypto = await import("crypto"); + const expected = + "sha256=" + + crypto.createHmac("sha256", secret).update(payload).digest("hex"); + + const sigBuffer = Buffer.from(signature); + const expectedBuffer = Buffer.from(expected); + + // timingSafeEqual requires buffers of the same length + if (sigBuffer.length !== expectedBuffer.length) { + return false; + } + + return crypto.timingSafeEqual(sigBuffer, expectedBuffer); +} + +/** + * Create a GitHub webhook handler + */ +export function createGitHubWebhookHandler(config: GitHubWebhookConfig) { + const defaultGetKey = (repo: string, ref: string) => { + const branch = ref.replace("refs/heads/", "").replace("refs/tags/", ""); + return `${repo}/${branch}`; + }; + + const defaultShouldIndex = (event: PushEvent) => { + // Don't index deletions + if (event.deleted) return false; + // Only index branch pushes (not tags by default) + if (!event.ref.startsWith("refs/heads/")) return false; + return true; + }; + + return async function handleWebhook( + eventType: string, + payload: PushEvent + ): Promise { + // Only handle push events + if (eventType !== "push") { + return { + status: "skipped", + message: `Event type "${eventType}" not handled`, + }; + } + + const getKey = config.getKey ?? defaultGetKey; + const shouldIndex = config.shouldIndex ?? defaultShouldIndex; + const key = getKey(payload.repository.full_name, payload.ref); + + // Handle branch deletion + if (payload.deleted) { + if (config.deleteOnBranchDelete) { + await config.store.delete(key); + return { status: "deleted", key, message: `Deleted index for ${key}` }; + } + return { status: "skipped", key, message: "Branch deleted, index preserved" }; + } + + // Check if we should index + if (!shouldIndex(payload)) { + return { status: "skipped", key, message: "Filtered by shouldIndex" }; + } + + try { + const source = new GitHubSource({ + owner: payload.repository.owner.login, + repo: payload.repository.name, + ref: payload.after, + }); + + const indexer = new Indexer(); + const result = await indexer.index(source, config.store, key); + + await config.onIndexed?.(key, result); + + return { + status: "indexed", + key, + message: `Indexed ${result.filesIndexed} files`, + filesIndexed: result.filesIndexed, + }; + } catch (error) { + await config.onError?.(error as Error, payload); + return { + status: "error", + key, + message: (error as Error).message, + }; + } + }; +} + diff --git a/context-connectors/src/integrations/index.ts b/context-connectors/src/integrations/index.ts new file mode 100644 index 0000000..78f4001 --- /dev/null +++ b/context-connectors/src/integrations/index.ts @@ -0,0 +1,11 @@ +export { + createGitHubWebhookHandler, + verifyWebhookSignature, + type GitHubWebhookConfig, + type PushEvent, + type WebhookResult, +} from "./github-webhook.js"; + +export { createVercelHandler } from "./github-webhook-vercel.js"; +export { createExpressHandler } from "./github-webhook-express.js"; + diff --git a/context-connectors/src/sources/bitbucket.test.ts b/context-connectors/src/sources/bitbucket.test.ts new file mode 100644 index 0000000..e218cd9 --- /dev/null +++ b/context-connectors/src/sources/bitbucket.test.ts @@ -0,0 +1,180 @@ +/** + * Tests for BitBucketSource + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { BitBucketSource } from "./bitbucket.js"; + +describe("BitBucketSource", () => { + const originalEnv = process.env.BITBUCKET_TOKEN; + + beforeEach(() => { + process.env.BITBUCKET_TOKEN = "test-token"; + }); + + afterEach(() => { + if (originalEnv) { + process.env.BITBUCKET_TOKEN = originalEnv; + } else { + delete process.env.BITBUCKET_TOKEN; + } + vi.restoreAllMocks(); + }); + + describe("constructor", () => { + it("uses provided token", () => { + expect(() => { + new BitBucketSource({ + token: "custom-token", + workspace: "myworkspace", + repo: "myrepo", + }); + }).not.toThrow(); + }); + + it("uses BITBUCKET_TOKEN from env", () => { + expect(() => { + new BitBucketSource({ + workspace: "myworkspace", + repo: "myrepo", + }); + }).not.toThrow(); + }); + + it("throws if no token available", () => { + delete process.env.BITBUCKET_TOKEN; + expect(() => { + new BitBucketSource({ + workspace: "myworkspace", + repo: "myrepo", + }); + }).toThrow(/BitBucket token required/); + }); + + it("uses HEAD as default ref", () => { + const source = new BitBucketSource({ + workspace: "myworkspace", + repo: "myrepo", + }); + // @ts-expect-error - accessing private property for testing + expect(source.ref).toBe("HEAD"); + }); + + it("accepts custom ref", () => { + const source = new BitBucketSource({ + workspace: "myworkspace", + repo: "myrepo", + ref: "develop", + }); + // @ts-expect-error - accessing private property for testing + expect(source.ref).toBe("develop"); + }); + + it("uses default BitBucket Cloud URL", () => { + const source = new BitBucketSource({ + workspace: "myworkspace", + repo: "myrepo", + }); + // @ts-expect-error - accessing private property for testing + expect(source.baseUrl).toBe("https://api.bitbucket.org/2.0"); + }); + + it("accepts custom base URL for Server/Data Center", () => { + const source = new BitBucketSource({ + workspace: "myworkspace", + repo: "myrepo", + baseUrl: "https://bitbucket.mycompany.com/rest/api/1.0", + }); + // @ts-expect-error - accessing private property for testing + expect(source.baseUrl).toBe("https://bitbucket.mycompany.com/rest/api/1.0"); + }); + + it("strips trailing slash from base URL", () => { + const source = new BitBucketSource({ + workspace: "myworkspace", + repo: "myrepo", + baseUrl: "https://bitbucket.mycompany.com/rest/api/1.0/", + }); + // @ts-expect-error - accessing private property for testing + expect(source.baseUrl).toBe("https://bitbucket.mycompany.com/rest/api/1.0"); + }); + }); + + describe("type", () => { + it("returns 'bitbucket'", () => { + const source = new BitBucketSource({ + workspace: "myworkspace", + repo: "myrepo", + }); + expect(source.type).toBe("bitbucket"); + }); + }); + + // Integration tests - require BITBUCKET_TOKEN, BITBUCKET_WORKSPACE, and BITBUCKET_REPO env vars + // BitBucket Cloud now requires authentication for all API access, so we can't use a hardcoded public repo + const integrationWorkspace = process.env.BITBUCKET_WORKSPACE; + const integrationRepo = process.env.BITBUCKET_REPO; + const runIntegration = !!originalEnv && !!integrationWorkspace && !!integrationRepo; + + describe.skipIf(!runIntegration)("integration", () => { + it("indexes a BitBucket repository", async () => { + const source = new BitBucketSource({ + token: originalEnv, + workspace: integrationWorkspace!, + repo: integrationRepo!, + }); + + const files = await source.fetchAll(); + expect(files.length).toBeGreaterThan(0); + }); + + it("lists files from a repository", async () => { + const source = new BitBucketSource({ + token: originalEnv, + workspace: integrationWorkspace!, + repo: integrationRepo!, + }); + + const files = await source.listFiles(); + expect(files.length).toBeGreaterThan(0); + expect(files[0]).toHaveProperty("path"); + }); + + it("reads a single file from a repository", async () => { + const source = new BitBucketSource({ + token: originalEnv, + workspace: integrationWorkspace!, + repo: integrationRepo!, + }); + + const content = await source.readFile("README.md"); + expect(content).not.toBeNull(); + }); + + it("returns null for missing file", async () => { + const source = new BitBucketSource({ + token: originalEnv, + workspace: integrationWorkspace!, + repo: integrationRepo!, + }); + + const content = await source.readFile("nonexistent-file-12345.txt"); + expect(content).toBeNull(); + }); + + it("gets correct metadata", async () => { + const source = new BitBucketSource({ + token: originalEnv, + workspace: integrationWorkspace!, + repo: integrationRepo!, + }); + + const metadata = await source.getMetadata(); + expect(metadata.type).toBe("bitbucket"); + expect(metadata.identifier).toBe(`${integrationWorkspace}/${integrationRepo}`); + expect(metadata.ref).toBeDefined(); + expect(metadata.syncedAt).toBeDefined(); + }); + }); +}); + diff --git a/context-connectors/src/sources/bitbucket.ts b/context-connectors/src/sources/bitbucket.ts new file mode 100644 index 0000000..6a67e86 --- /dev/null +++ b/context-connectors/src/sources/bitbucket.ts @@ -0,0 +1,516 @@ +/** + * BitBucket Source - Fetches files from BitBucket repositories + * + * Features: + * - Full indexing via archive download + * - Incremental updates via Diff API + * - Force push detection (triggers full re-index) + * - Respects .gitignore and .augmentignore + * - Supports both BitBucket Cloud and BitBucket Server/Data Center + * + * @module sources/bitbucket + */ + +import ignoreFactory, { type Ignore } from "ignore"; +import { shouldFilterFile } from "../core/file-filter.js"; +import { isoTimestamp } from "../core/utils.js"; +import type { FileEntry, FileInfo, SourceMetadata } from "../core/types.js"; +import type { FileChanges, Source } from "./types.js"; + +// With NodeNext module resolution, we need to access the default export properly +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const ignore = (ignoreFactory as any).default ?? ignoreFactory; + +/** Configuration for BitBucketSource */ +export interface BitBucketSourceConfig { + /** BitBucket access token. Defaults to process.env.BITBUCKET_TOKEN */ + token?: string; + /** BitBucket base URL. Defaults to https://api.bitbucket.org/2.0 for Cloud */ + baseUrl?: string; + /** Workspace slug (for BitBucket Cloud) */ + workspace: string; + /** Repository slug */ + repo: string; + /** Branch/tag/commit ref. Defaults to "HEAD" */ + ref?: string; +} + +export class BitBucketSource implements Source { + readonly type = "bitbucket" as const; + private readonly baseUrl: string; + private readonly workspace: string; + private readonly repo: string; + private readonly ref: string; + private readonly token: string; + private resolvedRef: string | null = null; + + constructor(config: BitBucketSourceConfig) { + this.baseUrl = (config.baseUrl ?? "https://api.bitbucket.org/2.0").replace(/\/$/, ""); + this.workspace = config.workspace; + this.repo = config.repo; + this.ref = config.ref ?? "HEAD"; + this.token = config.token ?? process.env.BITBUCKET_TOKEN ?? ""; + + if (!this.token) { + throw new Error("BitBucket token required. Set BITBUCKET_TOKEN environment variable or pass token in config."); + } + } + + /** + * Make an authenticated API request to BitBucket + */ + private async apiRequest(path: string, options: RequestInit = {}): Promise { + const url = `${this.baseUrl}${path}`; + const response = await fetch(url, { + ...options, + headers: { + Authorization: `Bearer ${this.token}`, + Accept: "application/json", + ...options.headers, + }, + }); + + if (!response.ok) { + throw new Error(`BitBucket API error: ${response.status} ${response.statusText} for ${path}`); + } + + return response.json() as T; + } + + /** + * Resolve ref (branch/tag/HEAD) to commit SHA + */ + private async resolveRefToSha(): Promise { + if (this.resolvedRef) { + return this.resolvedRef; + } + + let refToResolve = this.ref; + + // If ref is HEAD, get the default branch from repository info + if (refToResolve === "HEAD") { + const repoInfo = await this.apiRequest<{ mainbranch?: { name: string } }>( + `/repositories/${this.workspace}/${this.repo}` + ); + refToResolve = repoInfo.mainbranch?.name ?? "main"; + } + + try { + // Get the commit for the ref - try as branch first + const data = await this.apiRequest<{ target?: { hash: string }; hash?: string }>( + `/repositories/${this.workspace}/${this.repo}/refs/branches/${encodeURIComponent(refToResolve)}` + ); + // Branch refs have target.hash, tags might be different + this.resolvedRef = data.target?.hash ?? data.hash ?? ""; + if (!this.resolvedRef) { + // Try as a commit SHA directly + const commitData = await this.apiRequest<{ hash: string }>( + `/repositories/${this.workspace}/${this.repo}/commit/${encodeURIComponent(refToResolve)}` + ); + this.resolvedRef = commitData.hash; + } + return this.resolvedRef; + } catch (error) { + throw new Error( + `Failed to resolve ref "${refToResolve}" for ${this.workspace}/${this.repo}: ${error}` + ); + } + } + + /** + * Get raw file contents at a specific ref (used for incremental updates) + */ + private async readFileRaw(path: string, ref: string): Promise { + try { + const url = `${this.baseUrl}/repositories/${this.workspace}/${this.repo}/src/${encodeURIComponent(ref)}/${encodeURIComponent(path)}`; + const response = await fetch(url, { + headers: { Authorization: `Bearer ${this.token}` }, + }); + + if (!response.ok) { + return null; + } + + return response.text(); + } catch { + return null; + } + } + + /** + * Fetch all files by cloning the repository. + * This is more efficient than using the API for larger repos and avoids rate limits. + */ + private async fetchAllFiles(ref: string): Promise> { + console.log(`Cloning ${this.workspace}/${this.repo}@${ref}...`); + + // Create a temporary directory for the clone + const tempDir = await this.cloneRepository(ref); + + try { + // Load ignore patterns from the cloned repo + const { augmentignore, gitignore } = await this.loadIgnorePatternsFromDir(tempDir); + + const files = new Map(); + + // Walk the directory and collect files + await this.walkDirectory(tempDir, tempDir, augmentignore, gitignore, files); + + console.log(`Collected ${files.size} files from clone`); + return files; + } finally { + // Clean up the temporary directory + await this.cleanupTempDir(tempDir); + } + } + + /** + * Clone the repository to a temporary directory + */ + private async cloneRepository(ref: string): Promise { + const { execSync } = await import("node:child_process"); + const { mkdtemp } = await import("node:fs/promises"); + const { tmpdir } = await import("node:os"); + const { join } = await import("node:path"); + + // Create temp directory + const tempDir = await mkdtemp(join(tmpdir(), `bitbucket-${this.workspace}-${this.repo}-`)); + + // Construct clone URL with token auth + // Format: https://x-token-auth:{token}@bitbucket.org/{workspace}/{repo}.git + const cloneUrl = `https://x-token-auth:${this.token}@bitbucket.org/${this.workspace}/${this.repo}.git`; + + try { + // Clone with depth 1 for efficiency, then checkout the specific ref + execSync(`git clone --depth 1 --branch ${ref} "${cloneUrl}" "${tempDir}"`, { + stdio: "pipe", + timeout: 300000, // 5 minute timeout + }); + } catch { + // If branch clone fails, try cloning default branch and checking out the ref + try { + execSync(`git clone --depth 1 "${cloneUrl}" "${tempDir}"`, { + stdio: "pipe", + timeout: 300000, + }); + // Fetch the specific ref + execSync(`git fetch origin ${ref}`, { + cwd: tempDir, + stdio: "pipe", + timeout: 300000, + }); + execSync(`git checkout ${ref}`, { + cwd: tempDir, + stdio: "pipe", + }); + } catch (error) { + // Clean up on failure + await this.cleanupTempDir(tempDir); + throw new Error(`Failed to clone repository: ${error instanceof Error ? error.message : String(error)}`); + } + } + + return tempDir; + } + + /** + * Load ignore patterns from the cloned directory + */ + private async loadIgnorePatternsFromDir(dir: string): Promise<{ augmentignore: Ignore; gitignore: Ignore }> { + const { readFile } = await import("node:fs/promises"); + const { join } = await import("node:path"); + + const augmentignore = ignore(); + const gitignore = ignore(); + + // Load .gitignore if exists + try { + const gitignoreContent = await readFile(join(dir, ".gitignore"), "utf-8"); + gitignore.add(gitignoreContent); + } catch { + // .gitignore doesn't exist + } + + // Load .augmentignore if exists + try { + const augmentignoreContent = await readFile(join(dir, ".augmentignore"), "utf-8"); + augmentignore.add(augmentignoreContent); + } catch { + // .augmentignore doesn't exist + } + + return { augmentignore, gitignore }; + } + + /** Default directories to always skip */ + private static readonly SKIP_DIRS = new Set([".git", "node_modules", "__pycache__", ".venv", "venv"]); + + /** + * Recursively walk directory and collect files + */ + private async walkDirectory( + rootDir: string, + currentDir: string, + augmentignore: Ignore, + gitignore: Ignore, + files: Map + ): Promise { + const { readdir, readFile } = await import("node:fs/promises"); + const { join, relative } = await import("node:path"); + + const entries = await readdir(currentDir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = join(currentDir, entry.name); + const relativePath = relative(rootDir, fullPath); + + // Skip default ignored directories + if (entry.isDirectory() && BitBucketSource.SKIP_DIRS.has(entry.name)) { + continue; + } + + if (entry.isDirectory()) { + // Check directory against ignore patterns before descending + const dirPath = relativePath + "/"; + if (augmentignore.ignores(dirPath) || gitignore.ignores(dirPath)) { + continue; + } + await this.walkDirectory(rootDir, fullPath, augmentignore, gitignore, files); + } else if (entry.isFile()) { + // Apply ignore rules in priority order: + // 1. .augmentignore (highest priority) + if (augmentignore.ignores(relativePath)) { + continue; + } + + // 2. Read file content for filtering + let content: Buffer; + try { + content = await readFile(fullPath); + } catch { + continue; // Skip files we can't read + } + + // 3. Apply built-in filters (binary, large files, secrets) + const filterResult = shouldFilterFile({ + path: relativePath, + content, + }); + + if (filterResult.filtered) { + continue; + } + + // 4. .gitignore (lowest priority) + if (gitignore.ignores(relativePath)) { + continue; + } + + // File passed all filters + files.set(relativePath, content.toString("utf-8")); + } + } + } + + /** + * Clean up temporary directory + */ + private async cleanupTempDir(dir: string): Promise { + const { rm } = await import("node:fs/promises"); + try { + await rm(dir, { recursive: true, force: true }); + } catch { + // Ignore cleanup errors + } + } + + /** + * Check if the push was a force push (base commit not reachable from head) + */ + private async isForcePush(base: string, head: string): Promise { + try { + // BitBucket diff API - if base is not an ancestor, it's a force push + interface DiffStatResponse { + values: Array<{ status: string }>; + } + + const data = await this.apiRequest( + `/repositories/${this.workspace}/${this.repo}/diffstat/${encodeURIComponent(base)}..${encodeURIComponent(head)}` + ); + + // If we get here without error, the commits are comparable + // Check if base is behind head by trying reverse + if (data.values.length === 0) { + // No changes between commits - check if they're the same + if (base !== head) { + return true; // Different commits but no forward diff = force push + } + } + + return false; + } catch { + // If comparison fails, it's likely a force push + return true; + } + } + + /** + * Check if ignore files changed between commits + */ + private async ignoreFilesChanged(base: string, head: string): Promise { + interface DiffStatResponse { + values: Array<{ new?: { path: string }; old?: { path: string } }>; + } + + const data = await this.apiRequest( + `/repositories/${this.workspace}/${this.repo}/diffstat/${encodeURIComponent(base)}..${encodeURIComponent(head)}` + ); + + const ignoreFiles = [".gitignore", ".augmentignore"]; + return (data.values || []).some((diff) => + ignoreFiles.includes(diff.new?.path ?? "") || ignoreFiles.includes(diff.old?.path ?? "") + ); + } + + async fetchAll(): Promise { + const ref = await this.resolveRefToSha(); + const filesMap = await this.fetchAllFiles(ref); + + const files: FileEntry[] = []; + for (const [path, contents] of filesMap) { + files.push({ path, contents }); + } + + return files; + } + + async fetchChanges(previous: SourceMetadata): Promise { + // Need previous ref to compute changes + if (!previous.ref) { + return null; + } + + const currentRef = await this.resolveRefToSha(); + + // Same commit, no changes + if (previous.ref === currentRef) { + return { added: [], modified: [], removed: [] }; + } + + // Check for force push + if (await this.isForcePush(previous.ref, currentRef)) { + console.log("Force push detected, triggering full re-index"); + return null; + } + + // Check if ignore files changed + if (await this.ignoreFilesChanged(previous.ref, currentRef)) { + console.log("Ignore files changed, triggering full re-index"); + return null; + } + + // Get changed files via diffstat API + interface DiffStatResponse { + values: Array<{ + status: string; + new?: { path: string }; + old?: { path: string }; + }>; + } + + const data = await this.apiRequest( + `/repositories/${this.workspace}/${this.repo}/diffstat/${encodeURIComponent(previous.ref)}..${encodeURIComponent(currentRef)}` + ); + + const changedFiles = data.values || []; + + // If too many changes, do full reindex + if (changedFiles.length > 100) { + console.log(`Too many changes (${changedFiles.length}), triggering full re-index`); + return null; + } + + const added: FileEntry[] = []; + const modified: FileEntry[] = []; + const removed: string[] = []; + + for (const file of changedFiles) { + if (file.status === "removed") { + if (file.old?.path) { + removed.push(file.old.path); + } + } else { + const filePath = file.new?.path; + if (filePath) { + // Download file contents + const contents = await this.readFileRaw(filePath, currentRef); + if (contents !== null) { + const entry = { path: filePath, contents }; + if (file.status === "added") { + added.push(entry); + } else { + modified.push(entry); + } + } + + // Handle rename as remove + add + if (file.status === "renamed" && file.old?.path && file.old.path !== filePath) { + removed.push(file.old.path); + } + } + } + } + + return { added, modified, removed }; + } + + async getMetadata(): Promise { + const ref = await this.resolveRefToSha(); + return { + type: "bitbucket", + identifier: `${this.workspace}/${this.repo}`, + ref, + syncedAt: isoTimestamp(), + }; + } + + async listFiles(directory: string = ""): Promise { + const sha = await this.resolveRefToSha(); + + // Use src endpoint for specific directory (non-recursive) + interface SrcResponse { + values: Array<{ path: string; type: string }>; + next?: string; + } + + const results: FileInfo[] = []; + let url = `/repositories/${this.workspace}/${this.repo}/src/${encodeURIComponent(sha)}/${directory}?pagelen=100`; + + try { + // Paginate through all items in this directory (but don't recurse into subdirectories) + while (url) { + const data = await this.apiRequest(url); + + for (const item of data.values) { + results.push({ + path: item.path, + type: item.type === "commit_directory" ? "directory" as const : "file" as const, + }); + } + + // Get next page URL (relative path) + url = data.next ? data.next.replace(this.baseUrl, "") : ""; + } + + return results; + } catch { + // Directory doesn't exist + return []; + } + } + + async readFile(path: string): Promise { + const ref = await this.resolveRefToSha(); + return this.readFileRaw(path, ref); + } +} diff --git a/context-connectors/src/sources/filesystem.test.ts b/context-connectors/src/sources/filesystem.test.ts new file mode 100644 index 0000000..e16fa19 --- /dev/null +++ b/context-connectors/src/sources/filesystem.test.ts @@ -0,0 +1,215 @@ +/** + * Tests for FilesystemSource + */ + +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { promises as fs } from "node:fs"; +import { join } from "node:path"; +import { FilesystemSource } from "./filesystem.js"; + +const TEST_DIR = "/tmp/context-connectors-test-fs-source"; + +describe("FilesystemSource", () => { + beforeEach(async () => { + // Create test directory structure + await fs.mkdir(TEST_DIR, { recursive: true }); + await fs.mkdir(join(TEST_DIR, "src"), { recursive: true }); + await fs.mkdir(join(TEST_DIR, "node_modules/package"), { recursive: true }); + await fs.mkdir(join(TEST_DIR, ".git"), { recursive: true }); + + // Create test files + await fs.writeFile(join(TEST_DIR, "src/index.ts"), "export const foo = 1;"); + await fs.writeFile(join(TEST_DIR, "src/utils.ts"), "export function bar() {}"); + await fs.writeFile(join(TEST_DIR, "README.md"), "# Test Project"); + await fs.writeFile(join(TEST_DIR, "node_modules/package/index.js"), "module.exports = {}"); + await fs.writeFile(join(TEST_DIR, ".git/config"), "[core]"); + }); + + afterEach(async () => { + // Clean up test directory + await fs.rm(TEST_DIR, { recursive: true, force: true }); + }); + + describe("fetchAll", () => { + it("returns files from directory", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const files = await source.fetchAll(); + + expect(files.length).toBeGreaterThan(0); + const paths = files.map((f) => f.path); + expect(paths).toContain("src/index.ts"); + expect(paths).toContain("src/utils.ts"); + expect(paths).toContain("README.md"); + }); + + it("skips node_modules directory", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const files = await source.fetchAll(); + + const paths = files.map((f) => f.path); + expect(paths.some((p) => p.includes("node_modules"))).toBe(false); + }); + + it("skips .git directory", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const files = await source.fetchAll(); + + const paths = files.map((f) => f.path); + expect(paths.some((p) => p.includes(".git"))).toBe(false); + }); + + it("respects .gitignore", async () => { + // Create .gitignore + await fs.writeFile(join(TEST_DIR, ".gitignore"), "*.log\n"); + await fs.writeFile(join(TEST_DIR, "debug.log"), "debug output"); + + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const files = await source.fetchAll(); + + const paths = files.map((f) => f.path); + expect(paths).not.toContain("debug.log"); + }); + + it("filters binary files", async () => { + // Create a binary file + await fs.writeFile(join(TEST_DIR, "binary.dat"), Buffer.from([0x80, 0x81, 0x82, 0xff])); + + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const files = await source.fetchAll(); + + const paths = files.map((f) => f.path); + expect(paths).not.toContain("binary.dat"); + }); + + it("respects custom ignore patterns", async () => { + await fs.writeFile(join(TEST_DIR, "temp.txt"), "temp content"); + + const source = new FilesystemSource({ + rootPath: TEST_DIR, + ignorePatterns: ["temp.txt"], + }); + const files = await source.fetchAll(); + + const paths = files.map((f) => f.path); + expect(paths).not.toContain("temp.txt"); + }); + }); + + describe("readFile", () => { + it("returns file contents", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const contents = await source.readFile("src/index.ts"); + + expect(contents).toBe("export const foo = 1;"); + }); + + it("returns null for missing files", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const contents = await source.readFile("nonexistent.ts"); + + expect(contents).toBeNull(); + }); + + it("prevents path traversal", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const contents = await source.readFile("../../../etc/passwd"); + + expect(contents).toBeNull(); + }); + }); + + describe("getMetadata", () => { + it("returns correct type and identifier", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const metadata = await source.getMetadata(); + + expect(metadata.type).toBe("filesystem"); + expect(metadata.identifier).toBe(TEST_DIR); + expect(metadata.syncedAt).toBeDefined(); + }); + }); + + describe("fetchChanges", () => { + it("returns null (not supported in Phase 2)", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const changes = await source.fetchChanges({ + type: "filesystem", + identifier: TEST_DIR, + syncedAt: new Date().toISOString(), + }); + + expect(changes).toBeNull(); + }); + }); + + describe("listFiles", () => { + it("returns list of file and directory entries", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const entries = await source.listFiles(); + + expect(entries).toBeInstanceOf(Array); + expect(entries.length).toBeGreaterThan(0); + expect(entries[0]).toHaveProperty("path"); + expect(entries[0]).toHaveProperty("type"); + expect(["file", "directory"]).toContain(entries[0].type); + }); + + it("returns only immediate children of root (non-recursive)", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const entries = await source.listFiles(); + + // Should include src directory but NOT src/index.ts + const paths = entries.map((e) => e.path); + expect(paths).toContain("src"); + expect(paths).not.toContain("src/index.ts"); + expect(paths).not.toContain("src/utils.ts"); + }); + + it("correctly identifies files and directories", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const entries = await source.listFiles(); + + const srcEntry = entries.find((e) => e.path === "src"); + expect(srcEntry?.type).toBe("directory"); + + const readmeEntry = entries.find((e) => e.path === "README.md"); + expect(readmeEntry?.type).toBe("file"); + }); + + it("lists contents of subdirectory when directory parameter is provided", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const entries = await source.listFiles("src"); + + const paths = entries.map((e) => e.path); + expect(paths).toContain("src/index.ts"); + expect(paths).toContain("src/utils.ts"); + + // All entries should be files since src only contains files + expect(entries.every((e) => e.type === "file")).toBe(true); + }); + + it("returns empty array for non-existent directory", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const entries = await source.listFiles("nonexistent"); + + expect(entries).toEqual([]); + }); + + it("prevents path traversal", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const entries = await source.listFiles("../../../etc"); + + expect(entries).toEqual([]); + }); + + it("skips node_modules and .git directories", async () => { + const source = new FilesystemSource({ rootPath: TEST_DIR }); + const entries = await source.listFiles(); + + const paths = entries.map((e) => e.path); + expect(paths).not.toContain("node_modules"); + expect(paths).not.toContain(".git"); + }); + }); +}); + diff --git a/context-connectors/src/sources/filesystem.ts b/context-connectors/src/sources/filesystem.ts new file mode 100644 index 0000000..f5e38aa --- /dev/null +++ b/context-connectors/src/sources/filesystem.ts @@ -0,0 +1,256 @@ +/** + * Filesystem Source - Fetches files from the local filesystem. + * + * Indexes files from a local directory with automatic filtering: + * - Respects .gitignore and .augmentignore patterns + * - Filters binary files, large files, and secrets + * - Skips common non-code directories (node_modules, .git, etc.) + * + * @module sources/filesystem + * + * @example + * ```typescript + * import { FilesystemSource } from "@augmentcode/context-connectors/sources"; + * + * const source = new FilesystemSource({ + * rootPath: "./my-project", + * ignorePatterns: ["*.log", "tmp/"], + * }); + * + * // For indexing + * const files = await source.fetchAll(); + * + * // For clients + * const fileList = await source.listFiles(); + * const contents = await source.readFile("src/index.ts"); + * ``` + */ + +import { promises as fs } from "node:fs"; +import { join, relative, resolve } from "node:path"; +import ignoreFactory, { type Ignore } from "ignore"; +import { shouldFilterFile } from "../core/file-filter.js"; +import { isoTimestamp } from "../core/utils.js"; +import type { FileEntry, FileInfo, SourceMetadata } from "../core/types.js"; +import type { FileChanges, Source } from "./types.js"; + +// With NodeNext module resolution, we need to access the default export properly +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const ignore = (ignoreFactory as any).default ?? ignoreFactory; + +/** + * Configuration for FilesystemSource. + */ +export interface FilesystemSourceConfig { + /** Root directory to index (can be relative or absolute) */ + rootPath: string; + /** + * Additional patterns to ignore. + * Added on top of .gitignore/.augmentignore patterns. + */ + ignorePatterns?: string[]; +} + +/** Default directories to always skip */ +const DEFAULT_SKIP_DIRS = new Set([".git", "node_modules", "__pycache__", ".venv", "venv"]); + +/** + * Source implementation for local filesystem directories. + * + * Walks the directory tree, applying filters in this order: + * 1. Skip default directories (.git, node_modules, etc.) + * 2. Apply .augmentignore patterns (highest priority) + * 3. Apply built-in filters (binary, large files, secrets) + * 4. Apply .gitignore patterns (lowest priority) + * + * @example + * ```typescript + * const source = new FilesystemSource({ rootPath: "./my-project" }); + * + * // Get all indexable files + * const files = await source.fetchAll(); + * console.log(`Found ${files.length} files`); + * + * // Read a specific file + * const content = await source.readFile("package.json"); + * ``` + */ +export class FilesystemSource implements Source { + readonly type = "filesystem" as const; + private readonly rootPath: string; + private readonly ignorePatterns: string[]; + + /** + * Create a new FilesystemSource. + * + * @param config - Source configuration + */ + constructor(config: FilesystemSourceConfig) { + this.rootPath = resolve(config.rootPath); + this.ignorePatterns = config.ignorePatterns ?? []; + } + + /** + * Load ignore rules from .gitignore and .augmentignore files + */ + private async loadIgnoreRules(): Promise<{ augmentignore: Ignore; gitignore: Ignore }> { + const augmentignore = ignore(); + const gitignore = ignore(); + + // Load .gitignore if exists + try { + const gitignoreContent = await fs.readFile(join(this.rootPath, ".gitignore"), "utf-8"); + gitignore.add(gitignoreContent); + } catch { + // .gitignore doesn't exist + } + + // Load .augmentignore if exists + try { + const augmentignoreContent = await fs.readFile(join(this.rootPath, ".augmentignore"), "utf-8"); + augmentignore.add(augmentignoreContent); + } catch { + // .augmentignore doesn't exist + } + + // Add custom ignore patterns to gitignore (lowest priority) + if (this.ignorePatterns.length > 0) { + gitignore.add(this.ignorePatterns); + } + + return { augmentignore, gitignore }; + } + + /** + * Recursively walk directory and collect files + */ + private async walkDirectory( + dir: string, + augmentignore: Ignore, + gitignore: Ignore, + files: FileEntry[] + ): Promise { + const entries = await fs.readdir(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = join(dir, entry.name); + const relativePath = relative(this.rootPath, fullPath); + + // Skip default ignored directories + if (entry.isDirectory() && DEFAULT_SKIP_DIRS.has(entry.name)) { + continue; + } + + if (entry.isDirectory()) { + // Check directory against ignore patterns before descending + const dirPath = relativePath + "/"; + if (augmentignore.ignores(dirPath) || gitignore.ignores(dirPath)) { + continue; + } + await this.walkDirectory(fullPath, augmentignore, gitignore, files); + } else if (entry.isFile()) { + // Apply ignore rules in priority order: + // 1. .augmentignore (highest priority) + if (augmentignore.ignores(relativePath)) { + continue; + } + + // 2. Read file content for filtering + let content: Buffer; + try { + content = await fs.readFile(fullPath); + } catch { + continue; // Skip unreadable files + } + + // 3. Apply shouldFilterFile (path validation, size, keyish, UTF-8) + const filterResult = shouldFilterFile({ path: relativePath, content }); + if (filterResult.filtered) { + continue; + } + + // 4. .gitignore (lowest priority) + if (gitignore.ignores(relativePath)) { + continue; + } + + // File passed all filters + files.push({ + path: relativePath, + contents: content.toString("utf-8"), + }); + } + } + } + + async fetchAll(): Promise { + const { augmentignore, gitignore } = await this.loadIgnoreRules(); + const files: FileEntry[] = []; + await this.walkDirectory(this.rootPath, augmentignore, gitignore, files); + return files; + } + + async listFiles(directory: string = ""): Promise { + const targetDir = join(this.rootPath, directory); + + // Prevent path traversal + const resolvedTarget = resolve(targetDir); + if (!resolvedTarget.startsWith(this.rootPath)) { + return []; + } + + try { + const entries = await fs.readdir(targetDir, { withFileTypes: true }); + const results: FileInfo[] = []; + + for (const entry of entries) { + // Skip default ignored directories + if (entry.isDirectory() && DEFAULT_SKIP_DIRS.has(entry.name)) { + continue; + } + + const entryPath = directory ? join(directory, entry.name) : entry.name; + + results.push({ + path: entryPath, + type: entry.isDirectory() ? "directory" : "file", + }); + } + + return results; + } catch { + // Directory doesn't exist or isn't readable + return []; + } + } + + async fetchChanges(_previous: SourceMetadata): Promise { + // For Phase 2, return null to force full reindex + // Incremental updates can be enhanced later + return null; + } + + async getMetadata(): Promise { + return { + type: "filesystem", + identifier: this.rootPath, + syncedAt: isoTimestamp(), + }; + } + + async readFile(path: string): Promise { + // Prevent path traversal + const fullPath = join(this.rootPath, path); + const resolvedPath = resolve(fullPath); + if (!resolvedPath.startsWith(this.rootPath)) { + return null; + } + + try { + return await fs.readFile(resolvedPath, "utf-8"); + } catch { + return null; + } + } +} + diff --git a/context-connectors/src/sources/github.test.ts b/context-connectors/src/sources/github.test.ts new file mode 100644 index 0000000..594938e --- /dev/null +++ b/context-connectors/src/sources/github.test.ts @@ -0,0 +1,161 @@ +/** + * Tests for GitHubSource + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { GitHubSource } from "./github.js"; + +// Mock data +const mockCommitSha = "abc123def456"; +const mockFiles = [ + { path: "README.md", type: "blob" }, + { path: "src/index.ts", type: "blob" }, + { path: "src", type: "tree" }, +]; + +describe("GitHubSource", () => { + const originalEnv = process.env.GITHUB_TOKEN; + + beforeEach(() => { + process.env.GITHUB_TOKEN = "test-token"; + }); + + afterEach(() => { + if (originalEnv) { + process.env.GITHUB_TOKEN = originalEnv; + } else { + delete process.env.GITHUB_TOKEN; + } + vi.restoreAllMocks(); + }); + + describe("constructor", () => { + it("uses provided token", () => { + expect(() => { + new GitHubSource({ + token: "custom-token", + owner: "test", + repo: "repo", + }); + }).not.toThrow(); + }); + + it("uses GITHUB_TOKEN from env", () => { + expect(() => { + new GitHubSource({ + owner: "test", + repo: "repo", + }); + }).not.toThrow(); + }); + + it("throws if no token available", () => { + delete process.env.GITHUB_TOKEN; + expect(() => { + new GitHubSource({ + owner: "test", + repo: "repo", + }); + }).toThrow(/GitHub token required/); + }); + + it("uses HEAD as default ref", () => { + const source = new GitHubSource({ + owner: "test", + repo: "repo", + }); + // @ts-expect-error - accessing private property for testing + expect(source.ref).toBe("HEAD"); + }); + + it("accepts custom ref", () => { + const source = new GitHubSource({ + owner: "test", + repo: "repo", + ref: "develop", + }); + // @ts-expect-error - accessing private property for testing + expect(source.ref).toBe("develop"); + }); + }); + + describe("type", () => { + it("returns 'github'", () => { + const source = new GitHubSource({ + owner: "test", + repo: "repo", + }); + expect(source.type).toBe("github"); + }); + }); + + // Integration tests - only run if GITHUB_TOKEN is available (use originalEnv captured before beforeEach) + const hasToken = !!originalEnv; + + describe.skipIf(!hasToken)("integration", () => { + it("indexes a public repo", async () => { + const source = new GitHubSource({ + token: originalEnv, + owner: "octocat", + repo: "Hello-World", + ref: "master", + }); + + const files = await source.fetchAll(); + expect(files.length).toBeGreaterThan(0); + }); + + it("lists files from a public repo", async () => { + const source = new GitHubSource({ + token: originalEnv, + owner: "octocat", + repo: "Hello-World", + ref: "master", + }); + + const files = await source.listFiles(); + expect(files.length).toBeGreaterThan(0); + expect(files[0]).toHaveProperty("path"); + }); + + it("reads a single file from a public repo", async () => { + const source = new GitHubSource({ + token: originalEnv, + owner: "octocat", + repo: "Hello-World", + ref: "master", + }); + + const content = await source.readFile("README"); + expect(content).not.toBeNull(); + }); + + it("returns null for missing file", async () => { + const source = new GitHubSource({ + token: originalEnv, + owner: "octocat", + repo: "Hello-World", + ref: "master", + }); + + const content = await source.readFile("nonexistent-file.txt"); + expect(content).toBeNull(); + }); + + it("gets correct metadata", async () => { + const source = new GitHubSource({ + token: originalEnv, + owner: "octocat", + repo: "Hello-World", + ref: "master", + }); + + const metadata = await source.getMetadata(); + expect(metadata.type).toBe("github"); + expect(metadata.identifier).toBe("octocat/Hello-World"); + expect(metadata.ref).toBeDefined(); + expect(metadata.syncedAt).toBeDefined(); + }); + }); +}); + diff --git a/context-connectors/src/sources/github.ts b/context-connectors/src/sources/github.ts new file mode 100644 index 0000000..91aee12 --- /dev/null +++ b/context-connectors/src/sources/github.ts @@ -0,0 +1,489 @@ +/** + * GitHub Source - Fetches files from GitHub repositories. + * + * Features: + * - Full indexing via tarball download + * - Incremental updates via Compare API + * - Force push detection (triggers full re-index) + * - Respects .gitignore and .augmentignore + * - Uses Git Trees API for efficient file listing + * + * @module sources/github + * + * @example + * ```typescript + * import { GitHubSource } from "@augmentcode/context-connectors/sources"; + * + * const source = new GitHubSource({ + * owner: "microsoft", + * repo: "vscode", + * ref: "main", + * }); + * + * // For indexing + * const files = await source.fetchAll(); + * + * // For clients + * const fileList = await source.listFiles(); + * const contents = await source.readFile("package.json"); + * ``` + */ + +import { Readable } from "node:stream"; +import ignoreFactory, { type Ignore } from "ignore"; +import tar from "tar"; +import { shouldFilterFile } from "../core/file-filter.js"; +import { isoTimestamp } from "../core/utils.js"; +import type { FileEntry, FileInfo, SourceMetadata } from "../core/types.js"; +import type { FileChanges, Source } from "./types.js"; + +// With NodeNext module resolution, we need to access the default export properly +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const ignore = (ignoreFactory as any).default ?? ignoreFactory; + +/** + * Configuration for GitHubSource. + */ +export interface GitHubSourceConfig { + /** + * GitHub API token for authentication. + * Required for private repos and to avoid rate limits. + * @default process.env.GITHUB_TOKEN + */ + token?: string; + /** Repository owner (user or organization) */ + owner: string; + /** Repository name */ + repo: string; + /** + * Git ref (branch, tag, or commit SHA). + * @default "HEAD" + */ + ref?: string; +} + +// Type for dynamically imported Octokit - use any since it's an optional peer dep +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type OctokitType = any; + +/** + * Source implementation for GitHub repositories. + * + * Uses the GitHub API to: + * - Download repository contents as tarball (for full index) + * - Compare commits (for incremental updates) + * - List files via Git Trees API (for file listing) + * - Read individual files (for read_file tool) + * + * Requires @octokit/rest as a peer dependency. + * + * @example + * ```typescript + * const source = new GitHubSource({ + * owner: "octocat", + * repo: "hello-world", + * ref: "main", + * }); + * + * // Resolve ref to commit SHA + * const meta = await source.getMetadata(); + * console.log(`Indexing ${meta.identifier}@${meta.ref}`); + * ``` + */ +export class GitHubSource implements Source { + readonly type = "github" as const; + private readonly owner: string; + private readonly repo: string; + private readonly ref: string; + private readonly token: string; + private octokit: OctokitType | null = null; + private resolvedRef: string | null = null; + + /** + * Create a new GitHubSource. + * + * @param config - Source configuration + * @throws Error if no GitHub token is available + */ + constructor(config: GitHubSourceConfig) { + this.owner = config.owner; + this.repo = config.repo; + this.ref = config.ref ?? "HEAD"; + this.token = config.token ?? process.env.GITHUB_TOKEN ?? ""; + + if (!this.token) { + throw new Error("GitHub token required. Set GITHUB_TOKEN environment variable or pass token in config."); + } + } + + /** + * Get or create Octokit instance (lazy loading for optional dependency) + */ + private async getOctokit(): Promise { + if (this.octokit) { + return this.octokit; + } + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { Octokit } = (await import("@octokit/rest" as any)) as { Octokit: any }; + this.octokit = new Octokit({ auth: this.token }); + return this.octokit; + } catch { + throw new Error( + "GitHubSource requires @octokit/rest. Install it with: npm install @octokit/rest" + ); + } + } + + /** + * Resolve ref (branch/tag/HEAD) to commit SHA + */ + private async resolveRefToSha(): Promise { + if (this.resolvedRef) { + return this.resolvedRef; + } + + const octokit = await this.getOctokit(); + try { + const { data } = await octokit.repos.getCommit({ + owner: this.owner, + repo: this.repo, + ref: this.ref, + }); + this.resolvedRef = data.sha; + return data.sha; + } catch (error) { + throw new Error( + `Failed to resolve ref "${this.ref}" for ${this.owner}/${this.repo}: ${error}` + ); + } + } + + /** + * Load ignore patterns from .gitignore and .augmentignore + */ + private async loadIgnorePatterns(ref: string): Promise<{ + augmentignore: Ignore; + gitignore: Ignore; + }> { + const augmentignore = ignore(); + const gitignore = ignore(); + + // Try to load .gitignore + try { + const content = await this.getFileContents(".gitignore", ref); + if (content) { + gitignore.add(content); + } + } catch { + // .gitignore doesn't exist + } + + // Try to load .augmentignore + try { + const content = await this.getFileContents(".augmentignore", ref); + if (content) { + augmentignore.add(content); + } + } catch { + // .augmentignore doesn't exist + } + + return { augmentignore, gitignore }; + } + + /** + * Get file contents at a specific ref + */ + private async getFileContents(path: string, ref: string): Promise { + const octokit = await this.getOctokit(); + try { + const { data } = await octokit.repos.getContent({ + owner: this.owner, + repo: this.repo, + path, + ref, + }); + + if (Array.isArray(data) || data.type !== "file") { + return null; + } + + // Decode base64 content + return Buffer.from(data.content, "base64").toString("utf-8"); + } catch { + return null; + } + } + + /** + * Download tarball and extract files + */ + private async downloadTarball(ref: string): Promise> { + const octokit = await this.getOctokit(); + console.log(`Downloading tarball for ${this.owner}/${this.repo}@${ref}...`); + + // Get tarball URL + const { url } = await octokit.repos.downloadTarballArchive({ + owner: this.owner, + repo: this.repo, + ref, + }); + + // Download tarball + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to download tarball: ${response.statusText}`); + } + + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + + // Load ignore patterns + const { augmentignore, gitignore } = await this.loadIgnorePatterns(ref); + + // Extract files from tarball + const files = new Map(); + const stream = Readable.from(buffer); + + await new Promise((resolve, reject) => { + const parser = tar.list({ + onentry: (entry) => { + // Skip directories and symlinks + if (entry.type !== "File") { + return; + } + + // Remove the root directory prefix (e.g., "owner-repo-sha/") + const pathParts = entry.path.split("/"); + pathParts.shift(); // Remove first component + const filePath = pathParts.join("/"); + + // Read file contents + const chunks: Buffer[] = []; + entry.on("data", (chunk) => chunks.push(chunk)); + entry.on("end", () => { + const contentBuffer = Buffer.concat(chunks); + + // Apply filtering in priority order: + // 1. .augmentignore + if (augmentignore.ignores(filePath)) { + return; + } + + // 2. Path validation, file size, keyish patterns, UTF-8 validation + const filterResult = shouldFilterFile({ + path: filePath, + content: contentBuffer, + }); + + if (filterResult.filtered) { + return; + } + + // 3. .gitignore (checked last) + if (gitignore.ignores(filePath)) { + return; + } + + // File passed all filters + const contents = contentBuffer.toString("utf-8"); + files.set(filePath, contents); + }); + }, + }); + + stream.pipe(parser); + parser.on("close", resolve); + stream.on("error", reject); + }); + + console.log(`Extracted ${files.size} files from tarball`); + return files; + } + + /** + * Check if the push was a force push (base commit not reachable from head) + * + * Force push detection cases: + * 1. Compare API fails - base commit no longer exists + * 2. status: "diverged" - histories have diverged + * 3. status: "behind" - head is behind base (revert to older commit) + * 4. behind_by > 0 - additional indicator of non-linear history + */ + private async isForcePush(base: string, head: string): Promise { + const octokit = await this.getOctokit(); + try { + const { data } = await octokit.repos.compareCommits({ + owner: this.owner, + repo: this.repo, + base, + head, + }); + + // Check for non-linear history indicators + // "diverged" means histories have diverged (typical force push) + // "behind" means head is an ancestor of base (revert to older commit) + if (data.status === "diverged" || data.status === "behind") { + return true; + } + + // Additional safety check: behind_by > 0 indicates head is behind base + if (data.behind_by > 0) { + return true; + } + + return false; + } catch { + // If comparison fails, it's likely a force push + return true; + } + } + + /** + * Check if ignore files changed between commits + */ + private async ignoreFilesChanged(base: string, head: string): Promise { + const octokit = await this.getOctokit(); + const { data } = await octokit.repos.compareCommits({ + owner: this.owner, + repo: this.repo, + base, + head, + }); + + const ignoreFiles = [".gitignore", ".augmentignore"]; + return (data.files || []).some((file: { filename: string }) => + ignoreFiles.includes(file.filename) + ); + } + + async fetchAll(): Promise { + const ref = await this.resolveRefToSha(); + const filesMap = await this.downloadTarball(ref); + + const files: FileEntry[] = []; + for (const [path, contents] of filesMap) { + files.push({ path, contents }); + } + + return files; + } + + async fetchChanges(previous: SourceMetadata): Promise { + // Need previous ref to compute changes + if (!previous.ref) { + return null; + } + + const currentRef = await this.resolveRefToSha(); + + // Same commit, no changes + if (previous.ref === currentRef) { + return { added: [], modified: [], removed: [] }; + } + + // Check for force push + if (await this.isForcePush(previous.ref, currentRef)) { + console.log("Force push detected, triggering full re-index"); + return null; + } + + // Check if ignore files changed + if (await this.ignoreFilesChanged(previous.ref, currentRef)) { + console.log("Ignore files changed, triggering full re-index"); + return null; + } + + // Get changed files via compare API + const octokit = await this.getOctokit(); + const { data } = await octokit.repos.compareCommits({ + owner: this.owner, + repo: this.repo, + base: previous.ref, + head: currentRef, + }); + + const changedFiles = data.files || []; + + // If too many changes, do full reindex + if (changedFiles.length > 100) { + console.log(`Too many changes (${changedFiles.length}), triggering full re-index`); + return null; + } + + const added: FileEntry[] = []; + const modified: FileEntry[] = []; + const removed: string[] = []; + + for (const file of changedFiles) { + if (file.status === "removed") { + removed.push(file.filename); + } else if (file.status === "added" || file.status === "modified" || file.status === "renamed") { + // Download file contents + const contents = await this.getFileContents(file.filename, currentRef); + if (contents !== null) { + const entry = { path: file.filename, contents }; + if (file.status === "added") { + added.push(entry); + } else { + modified.push(entry); + } + } + + // Handle rename as remove + add + if (file.status === "renamed" && file.previous_filename) { + removed.push(file.previous_filename); + } + } + } + + return { added, modified, removed }; + } + + async getMetadata(): Promise { + const ref = await this.resolveRefToSha(); + return { + type: "github", + identifier: `${this.owner}/${this.repo}`, + ref, + syncedAt: isoTimestamp(), + }; + } + + async listFiles(directory: string = ""): Promise { + // Use getContent API for specific directory (non-recursive) + const octokit = await this.getOctokit(); + const sha = await this.resolveRefToSha(); + + try { + const { data } = await octokit.repos.getContent({ + owner: this.owner, + repo: this.repo, + path: directory, + ref: sha, + }); + + // getContent returns an array for directories, single object for files + if (!Array.isArray(data)) { + // This is a file, not a directory - return empty + return []; + } + + return data.map((item: { path: string; type: string }) => ({ + path: item.path, + type: item.type === "dir" ? "directory" as const : "file" as const, + })); + } catch { + // Directory doesn't exist + return []; + } + } + + async readFile(path: string): Promise { + const ref = await this.resolveRefToSha(); + return this.getFileContents(path, ref); + } +} + diff --git a/context-connectors/src/sources/gitlab.test.ts b/context-connectors/src/sources/gitlab.test.ts new file mode 100644 index 0000000..84b7c94 --- /dev/null +++ b/context-connectors/src/sources/gitlab.test.ts @@ -0,0 +1,180 @@ +/** + * Tests for GitLabSource + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { GitLabSource } from "./gitlab.js"; + +describe("GitLabSource", () => { + const originalEnv = process.env.GITLAB_TOKEN; + + beforeEach(() => { + process.env.GITLAB_TOKEN = "test-token"; + }); + + afterEach(() => { + if (originalEnv) { + process.env.GITLAB_TOKEN = originalEnv; + } else { + delete process.env.GITLAB_TOKEN; + } + vi.restoreAllMocks(); + }); + + describe("constructor", () => { + it("uses provided token", () => { + expect(() => { + new GitLabSource({ + token: "custom-token", + projectId: "group/project", + }); + }).not.toThrow(); + }); + + it("uses GITLAB_TOKEN from env", () => { + expect(() => { + new GitLabSource({ + projectId: "group/project", + }); + }).not.toThrow(); + }); + + it("throws if no token available", () => { + delete process.env.GITLAB_TOKEN; + expect(() => { + new GitLabSource({ + projectId: "group/project", + }); + }).toThrow(/GitLab token required/); + }); + + it("uses HEAD as default ref", () => { + const source = new GitLabSource({ + projectId: "group/project", + }); + // @ts-expect-error - accessing private property for testing + expect(source.ref).toBe("HEAD"); + }); + + it("accepts custom ref", () => { + const source = new GitLabSource({ + projectId: "group/project", + ref: "develop", + }); + // @ts-expect-error - accessing private property for testing + expect(source.ref).toBe("develop"); + }); + + it("uses default GitLab.com URL", () => { + const source = new GitLabSource({ + projectId: "group/project", + }); + // @ts-expect-error - accessing private property for testing + expect(source.baseUrl).toBe("https://gitlab.com"); + }); + + it("accepts custom base URL for self-hosted", () => { + const source = new GitLabSource({ + projectId: "group/project", + baseUrl: "https://gitlab.mycompany.com", + }); + // @ts-expect-error - accessing private property for testing + expect(source.baseUrl).toBe("https://gitlab.mycompany.com"); + }); + + it("strips trailing slash from base URL", () => { + const source = new GitLabSource({ + projectId: "group/project", + baseUrl: "https://gitlab.mycompany.com/", + }); + // @ts-expect-error - accessing private property for testing + expect(source.baseUrl).toBe("https://gitlab.mycompany.com"); + }); + + it("URL-encodes project ID", () => { + const source = new GitLabSource({ + projectId: "group/subgroup/project", + }); + // @ts-expect-error - accessing private property for testing + expect(source.encodedProjectId).toBe("group%2Fsubgroup%2Fproject"); + }); + }); + + describe("type", () => { + it("returns 'gitlab'", () => { + const source = new GitLabSource({ + projectId: "group/project", + }); + expect(source.type).toBe("gitlab"); + }); + }); + + // Integration tests - only run if GITLAB_TOKEN is available (use originalEnv captured before beforeEach) + const hasToken = !!originalEnv; + + describe.skipIf(!hasToken)("integration", () => { + // Use gitlab-runner to test pagination (has many files) + const testProject = "gitlab-org/gitlab-runner"; + const testRef = "main"; + + it("indexes a public GitLab project", async () => { + const source = new GitLabSource({ + token: originalEnv, + projectId: testProject, + ref: testRef, + }); + + const files = await source.fetchAll(); + expect(files.length).toBeGreaterThan(0); + }); + + it("lists files from a public project", async () => { + const source = new GitLabSource({ + token: originalEnv, + projectId: testProject, + ref: testRef, + }); + + const files = await source.listFiles(); + expect(files.length).toBeGreaterThan(0); + expect(files[0]).toHaveProperty("path"); + }); + + it("reads a single file from a public project", async () => { + const source = new GitLabSource({ + token: originalEnv, + projectId: testProject, + ref: testRef, + }); + + const content = await source.readFile("README.md"); + expect(content).not.toBeNull(); + }); + + it("returns null for missing file", async () => { + const source = new GitLabSource({ + token: originalEnv, + projectId: testProject, + ref: testRef, + }); + + const content = await source.readFile("nonexistent-file-12345.txt"); + expect(content).toBeNull(); + }); + + it("gets correct metadata", async () => { + const source = new GitLabSource({ + token: originalEnv, + projectId: testProject, + ref: testRef, + }); + + const metadata = await source.getMetadata(); + expect(metadata.type).toBe("gitlab"); + expect(metadata.identifier).toBe(testProject); + expect(metadata.ref).toBeDefined(); + expect(metadata.syncedAt).toBeDefined(); + }); + }); +}); + diff --git a/context-connectors/src/sources/gitlab.ts b/context-connectors/src/sources/gitlab.ts new file mode 100644 index 0000000..2347a88 --- /dev/null +++ b/context-connectors/src/sources/gitlab.ts @@ -0,0 +1,447 @@ +/** + * GitLab Source - Fetches files from GitLab repositories + */ + +import { Readable } from "node:stream"; +import ignoreFactory, { type Ignore } from "ignore"; +import tar from "tar"; +import { shouldFilterFile } from "../core/file-filter.js"; +import { isoTimestamp } from "../core/utils.js"; +import type { FileEntry, FileInfo, SourceMetadata } from "../core/types.js"; +import type { FileChanges, Source } from "./types.js"; + +// With NodeNext module resolution, we need to access the default export properly +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const ignore = (ignoreFactory as any).default ?? ignoreFactory; + +/** Configuration for GitLabSource */ +export interface GitLabSourceConfig { + /** GitLab API token. Defaults to process.env.GITLAB_TOKEN */ + token?: string; + /** GitLab base URL. Defaults to https://gitlab.com */ + baseUrl?: string; + /** Project ID or path (e.g., "group/project" or numeric ID) */ + projectId: string; + /** Branch/tag/commit ref. Defaults to "HEAD" */ + ref?: string; +} + +export class GitLabSource implements Source { + readonly type = "gitlab" as const; + private readonly baseUrl: string; + private readonly projectId: string; + private readonly encodedProjectId: string; + private readonly ref: string; + private readonly token: string; + private resolvedRef: string | null = null; + + constructor(config: GitLabSourceConfig) { + this.baseUrl = (config.baseUrl ?? "https://gitlab.com").replace(/\/$/, ""); + this.projectId = config.projectId; + // URL-encode the project path for API calls + this.encodedProjectId = encodeURIComponent(config.projectId); + this.ref = config.ref ?? "HEAD"; + this.token = config.token ?? process.env.GITLAB_TOKEN ?? ""; + + if (!this.token) { + throw new Error("GitLab token required. Set GITLAB_TOKEN environment variable or pass token in config."); + } + } + + /** + * Make an authenticated API request to GitLab + */ + private async apiRequest(path: string, options: RequestInit = {}): Promise { + const url = `${this.baseUrl}/api/v4${path}`; + const response = await fetch(url, { + ...options, + headers: { + "PRIVATE-TOKEN": this.token, + ...options.headers, + }, + }); + + if (!response.ok) { + throw new Error(`GitLab API error: ${response.status} ${response.statusText} for ${path}`); + } + + return response.json() as T; + } + + /** + * Make a paginated API request to GitLab, fetching all pages. + * Uses x-next-page header to determine if more pages exist. + */ + private async apiRequestPaginated(basePath: string): Promise { + const results: T[] = []; + let page = 1; + const perPage = 100; + + while (true) { + const separator = basePath.includes("?") ? "&" : "?"; + const url = `${this.baseUrl}/api/v4${basePath}${separator}per_page=${perPage}&page=${page}`; + + const response = await fetch(url, { + headers: { + "PRIVATE-TOKEN": this.token, + }, + }); + + if (!response.ok) { + throw new Error(`GitLab API error: ${response.status} ${response.statusText} for ${basePath}`); + } + + const data = (await response.json()) as T[]; + results.push(...data); + + // Check if there are more pages using x-next-page header + const nextPage = response.headers.get("x-next-page"); + if (!nextPage || nextPage === "") { + break; + } + + page = parseInt(nextPage, 10); + } + + return results; + } + + /** + * Resolve ref (branch/tag/HEAD) to commit SHA + */ + private async resolveRefToSha(): Promise { + if (this.resolvedRef) { + return this.resolvedRef; + } + + try { + // Get the commit for the ref + const data = await this.apiRequest<{ id: string }>( + `/projects/${this.encodedProjectId}/repository/commits/${encodeURIComponent(this.ref)}` + ); + this.resolvedRef = data.id; + return data.id; + } catch (error) { + throw new Error( + `Failed to resolve ref "${this.ref}" for ${this.projectId}: ${error}` + ); + } + } + + /** + * Load ignore patterns from .gitignore and .augmentignore + */ + private async loadIgnorePatterns(ref: string): Promise<{ + augmentignore: Ignore; + gitignore: Ignore; + }> { + const augmentignore = ignore(); + const gitignore = ignore(); + + // Try to load .gitignore + const gitignoreContent = await this.readFileRaw(".gitignore", ref); + if (gitignoreContent) { + gitignore.add(gitignoreContent); + } + + // Try to load .augmentignore + const augmentignoreContent = await this.readFileRaw(".augmentignore", ref); + if (augmentignoreContent) { + augmentignore.add(augmentignoreContent); + } + + return { augmentignore, gitignore }; + } + + /** + * Get raw file contents at a specific ref + */ + private async readFileRaw(path: string, ref: string): Promise { + try { + const encodedPath = encodeURIComponent(path); + const url = `${this.baseUrl}/api/v4/projects/${this.encodedProjectId}/repository/files/${encodedPath}/raw?ref=${encodeURIComponent(ref)}`; + const response = await fetch(url, { + headers: { "PRIVATE-TOKEN": this.token }, + }); + + if (!response.ok) { + return null; + } + + return response.text(); + } catch { + return null; + } + } + + /** + * Download archive and extract files + */ + private async downloadArchive(ref: string): Promise> { + console.log(`Downloading archive for ${this.projectId}@${ref}...`); + + const url = `${this.baseUrl}/api/v4/projects/${this.encodedProjectId}/repository/archive.tar.gz?sha=${encodeURIComponent(ref)}`; + // Note: GitLab has hotlinking protection that returns 406 for cross-origin requests. + // Using mode: 'same-origin' works around this protection. See: https://github.com/unjs/giget/issues/97 + const response = await fetch(url, { + headers: { "PRIVATE-TOKEN": this.token }, + mode: "same-origin", + }); + + if (!response.ok) { + throw new Error(`Failed to download archive: ${response.statusText}`); + } + + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + + // Load ignore patterns + const { augmentignore, gitignore } = await this.loadIgnorePatterns(ref); + + // Extract files from tarball + const files = new Map(); + const stream = Readable.from(buffer); + + await new Promise((resolve, reject) => { + const parser = tar.list({ + onentry: (entry) => { + // Skip directories and symlinks + if (entry.type !== "File") { + return; + } + + // Remove the root directory prefix (e.g., "project-ref-sha/") + const pathParts = entry.path.split("/"); + pathParts.shift(); // Remove first component + const filePath = pathParts.join("/"); + + // Read file contents + const chunks: Buffer[] = []; + entry.on("data", (chunk) => chunks.push(chunk)); + entry.on("end", () => { + const contentBuffer = Buffer.concat(chunks); + + // Apply filtering in priority order: + // 1. .augmentignore + if (augmentignore.ignores(filePath)) { + return; + } + + // 2. Path validation, file size, keyish patterns, UTF-8 validation + const filterResult = shouldFilterFile({ + path: filePath, + content: contentBuffer, + }); + + if (filterResult.filtered) { + return; + } + + // 3. .gitignore (checked last) + if (gitignore.ignores(filePath)) { + return; + } + + // File passed all filters + const contents = contentBuffer.toString("utf-8"); + files.set(filePath, contents); + }); + }, + }); + + stream.pipe(parser); + parser.on("close", resolve); + stream.on("error", reject); + }); + + console.log(`Extracted ${files.size} files from archive`); + return files; + } + + /** + * Check if the push was a force push (base commit not reachable from head) + * + * Force push detection cases: + * 1. Compare API fails - base commit no longer exists + * 2. Empty commits list with non-empty diffs - diverged histories + * 3. compare_same_ref true but different SHAs - revert to older commit + * + * GitLab Compare API returns: + * - commits: list of commits from base to head + * - diffs: list of file diffs + * - compare_timeout: boolean if comparison timed out + * - compare_same_ref: true if comparing same ref + * + * When head is behind base (force push revert), the commits array is empty + * but the diffs show changes because it's comparing backwards. + */ + private async isForcePush(base: string, head: string): Promise { + try { + interface CompareResult { + commits: Array<{ id: string }>; + diffs: Array<{ new_path: string }>; + compare_same_ref?: boolean; + } + + const data = await this.apiRequest( + `/projects/${this.encodedProjectId}/repository/compare?from=${encodeURIComponent(base)}&to=${encodeURIComponent(head)}` + ); + + // If commits array is empty but we have diffs, it indicates diverged/behind history + // This happens when head is an ancestor of base (revert to older commit) + if (data.commits.length === 0 && data.diffs.length > 0) { + return true; + } + + // If commits array is empty and no diffs, but the refs are different, + // this indicates head is behind base + if (data.commits.length === 0 && base !== head) { + // Double-check by comparing in reverse direction + const reverseData = await this.apiRequest( + `/projects/${this.encodedProjectId}/repository/compare?from=${encodeURIComponent(head)}&to=${encodeURIComponent(base)}` + ); + + // If reverse comparison has commits, then head is behind base (force push revert) + if (reverseData.commits.length > 0) { + return true; + } + } + + return false; + } catch { + // If comparison fails, it's likely a force push + return true; + } + } + + /** + * Check if ignore files changed between commits + */ + private async ignoreFilesChanged(base: string, head: string): Promise { + const data = await this.apiRequest<{ diffs: Array<{ new_path: string }> }>( + `/projects/${this.encodedProjectId}/repository/compare?from=${encodeURIComponent(base)}&to=${encodeURIComponent(head)}` + ); + + const ignoreFiles = [".gitignore", ".augmentignore"]; + return (data.diffs || []).some((diff) => + ignoreFiles.includes(diff.new_path) + ); + } + + async fetchAll(): Promise { + const ref = await this.resolveRefToSha(); + const filesMap = await this.downloadArchive(ref); + + const files: FileEntry[] = []; + for (const [path, contents] of filesMap) { + files.push({ path, contents }); + } + + return files; + } + + async fetchChanges(previous: SourceMetadata): Promise { + // Need previous ref to compute changes + if (!previous.ref) { + return null; + } + + const currentRef = await this.resolveRefToSha(); + + // Same commit, no changes + if (previous.ref === currentRef) { + return { added: [], modified: [], removed: [] }; + } + + // Check for force push + if (await this.isForcePush(previous.ref, currentRef)) { + console.log("Force push detected, triggering full re-index"); + return null; + } + + // Check if ignore files changed + if (await this.ignoreFilesChanged(previous.ref, currentRef)) { + console.log("Ignore files changed, triggering full re-index"); + return null; + } + + // Get changed files via compare API + const data = await this.apiRequest<{ diffs: Array<{ new_path: string; old_path: string; new_file: boolean; deleted_file: boolean; renamed_file: boolean }> }>( + `/projects/${this.encodedProjectId}/repository/compare?from=${encodeURIComponent(previous.ref)}&to=${encodeURIComponent(currentRef)}` + ); + + const changedFiles = data.diffs || []; + + // If too many changes, do full reindex + if (changedFiles.length > 100) { + console.log(`Too many changes (${changedFiles.length}), triggering full re-index`); + return null; + } + + const added: FileEntry[] = []; + const modified: FileEntry[] = []; + const removed: string[] = []; + + for (const file of changedFiles) { + if (file.deleted_file) { + removed.push(file.old_path); + } else { + // Download file contents + const contents = await this.readFileRaw(file.new_path, currentRef); + if (contents !== null) { + const entry = { path: file.new_path, contents }; + if (file.new_file) { + added.push(entry); + } else { + modified.push(entry); + } + } + + // Handle rename as remove + add + if (file.renamed_file && file.old_path !== file.new_path) { + removed.push(file.old_path); + } + } + } + + return { added, modified, removed }; + } + + async getMetadata(): Promise { + const ref = await this.resolveRefToSha(); + return { + type: "gitlab", + identifier: this.projectId, + ref, + syncedAt: isoTimestamp(), + }; + } + + async listFiles(directory: string = ""): Promise { + const sha = await this.resolveRefToSha(); + + // Use tree API without recursive=true for non-recursive listing + // Add path parameter to list specific directory + let url = `/projects/${this.encodedProjectId}/repository/tree?ref=${encodeURIComponent(sha)}`; + if (directory) { + url += `&path=${encodeURIComponent(directory)}`; + } + + try { + const data = await this.apiRequestPaginated<{ path: string; type: string }>(url); + + return data.map((item) => ({ + path: item.path, + type: item.type === "tree" ? "directory" as const : "file" as const, + })); + } catch { + // Directory doesn't exist + return []; + } + } + + async readFile(path: string): Promise { + const ref = await this.resolveRefToSha(); + return this.readFileRaw(path, ref); + } +} diff --git a/context-connectors/src/sources/index.ts b/context-connectors/src/sources/index.ts new file mode 100644 index 0000000..b72a8d2 --- /dev/null +++ b/context-connectors/src/sources/index.ts @@ -0,0 +1,16 @@ +/** + * Sources module exports + */ + +export type { FileChanges, Source } from "./types.js"; +export { FilesystemSource } from "./filesystem.js"; +export type { FilesystemSourceConfig } from "./filesystem.js"; +export { GitHubSource } from "./github.js"; +export type { GitHubSourceConfig } from "./github.js"; +export { GitLabSource } from "./gitlab.js"; +export type { GitLabSourceConfig } from "./gitlab.js"; +export { BitBucketSource } from "./bitbucket.js"; +export type { BitBucketSourceConfig } from "./bitbucket.js"; +export { WebsiteSource } from "./website.js"; +export type { WebsiteSourceConfig } from "./website.js"; + diff --git a/context-connectors/src/sources/types.ts b/context-connectors/src/sources/types.ts new file mode 100644 index 0000000..f96d2de --- /dev/null +++ b/context-connectors/src/sources/types.ts @@ -0,0 +1,143 @@ +/** + * Source interface and types for fetching files from data sources. + * + * A Source represents any data source that can be indexed: + * - Filesystem (local directories) + * - GitHub repositories + * - GitLab repositories + * - Websites + * + * Sources provide methods for both: + * - **Indexing**: fetchAll, fetchChanges, getMetadata + * - **Client operations**: listFiles, readFile + * + * @module sources/types + */ + +import type { FileEntry, FileInfo, SourceMetadata } from "../core/types.js"; + +/** + * Changes detected since the last sync, used for incremental indexing. + * + * When a source can determine what changed since the last sync, + * it returns this structure. If incremental updates aren't possible + * (e.g., force push, ignore file changes), the source returns null. + * + * @example + * ```typescript + * const changes = await source.fetchChanges(previousMetadata); + * if (changes) { + * console.log(`${changes.added.length} added, ${changes.removed.length} removed`); + * } else { + * console.log("Full re-index required"); + * } + * ``` + */ +export interface FileChanges { + /** Files that were added since last sync (includes contents) */ + added: FileEntry[]; + /** Files that were modified since last sync (includes contents) */ + modified: FileEntry[]; + /** Paths of files that were removed since last sync */ + removed: string[]; +} + +/** + * Source interface for fetching files from a data source. + * + * Implementations must provide methods for: + * - **Full indexing**: `fetchAll()` to get all files + * - **Incremental indexing**: `fetchChanges()` to get only what changed + * - **Metadata**: `getMetadata()` to track source version + * - **Client access**: `listFiles()` and `readFile()` for tools + * + * @example + * ```typescript + * // Create a source + * const source = new FilesystemSource({ rootPath: "./my-project" }); + * + * // For indexing + * const files = await source.fetchAll(); + * const metadata = await source.getMetadata(); + * + * // For client tools + * const fileList = await source.listFiles(); + * const contents = await source.readFile("src/index.ts"); + * ``` + */ +export interface Source { + /** The type of this source (matches SourceMetadata.type) */ + readonly type: SourceMetadata["type"]; + + // --- Methods for Indexing --- + + /** + * Fetch all files from the source for a full index. + * + * This method is called when: + * - Creating a new index + * - Incremental update isn't possible + * - Force re-index is requested + * + * Files are automatically filtered based on: + * - .augmentignore patterns + * - Built-in filters (binary files, large files, secrets) + * - .gitignore patterns + * + * @returns Array of all indexable files with their contents + */ + fetchAll(): Promise; + + /** + * Fetch changes since the last sync for incremental indexing. + * + * Returns null if incremental update isn't possible, which triggers + * a full re-index. Common reasons for returning null: + * - Force push detected + * - Ignore files (.gitignore, .augmentignore) changed + * - Too many changes to process efficiently + * - Source doesn't support incremental updates + * + * @param previous - Metadata from the previous sync + * @returns FileChanges if incremental possible, null otherwise + */ + fetchChanges(previous: SourceMetadata): Promise; + + /** + * Get metadata about the current state of the source. + * + * This metadata is stored alongside the index and used for: + * - Detecting changes for incremental updates + * - Displaying source information to users + * - Validating that a Source matches a stored index + * + * @returns Current source metadata including type, identifier, and ref + */ + getMetadata(): Promise; + + // --- Methods for Clients --- + + /** + * List files and directories in a specific directory (non-recursive). + * + * Used by the `listFiles` tool to show available files and directories. + * Returns only immediate children of the specified directory. + * Agents can explore subdirectories by making multiple calls. + * + * @param directory - Directory path to list (default: root "") + * @returns Array of file/directory info objects with paths and types + */ + listFiles(directory?: string): Promise; + + /** + * Read a single file by path. + * + * Used by the `readFile` tool to fetch file contents on demand. + * Returns null if the file doesn't exist or isn't readable. + * + * @param path - Relative path to the file + * @returns File contents as string, or null if not found + */ + readFile(path: string): Promise; +} + diff --git a/context-connectors/src/sources/website.test.ts b/context-connectors/src/sources/website.test.ts new file mode 100644 index 0000000..634912d --- /dev/null +++ b/context-connectors/src/sources/website.test.ts @@ -0,0 +1,173 @@ +/** + * Tests for WebsiteSource + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { WebsiteSource } from "./website.js"; + +describe("WebsiteSource", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe("constructor", () => { + it("parses URL correctly", () => { + const source = new WebsiteSource({ + url: "https://example.com/docs", + }); + // @ts-expect-error - accessing private property for testing + expect(source.startUrl.hostname).toBe("example.com"); + }); + + it("uses default maxDepth of 3", () => { + const source = new WebsiteSource({ + url: "https://example.com", + }); + // @ts-expect-error - accessing private property for testing + expect(source.maxDepth).toBe(3); + }); + + it("accepts custom maxDepth", () => { + const source = new WebsiteSource({ + url: "https://example.com", + maxDepth: 5, + }); + // @ts-expect-error - accessing private property for testing + expect(source.maxDepth).toBe(5); + }); + + it("uses default maxPages of 100", () => { + const source = new WebsiteSource({ + url: "https://example.com", + }); + // @ts-expect-error - accessing private property for testing + expect(source.maxPages).toBe(100); + }); + + it("accepts custom maxPages", () => { + const source = new WebsiteSource({ + url: "https://example.com", + maxPages: 50, + }); + // @ts-expect-error - accessing private property for testing + expect(source.maxPages).toBe(50); + }); + + it("uses default delay of 100ms", () => { + const source = new WebsiteSource({ + url: "https://example.com", + }); + // @ts-expect-error - accessing private property for testing + expect(source.delayMs).toBe(100); + }); + + it("respects robots.txt by default", () => { + const source = new WebsiteSource({ + url: "https://example.com", + }); + // @ts-expect-error - accessing private property for testing + expect(source.respectRobotsTxt).toBe(true); + }); + + it("can disable robots.txt", () => { + const source = new WebsiteSource({ + url: "https://example.com", + respectRobotsTxt: false, + }); + // @ts-expect-error - accessing private property for testing + expect(source.respectRobotsTxt).toBe(false); + }); + }); + + describe("type", () => { + it("returns 'website'", () => { + const source = new WebsiteSource({ + url: "https://example.com", + }); + expect(source.type).toBe("website"); + }); + }); + + describe("getMetadata", () => { + it("returns correct metadata structure", async () => { + const source = new WebsiteSource({ + url: "https://example.com/docs", + }); + + const metadata = await source.getMetadata(); + expect(metadata.type).toBe("website"); + expect(metadata.identifier).toBe("example.com"); + expect(metadata.ref).toBeDefined(); + expect(metadata.syncedAt).toBeDefined(); + }); + }); + + describe("fetchChanges", () => { + it("always returns null (no incremental updates)", async () => { + const source = new WebsiteSource({ + url: "https://example.com", + }); + + const changes = await source.fetchChanges({ + type: "website", + identifier: "example.com", + syncedAt: new Date().toISOString(), + }); + + expect(changes).toBeNull(); + }); + }); + + describe("pattern matching", () => { + it("matches simple paths", () => { + const source = new WebsiteSource({ + url: "https://example.com", + includePaths: ["/docs/*"], + }); + // @ts-expect-error - accessing private method for testing + expect(source.matchPattern("/docs/intro", "/docs/*")).toBe(true); + // @ts-expect-error - accessing private method for testing + expect(source.matchPattern("/blog/post", "/docs/*")).toBe(false); + }); + + it("matches wildcard patterns", () => { + const source = new WebsiteSource({ + url: "https://example.com", + }); + // @ts-expect-error - accessing private method for testing + expect(source.matchPattern("/docs/v2/guide", "/docs/*/guide")).toBe(true); + }); + }); + + // Integration tests - actually crawl a website + describe.skip("integration", () => { + it("crawls a simple website", async () => { + const source = new WebsiteSource({ + url: "https://example.com", + maxDepth: 1, + maxPages: 5, + }); + + const files = await source.fetchAll(); + expect(files.length).toBeGreaterThan(0); + expect(files[0].contents).toBeDefined(); + }); + + it("lists files from crawled site", async () => { + const source = new WebsiteSource({ + url: "https://example.com", + maxDepth: 1, + maxPages: 5, + }); + + const files = await source.listFiles(); + expect(files.length).toBeGreaterThan(0); + expect(files[0]).toHaveProperty("path"); + }); + }); +}); + diff --git a/context-connectors/src/sources/website.ts b/context-connectors/src/sources/website.ts new file mode 100644 index 0000000..25dcd9e --- /dev/null +++ b/context-connectors/src/sources/website.ts @@ -0,0 +1,440 @@ +/** + * Website Source - Crawls and indexes website content + */ + +import { isoTimestamp } from "../core/utils.js"; +import type { FileEntry, FileInfo, SourceMetadata } from "../core/types.js"; +import type { FileChanges, Source } from "./types.js"; + +/** Configuration for WebsiteSource */ +export interface WebsiteSourceConfig { + /** Starting URL to crawl */ + url: string; + /** Maximum crawl depth. Defaults to 3 */ + maxDepth?: number; + /** Maximum pages to crawl. Defaults to 100 */ + maxPages?: number; + /** URL patterns to include (glob patterns) */ + includePaths?: string[]; + /** URL patterns to exclude (glob patterns) */ + excludePaths?: string[]; + /** Whether to respect robots.txt. Defaults to true */ + respectRobotsTxt?: boolean; + /** Custom user agent string */ + userAgent?: string; + /** Delay between requests in ms. Defaults to 100 */ + delayMs?: number; +} + +// Types for dynamically imported dependencies +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type CheerioAPI = any; + +interface CrawledPage { + url: string; + path: string; + content: string; + title: string; +} + +export class WebsiteSource implements Source { + readonly type = "website" as const; + private readonly startUrl: URL; + private readonly maxDepth: number; + private readonly maxPages: number; + private readonly includePaths: string[]; + private readonly excludePaths: string[]; + private readonly respectRobotsTxt: boolean; + private readonly userAgent: string; + private readonly delayMs: number; + private crawledPages: CrawledPage[] = []; + private robotsRules: Set = new Set(); + private robotsLoaded = false; + + constructor(config: WebsiteSourceConfig) { + this.startUrl = new URL(config.url); + this.maxDepth = config.maxDepth ?? 3; + this.maxPages = config.maxPages ?? 100; + this.includePaths = config.includePaths ?? []; + this.excludePaths = config.excludePaths ?? []; + this.respectRobotsTxt = config.respectRobotsTxt ?? true; + this.userAgent = config.userAgent ?? "ContextConnectors/1.0"; + this.delayMs = config.delayMs ?? 100; + } + + /** + * Load and cache cheerio dependency + */ + private async getCheerio(): Promise<{ load: (html: string) => CheerioAPI }> { + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (await import("cheerio" as any)) as { load: (html: string) => CheerioAPI }; + } catch { + throw new Error( + "WebsiteSource requires cheerio. Install it with: npm install cheerio" + ); + } + } + + /** + * Load robots.txt rules + */ + private async loadRobotsTxt(): Promise { + if (this.robotsLoaded || !this.respectRobotsTxt) { + return; + } + + try { + const robotsUrl = new URL("/robots.txt", this.startUrl.origin); + const response = await fetch(robotsUrl.href, { + headers: { "User-Agent": this.userAgent }, + }); + + if (response.ok) { + const text = await response.text(); + this.parseRobotsTxt(text); + } + } catch { + // Ignore errors loading robots.txt + } + + this.robotsLoaded = true; + } + + /** + * Parse robots.txt content + */ + private parseRobotsTxt(content: string): void { + let inUserAgentBlock = false; + + for (const line of content.split("\n")) { + const trimmed = line.trim().toLowerCase(); + + if (trimmed.startsWith("user-agent:")) { + const agent = trimmed.substring(11).trim(); + inUserAgentBlock = agent === "*" || agent === this.userAgent.toLowerCase(); + } else if (inUserAgentBlock && trimmed.startsWith("disallow:")) { + const path = trimmed.substring(9).trim(); + if (path) { + this.robotsRules.add(path); + } + } + } + } + + /** + * Check if a path is allowed by robots.txt + */ + private isAllowedByRobots(path: string): boolean { + if (!this.respectRobotsTxt) { + return true; + } + + for (const rule of this.robotsRules) { + if (path.startsWith(rule)) { + return false; + } + } + return true; + } + + /** + * Check if URL should be crawled based on include/exclude patterns + */ + private shouldCrawlUrl(url: URL): boolean { + const path = url.pathname; + + // Check exclude patterns first + for (const pattern of this.excludePaths) { + if (this.matchPattern(path, pattern)) { + return false; + } + } + + // If include patterns specified, must match one + if (this.includePaths.length > 0) { + return this.includePaths.some((pattern) => this.matchPattern(path, pattern)); + } + + return true; + } + + /** + * Simple glob pattern matching + */ + private matchPattern(path: string, pattern: string): boolean { + // Convert glob to regex + const regex = new RegExp( + "^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$" + ); + return regex.test(path); + } + + /** + * Delay helper for rate limiting + */ + private delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + /** + * Extract links from HTML + */ + private extractLinks($: CheerioAPI, baseUrl: URL): URL[] { + const links: URL[] = []; + + $("a[href]").each((_: number, element: unknown) => { + try { + const href = $(element).attr("href"); + if (!href) return; + + // Skip non-http links + if (href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("javascript:")) { + return; + } + + const url = new URL(href, baseUrl.href); + + // Only follow same-origin links + if (url.origin === this.startUrl.origin) { + // Normalize URL (remove hash, trailing slash) + url.hash = ""; + if (url.pathname !== "/" && url.pathname.endsWith("/")) { + url.pathname = url.pathname.slice(0, -1); + } + links.push(url); + } + } catch { + // Invalid URL, skip + } + }); + + return links; + } + + /** + * Convert HTML to markdown-like text + */ + private htmlToText($: CheerioAPI): string { + // Remove script, style, and nav elements + $("script, style, nav, header, footer, aside").remove(); + + // Get title + const title = $("title").text().trim(); + + // Get main content - prefer article or main, fallback to body + let content = $("article, main, [role=main]").first(); + if (content.length === 0) { + content = $("body"); + } + + // Convert headings + content.find("h1, h2, h3, h4, h5, h6").each((_: number, el: unknown) => { + const level = parseInt($(el).prop("tagName").substring(1)); + const prefix = "#".repeat(level); + $(el).replaceWith(`\n\n${prefix} ${$(el).text().trim()}\n\n`); + }); + + // Convert paragraphs + content.find("p").each((_: number, el: unknown) => { + $(el).replaceWith(`\n\n${$(el).text().trim()}\n\n`); + }); + + // Convert lists + content.find("li").each((_: number, el: unknown) => { + $(el).replaceWith(`\n- ${$(el).text().trim()}`); + }); + + // Convert code blocks + content.find("pre, code").each((_: number, el: unknown) => { + $(el).replaceWith(`\n\`\`\`\n${$(el).text()}\n\`\`\`\n`); + }); + + // Get text content + let text = content.text(); + + // Clean up whitespace + text = text + .replace(/\n{3,}/g, "\n\n") + .replace(/[ \t]+/g, " ") + .trim(); + + // Add title as heading if present + if (title) { + text = `# ${title}\n\n${text}`; + } + + return text; + } + + /** + * Crawl a single page + */ + private async crawlPage(url: URL): Promise<{ content: string; title: string; links: URL[] } | null> { + try { + const response = await fetch(url.href, { + headers: { + "User-Agent": this.userAgent, + "Accept": "text/html,application/xhtml+xml", + }, + }); + + if (!response.ok) { + return null; + } + + const contentType = response.headers.get("content-type") || ""; + if (!contentType.includes("text/html")) { + return null; + } + + const html = await response.text(); + const cheerio = await this.getCheerio(); + const $ = cheerio.load(html); + + const title = $("title").text().trim() || url.pathname; + const content = this.htmlToText($); + const links = this.extractLinks($, url); + + return { content, title, links }; + } catch { + return null; + } + } + + /** + * Crawl the website starting from the configured URL + */ + private async crawl(): Promise { + await this.loadRobotsTxt(); + + const visited = new Set(); + const queue: Array<{ url: URL; depth: number }> = [{ url: this.startUrl, depth: 0 }]; + this.crawledPages = []; + + console.log(`Starting crawl from ${this.startUrl.href} (max depth: ${this.maxDepth}, max pages: ${this.maxPages})`); + + while (queue.length > 0 && this.crawledPages.length < this.maxPages) { + const { url, depth } = queue.shift()!; + const urlKey = url.href; + + if (visited.has(urlKey)) { + continue; + } + visited.add(urlKey); + + // Check robots.txt + if (!this.isAllowedByRobots(url.pathname)) { + continue; + } + + // Rate limiting + if (visited.size > 1) { + await this.delay(this.delayMs); + } + + const result = await this.crawlPage(url); + if (!result) { + continue; + } + + // Add links to queue if within depth limit (always traverse to discover pages) + if (depth < this.maxDepth) { + for (const link of result.links) { + if (!visited.has(link.href)) { + queue.push({ url: link, depth: depth + 1 }); + } + } + } + + // Check include/exclude patterns for indexing (not for traversal) + if (!this.shouldCrawlUrl(url)) { + continue; + } + + // Create a path from the URL for storage + let path = url.pathname; + if (path === "/" || path === "") { + path = "/index"; + } + // Remove leading slash and add .md extension + path = path.replace(/^\//, "") + ".md"; + + this.crawledPages.push({ + url: url.href, + path, + content: result.content, + title: result.title, + }); + + console.log(`Crawled: ${url.pathname} (${this.crawledPages.length}/${this.maxPages})`); + } + + console.log(`Crawl complete. Indexed ${this.crawledPages.length} pages.`); + } + + async fetchAll(): Promise { + await this.crawl(); + + return this.crawledPages.map((page) => ({ + path: page.path, + contents: page.content, + })); + } + + async fetchChanges(_previous: SourceMetadata): Promise { + // Websites don't have a good mechanism for incremental updates + // Always return null to trigger a full re-crawl + return null; + } + + async getMetadata(): Promise { + return { + type: "website", + identifier: this.startUrl.hostname, + ref: isoTimestamp(), // Use timestamp as "ref" since websites don't have versions + syncedAt: isoTimestamp(), + }; + } + + async listFiles(directory: string = ""): Promise { + // Websites don't have a directory structure - all pages are in root + // Only return results when querying root directory + if (directory !== "") { + return []; + } + + // If we haven't crawled yet, do a crawl + if (this.crawledPages.length === 0) { + await this.crawl(); + } + + return this.crawledPages.map((page) => ({ path: page.path, type: "file" as const })); + } + + async readFile(path: string): Promise { + // Check if we have the file from a previous crawl + const page = this.crawledPages.find((p) => p.path === path); + if (page) { + return page.content; + } + + // Try to construct URL from path and fetch + try { + // Remove .md extension and reconstruct URL + let urlPath = path.replace(/\.md$/, ""); + if (urlPath === "index") { + urlPath = "/"; + } else { + urlPath = "/" + urlPath; + } + + const url = new URL(urlPath, this.startUrl.origin); + const result = await this.crawlPage(url); + return result?.content ?? null; + } catch { + return null; + } + } +} + diff --git a/context-connectors/src/stores/filesystem.test.ts b/context-connectors/src/stores/filesystem.test.ts new file mode 100644 index 0000000..3c2c854 --- /dev/null +++ b/context-connectors/src/stores/filesystem.test.ts @@ -0,0 +1,156 @@ +/** + * Tests for FilesystemStore + */ + +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { promises as fs } from "node:fs"; +import { join } from "node:path"; +import { FilesystemStore } from "./filesystem.js"; +import type { IndexState } from "../core/types.js"; + +const TEST_DIR = "/tmp/context-connectors-test-fs-store"; + +// Create a minimal mock IndexState for testing +function createMockState(): IndexState { + return { + contextState: { + checkpointId: "test-checkpoint-123", + addedBlobs: [], + deletedBlobs: [], + blobs: [], + }, + source: { + type: "filesystem", + identifier: "/path/to/project", + syncedAt: new Date().toISOString(), + }, + }; +} + +describe("FilesystemStore", () => { + beforeEach(async () => { + // Clean up test directory before each test + await fs.rm(TEST_DIR, { recursive: true, force: true }); + }); + + afterEach(async () => { + // Clean up test directory after each test + await fs.rm(TEST_DIR, { recursive: true, force: true }); + }); + + describe("save", () => { + it("creates directory and file", async () => { + const store = new FilesystemStore({ basePath: TEST_DIR }); + const state = createMockState(); + + await store.save("my-project", state); + + // Verify file was created + const statePath = join(TEST_DIR, "my-project", "state.json"); + const data = await fs.readFile(statePath, "utf-8"); + const savedState = JSON.parse(data); + + expect(savedState.contextState.checkpointId).toBe("test-checkpoint-123"); + expect(savedState.source.type).toBe("filesystem"); + }); + + it("sanitizes key for filesystem safety", async () => { + const store = new FilesystemStore({ basePath: TEST_DIR }); + const state = createMockState(); + + await store.save("owner/repo@main", state); + + // Key should be sanitized + const sanitizedKey = "owner_repo_main"; + const statePath = join(TEST_DIR, sanitizedKey, "state.json"); + await expect(fs.access(statePath)).resolves.toBeUndefined(); + }); + }); + + describe("load", () => { + it("returns saved state", async () => { + const store = new FilesystemStore({ basePath: TEST_DIR }); + const originalState = createMockState(); + + await store.save("test-key", originalState); + const loadedState = await store.load("test-key"); + + expect(loadedState).not.toBeNull(); + expect(loadedState!.contextState.checkpointId).toBe("test-checkpoint-123"); + expect(loadedState!.source.identifier).toBe("/path/to/project"); + }); + + it("returns null for missing key", async () => { + const store = new FilesystemStore({ basePath: TEST_DIR }); + const state = await store.load("nonexistent-key"); + + expect(state).toBeNull(); + }); + + it("returns null when basePath does not exist", async () => { + const store = new FilesystemStore({ basePath: "/nonexistent/path" }); + const state = await store.load("some-key"); + + expect(state).toBeNull(); + }); + }); + + describe("delete", () => { + it("removes state", async () => { + const store = new FilesystemStore({ basePath: TEST_DIR }); + const state = createMockState(); + + await store.save("to-delete", state); + expect(await store.load("to-delete")).not.toBeNull(); + + await store.delete("to-delete"); + expect(await store.load("to-delete")).toBeNull(); + }); + + it("does not throw for missing key", async () => { + const store = new FilesystemStore({ basePath: TEST_DIR }); + await expect(store.delete("nonexistent")).resolves.toBeUndefined(); + }); + }); + + describe("list", () => { + it("returns saved keys", async () => { + const store = new FilesystemStore({ basePath: TEST_DIR }); + const state = createMockState(); + + await store.save("project-a", state); + await store.save("project-b", state); + await store.save("project-c", state); + + const keys = await store.list(); + + expect(keys).toContain("project-a"); + expect(keys).toContain("project-b"); + expect(keys).toContain("project-c"); + expect(keys.length).toBe(3); + }); + + it("returns empty array when basePath does not exist", async () => { + const store = new FilesystemStore({ basePath: "/nonexistent/path" }); + const keys = await store.list(); + + expect(keys).toEqual([]); + }); + + it("ignores directories without state.json", async () => { + const store = new FilesystemStore({ basePath: TEST_DIR }); + const state = createMockState(); + + await store.save("valid-project", state); + // Create an invalid directory without state.json + await fs.mkdir(join(TEST_DIR, "invalid-project"), { recursive: true }); + + const keys = await store.list(); + + expect(keys).toContain("valid-project"); + expect(keys).not.toContain("invalid-project"); + expect(keys.length).toBe(1); + }); + }); +}); + diff --git a/context-connectors/src/stores/filesystem.ts b/context-connectors/src/stores/filesystem.ts new file mode 100644 index 0000000..4cde870 --- /dev/null +++ b/context-connectors/src/stores/filesystem.ts @@ -0,0 +1,170 @@ +/** + * Filesystem Store - Persists index state to local filesystem. + * + * Stores index state and DirectContext data to disk, enabling: + * - Offline access to indexes + * - Incremental updates (by preserving previous state) + * - Sharing indexes between machines (by copying the directory) + * + * @module stores/filesystem + * + * @example + * ```typescript + * import { FilesystemStore } from "@augmentcode/context-connectors/stores"; + * + * // Default location: .context-connectors + * const store = new FilesystemStore(); + * + * // Custom location + * const customStore = new FilesystemStore({ + * basePath: "/data/indexes", + * }); + * + * // Save an index + * await store.save("my-project", state, contextData); + * + * // Load an index + * const { state, contextData } = await store.load("my-project"); + * ``` + */ + +import { promises as fs } from "node:fs"; +import { join } from "node:path"; +import { sanitizeKey } from "../core/utils.js"; +import type { IndexState } from "../core/types.js"; +import type { IndexStore } from "./types.js"; + +/** + * Configuration for FilesystemStore. + */ +export interface FilesystemStoreConfig { + /** + * Directory to store index files. + * @default ".context-connectors" + */ + basePath?: string; +} + +/** Default base path for storing index files */ +const DEFAULT_BASE_PATH = ".context-connectors"; + +/** State filename within each index directory */ +const STATE_FILENAME = "state.json"; + +/** + * Store implementation that persists to the local filesystem. + * + * Creates a directory structure: + * ``` + * {basePath}/ + * {key}/ + * state.json - Index metadata and file list + * context.bin - DirectContext binary data + * ``` + * + * @example + * ```typescript + * const store = new FilesystemStore({ basePath: "./indexes" }); + * + * // Check if index exists + * if (await store.exists("my-project")) { + * const { state, contextData } = await store.load("my-project"); + * } + * ``` + */ +export class FilesystemStore implements IndexStore { + private readonly basePath: string; + + /** + * Create a new FilesystemStore. + * + * @param config - Optional configuration + */ + constructor(config: FilesystemStoreConfig = {}) { + this.basePath = config.basePath ?? DEFAULT_BASE_PATH; + } + + /** + * Get the path to the state file for a given key + */ + private getStatePath(key: string): string { + const sanitized = sanitizeKey(key); + return join(this.basePath, sanitized, STATE_FILENAME); + } + + /** + * Get the directory path for a given key + */ + private getKeyDir(key: string): string { + const sanitized = sanitizeKey(key); + return join(this.basePath, sanitized); + } + + async load(key: string): Promise { + const statePath = this.getStatePath(key); + + try { + const data = await fs.readFile(statePath, "utf-8"); + return JSON.parse(data) as IndexState; + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + return null; + } + throw error; + } + } + + async save(key: string, state: IndexState): Promise { + const keyDir = this.getKeyDir(key); + const statePath = this.getStatePath(key); + + // Ensure directory exists + await fs.mkdir(keyDir, { recursive: true }); + + // Write state with pretty-printing for debuggability + await fs.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8"); + } + + async delete(key: string): Promise { + const keyDir = this.getKeyDir(key); + + try { + // Remove the entire directory (includes state.json and any other files) + await fs.rm(keyDir, { recursive: true, force: true }); + } catch (error) { + // Ignore if directory doesn't exist + if ((error as NodeJS.ErrnoException).code !== "ENOENT") { + throw error; + } + } + } + + async list(): Promise { + try { + const entries = await fs.readdir(this.basePath, { withFileTypes: true }); + const keys: string[] = []; + + for (const entry of entries) { + if (entry.isDirectory()) { + // Check if this directory contains a state.json file + const statePath = join(this.basePath, entry.name, STATE_FILENAME); + try { + await fs.access(statePath); + keys.push(entry.name); // Return sanitized name + } catch { + // Directory doesn't contain a valid state, skip it + } + } + } + + return keys; + } catch (error) { + // If basePath doesn't exist, return empty list + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + return []; + } + throw error; + } + } +} + diff --git a/context-connectors/src/stores/index.ts b/context-connectors/src/stores/index.ts new file mode 100644 index 0000000..136335f --- /dev/null +++ b/context-connectors/src/stores/index.ts @@ -0,0 +1,12 @@ +/** + * Stores module exports + */ + +export type { IndexStoreReader, IndexStore } from "./types.js"; +export { FilesystemStore } from "./filesystem.js"; +export type { FilesystemStoreConfig } from "./filesystem.js"; +export { MemoryStore } from "./memory.js"; +export type { MemoryStoreConfig } from "./memory.js"; +export { S3Store } from "./s3.js"; +export type { S3StoreConfig } from "./s3.js"; + diff --git a/context-connectors/src/stores/memory.test.ts b/context-connectors/src/stores/memory.test.ts new file mode 100644 index 0000000..bba8450 --- /dev/null +++ b/context-connectors/src/stores/memory.test.ts @@ -0,0 +1,149 @@ +/** + * Tests for MemoryStore + */ + +import { describe, it, expect, beforeEach } from "vitest"; +import { MemoryStore } from "./memory.js"; +import type { IndexState } from "../core/types.js"; + +describe("MemoryStore", () => { + let store: MemoryStore; + + const createTestState = (id: string): IndexState => ({ + contextState: { + checkpointId: `checkpoint-${id}`, + addedBlobs: [], + deletedBlobs: [], + blobs: [], + }, + source: { + type: "filesystem", + identifier: `/test/${id}`, + syncedAt: new Date().toISOString(), + }, + }); + + beforeEach(() => { + store = new MemoryStore(); + }); + + describe("save and load", () => { + it("should save and load state", async () => { + const state = createTestState("1"); + await store.save("test-key", state); + + const loaded = await store.load("test-key"); + expect(loaded).toEqual(state); + }); + + it("should return null for non-existent key", async () => { + const loaded = await store.load("non-existent"); + expect(loaded).toBeNull(); + }); + + it("should overwrite existing state", async () => { + const state1 = createTestState("1"); + const state2 = createTestState("2"); + + await store.save("key", state1); + await store.save("key", state2); + + const loaded = await store.load("key"); + expect(loaded).toEqual(state2); + }); + + it("should return deep copy on load", async () => { + const state = createTestState("1"); + await store.save("key", state); + + const loaded = await store.load("key"); + loaded!.source.identifier = "modified"; + + const loadedAgain = await store.load("key"); + expect(loadedAgain!.source.identifier).toBe("/test/1"); + }); + + it("should store deep copy on save", async () => { + const state = createTestState("1"); + await store.save("key", state); + + state.source.identifier = "modified"; + + const loaded = await store.load("key"); + expect(loaded!.source.identifier).toBe("/test/1"); + }); + }); + + describe("delete", () => { + it("should delete existing key", async () => { + const state = createTestState("1"); + await store.save("key", state); + expect(store.has("key")).toBe(true); + + await store.delete("key"); + expect(store.has("key")).toBe(false); + }); + + it("should not throw for non-existent key", async () => { + await expect(store.delete("non-existent")).resolves.not.toThrow(); + }); + }); + + describe("list", () => { + it("should return empty array when no keys", async () => { + const keys = await store.list(); + expect(keys).toEqual([]); + }); + + it("should return all keys", async () => { + await store.save("key1", createTestState("1")); + await store.save("key2", createTestState("2")); + await store.save("key3", createTestState("3")); + + const keys = await store.list(); + expect(keys.sort()).toEqual(["key1", "key2", "key3"]); + }); + }); + + describe("helper methods", () => { + it("size should return number of stored keys", async () => { + expect(store.size).toBe(0); + + await store.save("key1", createTestState("1")); + expect(store.size).toBe(1); + + await store.save("key2", createTestState("2")); + expect(store.size).toBe(2); + }); + + it("clear should remove all data", async () => { + await store.save("key1", createTestState("1")); + await store.save("key2", createTestState("2")); + + store.clear(); + expect(store.size).toBe(0); + expect(await store.list()).toEqual([]); + }); + + it("has should check key existence", async () => { + expect(store.has("key")).toBe(false); + + await store.save("key", createTestState("1")); + expect(store.has("key")).toBe(true); + }); + }); + + describe("initialization", () => { + it("should accept initial data", async () => { + const initialData = new Map(); + initialData.set("existing", createTestState("existing")); + + const storeWithData = new MemoryStore({ initialData }); + + expect(storeWithData.has("existing")).toBe(true); + const loaded = await storeWithData.load("existing"); + expect(loaded!.source.identifier).toBe("/test/existing"); + }); + }); +}); + diff --git a/context-connectors/src/stores/memory.ts b/context-connectors/src/stores/memory.ts new file mode 100644 index 0000000..a13184f --- /dev/null +++ b/context-connectors/src/stores/memory.ts @@ -0,0 +1,62 @@ +/** + * Memory Store - In-memory storage for testing and embedded use + * + * This store keeps all data in memory and is useful for: + * - Unit testing without filesystem access + * - Embedded usage where persistence is not needed + * - Short-lived processes + */ + +import type { IndexState } from "../core/types.js"; +import type { IndexStore } from "./types.js"; + +/** Configuration for MemoryStore */ +export interface MemoryStoreConfig { + /** Optional initial data to populate the store */ + initialData?: Map; +} + +export class MemoryStore implements IndexStore { + private readonly data: Map; + + constructor(config: MemoryStoreConfig = {}) { + this.data = config.initialData + ? new Map(config.initialData) + : new Map(); + } + + async load(key: string): Promise { + const state = this.data.get(key); + // Return a deep copy to prevent external mutation + return state ? JSON.parse(JSON.stringify(state)) : null; + } + + async save(key: string, state: IndexState): Promise { + // Store a deep copy to prevent external mutation + this.data.set(key, JSON.parse(JSON.stringify(state))); + } + + async delete(key: string): Promise { + this.data.delete(key); + } + + async list(): Promise { + return Array.from(this.data.keys()); + } + + /** Get the number of stored indexes (useful for testing) */ + get size(): number { + return this.data.size; + } + + /** Clear all stored data (useful for testing) */ + clear(): void { + this.data.clear(); + } + + /** Check if a key exists (useful for testing) */ + has(key: string): boolean { + return this.data.has(key); + } +} + diff --git a/context-connectors/src/stores/s3.test.ts b/context-connectors/src/stores/s3.test.ts new file mode 100644 index 0000000..494d7d0 --- /dev/null +++ b/context-connectors/src/stores/s3.test.ts @@ -0,0 +1,173 @@ +/** + * Tests for S3Store + * + * Unit tests mock the S3 client. + * Integration tests require AWS credentials and skip if not available. + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import type { IndexState } from "../core/types.js"; + +// Mock the @aws-sdk/client-s3 module +vi.mock("@aws-sdk/client-s3", () => { + const mockSend = vi.fn(); + return { + S3Client: vi.fn().mockImplementation(() => ({ send: mockSend })), + GetObjectCommand: vi.fn(), + PutObjectCommand: vi.fn(), + DeleteObjectCommand: vi.fn(), + ListObjectsV2Command: vi.fn(), + __mockSend: mockSend, + }; +}); + +describe("S3Store", () => { + const createTestState = (id: string): IndexState => ({ + contextState: { + checkpointId: `checkpoint-${id}`, + addedBlobs: [], + deletedBlobs: [], + blobs: [], + }, + source: { + type: "filesystem", + identifier: `/test/${id}`, + syncedAt: new Date().toISOString(), + }, + }); + + let mockSend: ReturnType; + + beforeEach(async () => { + vi.clearAllMocks(); + const s3Module = await import("@aws-sdk/client-s3"); + mockSend = (s3Module as unknown as { __mockSend: ReturnType }).__mockSend; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe("configuration", () => { + it("should use default prefix and region", async () => { + const { S3Store } = await import("./s3.js"); + const store = new S3Store({ bucket: "test-bucket" }); + + // Trigger client initialization + mockSend.mockResolvedValueOnce({ + Body: { transformToString: () => Promise.resolve(null) }, + }); + await store.load("test"); + + const { S3Client } = await import("@aws-sdk/client-s3"); + expect(S3Client).toHaveBeenCalledWith({ + region: "us-east-1", + endpoint: undefined, + forcePathStyle: false, + }); + }); + + it("should use custom configuration", async () => { + const { S3Store } = await import("./s3.js"); + const store = new S3Store({ + bucket: "test-bucket", + prefix: "custom/", + region: "eu-west-1", + endpoint: "http://localhost:9000", + forcePathStyle: true, + }); + + mockSend.mockResolvedValueOnce({ + Body: { transformToString: () => Promise.resolve(null) }, + }); + await store.load("test"); + + const { S3Client } = await import("@aws-sdk/client-s3"); + expect(S3Client).toHaveBeenCalledWith({ + region: "eu-west-1", + endpoint: "http://localhost:9000", + forcePathStyle: true, + }); + }); + }); + + describe("load", () => { + it("should load state from S3", async () => { + const { S3Store } = await import("./s3.js"); + const store = new S3Store({ bucket: "test-bucket" }); + const state = createTestState("1"); + + mockSend.mockResolvedValueOnce({ + Body: { transformToString: () => Promise.resolve(JSON.stringify(state)) }, + }); + + const loaded = await store.load("test-key"); + expect(loaded).toEqual(state); + }); + + it("should return null for non-existent key", async () => { + const { S3Store } = await import("./s3.js"); + const store = new S3Store({ bucket: "test-bucket" }); + + mockSend.mockRejectedValueOnce({ name: "NoSuchKey" }); + + const loaded = await store.load("non-existent"); + expect(loaded).toBeNull(); + }); + }); + + describe("save", () => { + it("should save state to S3", async () => { + const { S3Store } = await import("./s3.js"); + const store = new S3Store({ bucket: "test-bucket" }); + const state = createTestState("1"); + + mockSend.mockResolvedValueOnce({}); + + await store.save("test-key", state); + + const { PutObjectCommand } = await import("@aws-sdk/client-s3"); + expect(PutObjectCommand).toHaveBeenCalledWith({ + Bucket: "test-bucket", + Key: "context-connectors/test-key/state.json", + Body: JSON.stringify(state, null, 2), + ContentType: "application/json", + }); + }); + }); + + describe("delete", () => { + it("should delete state from S3", async () => { + const { S3Store } = await import("./s3.js"); + const store = new S3Store({ bucket: "test-bucket" }); + + mockSend.mockResolvedValueOnce({}); + + await store.delete("test-key"); + + const { DeleteObjectCommand } = await import("@aws-sdk/client-s3"); + expect(DeleteObjectCommand).toHaveBeenCalledWith({ + Bucket: "test-bucket", + Key: "context-connectors/test-key/state.json", + }); + }); + }); + + describe("list", () => { + it("should list keys from S3", async () => { + const { S3Store } = await import("./s3.js"); + const store = new S3Store({ bucket: "test-bucket" }); + + mockSend.mockResolvedValueOnce({ + CommonPrefixes: [ + { Prefix: "context-connectors/key1/" }, + { Prefix: "context-connectors/key2/" }, + ], + }); + + const keys = await store.list(); + expect(keys.sort()).toEqual(["key1", "key2"]); + }); + }); +}); + diff --git a/context-connectors/src/stores/s3.ts b/context-connectors/src/stores/s3.ts new file mode 100644 index 0000000..c95db3d --- /dev/null +++ b/context-connectors/src/stores/s3.ts @@ -0,0 +1,238 @@ +/** + * S3 Store - Persists index state to S3-compatible object storage. + * + * Enables cloud-based index storage for: + * - Sharing indexes across machines + * - CI/CD pipelines (index in CI, use in production) + * - Serverless deployments + * + * Supports: + * - AWS S3 + * - MinIO + * - Cloudflare R2 + * - DigitalOcean Spaces + * - Any S3-compatible storage + * + * Requires @aws-sdk/client-s3 as a peer dependency. + * + * @module stores/s3 + * + * @example + * ```typescript + * import { S3Store } from "@augmentcode/context-connectors/stores"; + * + * // AWS S3 + * const awsStore = new S3Store({ + * bucket: "my-indexes", + * prefix: "context-connectors/", + * region: "us-west-2", + * }); + * + * // MinIO or other S3-compatible + * const minioStore = new S3Store({ + * bucket: "indexes", + * endpoint: "http://localhost:9000", + * forcePathStyle: true, + * }); + * ``` + */ + +import type { IndexState } from "../core/types.js"; +import type { IndexStore } from "./types.js"; + +/** + * Configuration for S3Store. + */ +export interface S3StoreConfig { + /** S3 bucket name */ + bucket: string; + /** + * Key prefix for all stored indexes. + * @default "context-connectors/" + */ + prefix?: string; + /** + * AWS region. + * @default process.env.AWS_REGION or "us-east-1" + */ + region?: string; + /** + * Custom endpoint URL for S3-compatible services. + * Required for MinIO, R2, DigitalOcean Spaces, etc. + */ + endpoint?: string; + /** + * Force path-style URLs instead of virtual-hosted-style. + * Required for some S3-compatible services. + * @default false + */ + forcePathStyle?: boolean; +} + +const DEFAULT_PREFIX = "context-connectors/"; +const STATE_FILENAME = "state.json"; + +/** Type for the S3 client (imported dynamically) */ +type S3ClientType = import("@aws-sdk/client-s3").S3Client; +type GetObjectCommandType = typeof import("@aws-sdk/client-s3").GetObjectCommand; +type PutObjectCommandType = typeof import("@aws-sdk/client-s3").PutObjectCommand; +type DeleteObjectCommandType = typeof import("@aws-sdk/client-s3").DeleteObjectCommand; +type ListObjectsV2CommandType = typeof import("@aws-sdk/client-s3").ListObjectsV2Command; + +/** + * Store implementation that persists to S3-compatible object storage. + * + * Creates an object structure: + * ``` + * {prefix}{key}/ + * state.json - Index metadata and file list + * context.bin - DirectContext binary data + * ``` + * + * @example + * ```typescript + * const store = new S3Store({ bucket: "my-indexes" }); + * + * // Check if index exists + * if (await store.exists("my-project")) { + * const { state, contextData } = await store.load("my-project"); + * } + * ``` + */ +export class S3Store implements IndexStore { + private readonly bucket: string; + private readonly prefix: string; + private readonly region: string; + private readonly endpoint?: string; + private readonly forcePathStyle: boolean; + private client: S3ClientType | null = null; + private commands: { + GetObjectCommand: GetObjectCommandType; + PutObjectCommand: PutObjectCommandType; + DeleteObjectCommand: DeleteObjectCommandType; + ListObjectsV2Command: ListObjectsV2CommandType; + } | null = null; + + /** + * Create a new S3Store. + * + * @param config - Store configuration + */ + constructor(config: S3StoreConfig) { + this.bucket = config.bucket; + this.prefix = config.prefix ?? DEFAULT_PREFIX; + this.region = config.region ?? process.env.AWS_REGION ?? "us-east-1"; + this.endpoint = config.endpoint; + this.forcePathStyle = config.forcePathStyle ?? false; + } + + private async getClient(): Promise { + if (this.client) return this.client; + + try { + const s3Module = await import("@aws-sdk/client-s3"); + const { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand, ListObjectsV2Command } = s3Module; + + this.client = new S3Client({ + region: this.region, + endpoint: this.endpoint, + forcePathStyle: this.forcePathStyle, + }); + + this.commands = { + GetObjectCommand, + PutObjectCommand, + DeleteObjectCommand, + ListObjectsV2Command, + }; + + return this.client; + } catch { + throw new Error( + "S3Store requires @aws-sdk/client-s3. Install it with: npm install @aws-sdk/client-s3" + ); + } + } + + private getStateKey(key: string): string { + return `${this.prefix}${key}/${STATE_FILENAME}`; + } + + async load(key: string): Promise { + const client = await this.getClient(); + const stateKey = this.getStateKey(key); + + try { + const command = new this.commands!.GetObjectCommand({ + Bucket: this.bucket, + Key: stateKey, + }); + const response = await client.send(command); + const body = await response.Body?.transformToString(); + if (!body) return null; + return JSON.parse(body) as IndexState; + } catch (error: unknown) { + const err = error as { name?: string }; + if (err.name === "NoSuchKey") { + return null; + } + throw error; + } + } + + async save(key: string, state: IndexState): Promise { + const client = await this.getClient(); + const stateKey = this.getStateKey(key); + + const command = new this.commands!.PutObjectCommand({ + Bucket: this.bucket, + Key: stateKey, + Body: JSON.stringify(state, null, 2), + ContentType: "application/json", + }); + await client.send(command); + } + + async delete(key: string): Promise { + const client = await this.getClient(); + const stateKey = this.getStateKey(key); + + const command = new this.commands!.DeleteObjectCommand({ + Bucket: this.bucket, + Key: stateKey, + }); + await client.send(command); + } + + async list(): Promise { + const client = await this.getClient(); + const keys: string[] = []; + + let continuationToken: string | undefined; + do { + const command = new this.commands!.ListObjectsV2Command({ + Bucket: this.bucket, + Prefix: this.prefix, + Delimiter: "/", + ContinuationToken: continuationToken, + }); + const response = await client.send(command); + + // CommonPrefixes contains the "directories" + for (const prefix of response.CommonPrefixes ?? []) { + if (prefix.Prefix) { + // Extract key name from prefix (remove base prefix and trailing slash) + const keyName = prefix.Prefix + .slice(this.prefix.length) + .replace(/\/$/, ""); + if (keyName) keys.push(keyName); + } + } + + continuationToken = response.NextContinuationToken; + } while (continuationToken); + + return keys; + } +} + diff --git a/context-connectors/src/stores/types.ts b/context-connectors/src/stores/types.ts new file mode 100644 index 0000000..50d740c --- /dev/null +++ b/context-connectors/src/stores/types.ts @@ -0,0 +1,85 @@ +/** + * Store interfaces for persisting index state. + * + * Stores provide persistence for indexed data: + * - **IndexStoreReader**: Read-only access (for clients) + * - **IndexStore**: Full read/write access (for indexer) + * + * Available implementations: + * - `FilesystemStore`: Local file storage + * - `S3Store`: AWS S3 and compatible services + * - `MemoryStore`: In-memory storage (for testing) + * + * @module stores/types + */ + +import type { IndexState } from "../core/types.js"; + +/** + * Read-only store interface for loading index state. + * + * Sufficient for SearchClient and other consumers that only + * need to read existing indexes. + * + * @example + * ```typescript + * const store: IndexStoreReader = new FilesystemStore(); + * const state = await store.load("my-project"); + * const keys = await store.list(); + * ``` + */ +export interface IndexStoreReader { + /** + * Load index state by key. + * + * @param key - The index key/name + * @returns The stored IndexState, or null if not found + */ + load(key: string): Promise; + + /** + * List all available index keys. + * + * @returns Array of index keys that can be loaded + */ + list(): Promise; +} + +/** + * Full store interface for reading and writing index state. + * + * Required by the Indexer for creating and updating indexes. + * Extends IndexStoreReader with save and delete operations. + * + * @example + * ```typescript + * const store: IndexStore = new FilesystemStore(); + * + * // Indexer uses full interface + * await store.save("my-project", indexState); + * + * // Cleanup + * await store.delete("old-project"); + * ``` + */ +export interface IndexStore extends IndexStoreReader { + /** + * Save index state with the given key. + * + * Overwrites any existing state with the same key. + * + * @param key - The index key/name + * @param state - The IndexState to persist + */ + save(key: string, state: IndexState): Promise; + + /** + * Delete index state by key. + * + * No-op if the key doesn't exist. + * + * @param key - The index key/name to delete + */ + delete(key: string): Promise; +} + diff --git a/context-connectors/src/tools/index.ts b/context-connectors/src/tools/index.ts new file mode 100644 index 0000000..5305c1a --- /dev/null +++ b/context-connectors/src/tools/index.ts @@ -0,0 +1,9 @@ +/** + * Tools module exports + */ + +export { search, type SearchResult } from "./search.js"; +export { listFiles, formatListOutput, type ListFilesOptions, type ListFilesResult } from "./list-files.js"; +export { readFile, type ReadFileResult, type ReadFileOptions } from "./read-file.js"; +export type { ToolContext, SearchOptions, FileInfo } from "./types.js"; + diff --git a/context-connectors/src/tools/list-files.test.ts b/context-connectors/src/tools/list-files.test.ts new file mode 100644 index 0000000..124c209 --- /dev/null +++ b/context-connectors/src/tools/list-files.test.ts @@ -0,0 +1,220 @@ +/** + * Tests for listFiles tool + */ + +import { describe, it, expect, vi } from "vitest"; +import type { DirectContext } from "@augmentcode/auggie-sdk"; +import type { Source } from "../sources/types.js"; +import type { ToolContext } from "./types.js"; +import { listFiles, formatListOutput } from "./list-files.js"; +import type { FileInfo } from "../core/types.js"; + +describe("listFiles tool", () => { + // Create mock Source with file/directory entries + const createMockSource = (entries: FileInfo[], directoryHandler?: (dir?: string) => FileInfo[]) => { + const listFilesFn = directoryHandler + ? vi.fn().mockImplementation((dir?: string) => Promise.resolve(directoryHandler(dir))) + : vi.fn().mockResolvedValue(entries); + + return { + type: "filesystem" as const, + listFiles: listFilesFn, + readFile: vi.fn(), + fetchAll: vi.fn(), + fetchChanges: vi.fn(), + getMetadata: vi.fn(), + } as unknown as Source; + }; + + // Create mock DirectContext + const createMockContext = () => { + return { + search: vi.fn(), + } as unknown as DirectContext; + }; + + // Create mock ToolContext + const createToolContext = (source: Source | null): ToolContext => ({ + context: createMockContext(), + source, + state: { + contextState: {} as any, + source: { + type: "filesystem", + identifier: "/test", + syncedAt: new Date().toISOString(), + }, + }, + }); + + it("throws error when source is null", async () => { + const ctx = createToolContext(null); + + await expect(listFiles(ctx)).rejects.toThrow( + "Source not configured. Cannot list files in search-only mode." + ); + }); + + it("returns file and directory entries from source", async () => { + const mockSource = createMockSource([ + { path: "src", type: "directory" }, + { path: "README.md", type: "file" }, + ]); + const ctx = createToolContext(mockSource); + + // With default depth=2, it recurses into directories + // Use depth=1 to get only immediate children (original behavior) + const entries = await listFiles(ctx, { depth: 1 }); + + expect(entries).toHaveLength(2); + expect(entries).toContainEqual({ path: "README.md", type: "file" }); + expect(entries).toContainEqual({ path: "src", type: "directory" }); + expect(mockSource.listFiles).toHaveBeenCalled(); + }); + + it("passes directory parameter to source", async () => { + const mockSource = createMockSource([], (dir?: string) => { + if (dir === "src") { + return [ + { path: "src/index.ts", type: "file" }, + { path: "src/utils.ts", type: "file" }, + ]; + } + return [ + { path: "src", type: "directory" }, + { path: "README.md", type: "file" }, + ]; + }); + const ctx = createToolContext(mockSource); + + const entries = await listFiles(ctx, { directory: "src" }); + + expect(entries).toHaveLength(2); + expect(entries[0].path).toBe("src/index.ts"); + expect(mockSource.listFiles).toHaveBeenCalledWith("src"); + }); + + it("filters by pattern (matches filename only)", async () => { + const mockSource = createMockSource([ + { path: "src/index.ts", type: "file" }, + { path: "src/utils.ts", type: "file" }, + { path: "src/helpers", type: "directory" }, + ]); + const ctx = createToolContext(mockSource); + + // Use depth=1 to avoid recursive listing for simpler test + const entries = await listFiles(ctx, { pattern: "*.ts", depth: 1 }); + + expect(entries).toHaveLength(2); + expect(entries.every((e) => e.path.endsWith(".ts"))).toBe(true); + }); + + it("supports path-based patterns with matchBase", async () => { + const mockSource = createMockSource([ + { path: "src/index.ts", type: "file" }, + { path: "src/utils.ts", type: "file" }, + { path: "lib/helper.ts", type: "file" }, + ]); + const ctx = createToolContext(mockSource); + + // Pattern with path should match full path + const entries = await listFiles(ctx, { pattern: "src/*.ts", depth: 1 }); + + expect(entries).toHaveLength(2); + expect(entries.every((e) => e.path.startsWith("src/"))).toBe(true); + }); + + it("returns empty array when no entries match pattern", async () => { + const mockSource = createMockSource([ + { path: "src/index.ts", type: "file" }, + { path: "README.md", type: "file" }, + ]); + const ctx = createToolContext(mockSource); + + const entries = await listFiles(ctx, { pattern: "*.py" }); + + expect(entries).toHaveLength(0); + }); + + it("returns all entries when pattern is not provided", async () => { + const mockSource = createMockSource([ + { path: "src", type: "directory" }, + { path: "README.md", type: "file" }, + { path: "package.json", type: "file" }, + ]); + const ctx = createToolContext(mockSource); + + // Use depth=1 to avoid recursive listing for simpler test + const entries = await listFiles(ctx, { depth: 1 }); + + expect(entries).toHaveLength(3); + }); + + it("recursively lists entries with default depth", async () => { + // Mock source that returns different results for different directories + const mockSource = createMockSource([], (dir?: string) => { + if (dir === "src") { + return [ + { path: "src/index.ts", type: "file" }, + ]; + } + return [ + { path: "src", type: "directory" }, + { path: "README.md", type: "file" }, + ]; + }); + const ctx = createToolContext(mockSource); + + // Default depth=2 should recurse into src/ + const entries = await listFiles(ctx); + + expect(entries).toHaveLength(3); // src, README.md, src/index.ts + expect(entries).toContainEqual({ path: "src", type: "directory" }); + expect(entries).toContainEqual({ path: "README.md", type: "file" }); + expect(entries).toContainEqual({ path: "src/index.ts", type: "file" }); + }); +}); + +describe("formatListOutput", () => { + it("returns 'No files found.' for empty list", () => { + const output = formatListOutput([]); + expect(output).toBe("No files found."); + }); + + it("includes header with default options", () => { + const entries: FileInfo[] = [ + { path: "src", type: "directory" }, + { path: "README.md", type: "file" }, + ]; + const output = formatListOutput(entries); + + expect(output).toContain("files and directories up to 2 levels deep"); + expect(output).toContain("the root directory"); + expect(output).toContain("excluding hidden items"); + expect(output).toContain("src [directory]"); + expect(output).toContain("README.md [file]"); + }); + + it("includes header with custom directory", () => { + const entries: FileInfo[] = [{ path: "src/index.ts", type: "file" }]; + const output = formatListOutput(entries, { directory: "src" }); + + expect(output).toContain("in src"); + }); + + it("describes depth=1 as immediate children", () => { + const entries: FileInfo[] = [{ path: "file.ts", type: "file" }]; + const output = formatListOutput(entries, { depth: 1 }); + + expect(output).toContain("immediate children"); + expect(output).not.toContain("levels deep"); + }); + + it("describes showHidden correctly", () => { + const entries: FileInfo[] = [{ path: ".hidden", type: "file" }]; + const output = formatListOutput(entries, { showHidden: true }); + + expect(output).toContain("including hidden items"); + }); +}); + diff --git a/context-connectors/src/tools/list-files.ts b/context-connectors/src/tools/list-files.ts new file mode 100644 index 0000000..5ce6d7c --- /dev/null +++ b/context-connectors/src/tools/list-files.ts @@ -0,0 +1,222 @@ +/** + * List files tool - List files from a source. + * + * Provides file listing functionality with: + * - Recursive depth control + * - Glob pattern filtering + * - Output truncation + * - Hidden file filtering + * + * Requires a Source to be configured in the tool context. + * + * @module tools/list-files + */ + +import type { FileInfo } from "../core/types.js"; +import type { ToolContext } from "./types.js"; + +/** Default maximum output length in characters */ +const DEFAULT_MAX_OUTPUT = 50000; + +/** Default directory depth */ +const DEFAULT_DEPTH = 2; + +/** + * Options for listing files. + */ +export interface ListFilesOptions { + /** + * Directory to list (default: root ""). + * @example "src", "src/utils" + */ + directory?: string; + /** + * Glob pattern to filter results. + * Uses minimatch for pattern matching. + * @example "*.ts", "*.json" + */ + pattern?: string; + /** + * Maximum depth to recurse into subdirectories. + * 1 = immediate children only, 2 = one level of subdirectories, etc. + * @default 2 + */ + depth?: number; + /** + * Whether to include hidden files (starting with .). + * @default false + */ + showHidden?: boolean; + /** + * Maximum characters in output. + * @default 50000 + */ + maxOutputLength?: number; +} + +/** + * Result from listing files. + */ +export interface ListFilesResult { + /** Array of file/directory entries */ + entries: FileInfo[]; + /** Whether output was truncated */ + truncated?: boolean; + /** Number of entries omitted due to truncation */ + omittedCount?: number; +} + +/** + * Format list entries as text with a descriptive header. + * + * @param entries - Array of file/directory entries + * @param options - The options used for listing (for header context) + * @returns Formatted string with header and entries + */ +export function formatListOutput( + entries: FileInfo[], + options?: ListFilesOptions +): string { + if (entries.length === 0) { + return "No files found."; + } + + const directory = options?.directory || "the root directory"; + const depth = options?.depth ?? DEFAULT_DEPTH; + const showHidden = options?.showHidden ?? false; + + // Build header with proper grammar + const depthDesc = depth === 1 + ? "immediate children" + : `files and directories up to ${depth} levels deep`; + const hiddenDesc = showHidden ? "including" : "excluding"; + const header = `Here are the ${depthDesc} in ${directory}, ${hiddenDesc} hidden items:\n`; + + const body = entries.map((e) => `${e.path} [${e.type}]`).join("\n"); + + return header + body; +} + +/** + * List files and directories from the source with depth control. + * + * This function requires a Source to be configured in the context. + * When called in search-only mode (no Source), it throws an error. + * + * Features: + * - Recursive listing up to specified depth (default: 2) + * - Optional glob pattern filtering + * - Hidden file filtering + * - Output truncation + * + * @param ctx - Tool context (must have source configured) + * @param options - Optional filter and depth options + * @returns Array of file/directory info objects + * @throws Error if no Source is configured + * + * @example + * ```typescript + * // List with default depth (2 levels) + * const files = await listFiles(ctx); + * + * // List only immediate children + * const shallow = await listFiles(ctx, { depth: 1 }); + * + * // List deeper with pattern filter + * const tsFiles = await listFiles(ctx, { + * directory: "src", + * pattern: "*.ts", + * depth: 3, + * }); + * ``` + */ +export async function listFiles( + ctx: ToolContext, + options?: ListFilesOptions +): Promise { + if (!ctx.source) { + throw new Error("Source not configured. Cannot list files in search-only mode."); + } + + const { + directory = "", + pattern, + depth = DEFAULT_DEPTH, + showHidden = false, + maxOutputLength = DEFAULT_MAX_OUTPUT, + } = options ?? {}; + + // Collect entries recursively up to depth + const allEntries: FileInfo[] = []; + await collectEntries(ctx, directory, depth, showHidden, allEntries); + + // Apply pattern filter if specified + let filteredEntries = allEntries; + if (pattern) { + const { minimatch } = await import("minimatch"); + // Use matchBase to allow "*.ts" to match basename while "src/*.ts" matches full path + filteredEntries = allEntries.filter((f) => + minimatch(f.path, pattern, { matchBase: true }) + ); + } + + // Sort entries alphabetically + filteredEntries.sort((a, b) => a.path.localeCompare(b.path)); + + // Apply truncation based on output length + let truncatedEntries = filteredEntries; + let truncated = false; + let omittedCount = 0; + + // Estimate output size (path + type annotation + newline) + let estimatedSize = 0; + for (let i = 0; i < filteredEntries.length; i++) { + const entry = filteredEntries[i]; + const entrySize = entry.path.length + entry.type.length + 5; // " [type]\n" + if (estimatedSize + entrySize > maxOutputLength) { + truncatedEntries = filteredEntries.slice(0, i); + omittedCount = filteredEntries.length - i; + truncated = true; + break; + } + estimatedSize += entrySize; + } + + // Add truncation info to last entry if needed (for display purposes) + if (truncated && truncatedEntries.length > 0) { + // The caller can check the array length vs reported total + } + + return truncatedEntries; +} + +/** + * Recursively collect entries up to specified depth. + */ +async function collectEntries( + ctx: ToolContext, + directory: string, + remainingDepth: number, + showHidden: boolean, + results: FileInfo[] +): Promise { + if (remainingDepth <= 0 || !ctx.source) return; + + const entries = await ctx.source.listFiles(directory); + + for (const entry of entries) { + // Skip hidden files unless requested + const name = entry.path.split("/").pop() || ""; + if (!showHidden && name.startsWith(".")) { + continue; + } + + results.push(entry); + + // Recurse into directories + if (entry.type === "directory" && remainingDepth > 1) { + await collectEntries(ctx, entry.path, remainingDepth - 1, showHidden, results); + } + } +} + diff --git a/context-connectors/src/tools/read-file.test.ts b/context-connectors/src/tools/read-file.test.ts new file mode 100644 index 0000000..a425ed1 --- /dev/null +++ b/context-connectors/src/tools/read-file.test.ts @@ -0,0 +1,140 @@ +/** + * Tests for readFile tool + */ + +import { describe, it, expect, vi } from "vitest"; +import type { DirectContext } from "@augmentcode/auggie-sdk"; +import type { Source } from "../sources/types.js"; +import type { ToolContext } from "./types.js"; +import { readFile } from "./read-file.js"; + +describe("readFile tool", () => { + // Create mock Source + const createMockSource = (fileContents: Map) => { + return { + type: "filesystem" as const, + readFile: vi.fn().mockImplementation((path: string) => { + return Promise.resolve(fileContents.get(path) ?? null); + }), + listFiles: vi.fn(), + fetchAll: vi.fn(), + fetchChanges: vi.fn(), + getMetadata: vi.fn(), + } as unknown as Source; + }; + + // Create mock DirectContext + const createMockContext = () => { + return { + search: vi.fn(), + } as unknown as DirectContext; + }; + + // Create mock ToolContext + const createToolContext = (source: Source | null): ToolContext => ({ + context: createMockContext(), + source, + state: { + contextState: {} as any, + source: { + type: "filesystem", + identifier: "/test", + syncedAt: new Date().toISOString(), + }, + }, + }); + + it("throws error when source is null", async () => { + const ctx = createToolContext(null); + + await expect(readFile(ctx, "file.ts")).rejects.toThrow( + "Source not configured. Cannot read files in search-only mode." + ); + }); + + it("returns file contents with line numbers by default", async () => { + const mockSource = createMockSource( + new Map([["src/index.ts", "export const foo = 1;"]]) + ); + const ctx = createToolContext(mockSource); + + const result = await readFile(ctx, "src/index.ts"); + + expect(result.path).toBe("src/index.ts"); + expect(result.contents).toContain("cat -n"); + expect(result.contents).toContain(" 1\t"); + expect(result.contents).toContain("export const foo = 1;"); + expect(result.contents).toContain("Total lines in file: 1"); + expect(result.totalLines).toBe(1); + expect(result.error).toBeUndefined(); + }); + + it("returns raw contents when line numbers disabled", async () => { + const mockSource = createMockSource( + new Map([["src/index.ts", "export const foo = 1;"]]) + ); + const ctx = createToolContext(mockSource); + + const result = await readFile(ctx, "src/index.ts", { includeLineNumbers: false }); + + expect(result.path).toBe("src/index.ts"); + expect(result.contents).toBe("export const foo = 1;"); + expect(result.error).toBeUndefined(); + }); + + it("respects view range", async () => { + const mockSource = createMockSource( + new Map([["src/index.ts", "line1\nline2\nline3\nline4\nline5"]]) + ); + const ctx = createToolContext(mockSource); + + const result = await readFile(ctx, "src/index.ts", { + startLine: 2, + endLine: 4, + includeLineNumbers: false, + }); + + expect(result.contents).toBe("line2\nline3\nline4"); + expect(result.totalLines).toBe(5); + }); + + it("performs regex search with context", async () => { + const mockSource = createMockSource( + new Map([["src/index.ts", "line1\nline2\nmatch\nline4\nline5"]]) + ); + const ctx = createToolContext(mockSource); + + const result = await readFile(ctx, "src/index.ts", { + searchPattern: "match", + contextLinesBefore: 1, + contextLinesAfter: 1, + }); + + expect(result.contents).toContain("match"); + expect(result.contents).toContain("line2"); // context before + expect(result.contents).toContain("line4"); // context after + }); + + it("returns error for missing file", async () => { + const mockSource = createMockSource(new Map()); + const ctx = createToolContext(mockSource); + + const result = await readFile(ctx, "nonexistent.ts"); + + expect(result.path).toBe("nonexistent.ts"); + expect(result.contents).toBeNull(); + expect(result.error).toBe("File not found or not readable"); + }); + + it("calls source.readFile with correct path", async () => { + const mockSource = createMockSource( + new Map([["deep/nested/file.ts", "content"]]) + ); + const ctx = createToolContext(mockSource); + + await readFile(ctx, "deep/nested/file.ts"); + + expect(mockSource.readFile).toHaveBeenCalledWith("deep/nested/file.ts"); + }); +}); + diff --git a/context-connectors/src/tools/read-file.ts b/context-connectors/src/tools/read-file.ts new file mode 100644 index 0000000..e313bb3 --- /dev/null +++ b/context-connectors/src/tools/read-file.ts @@ -0,0 +1,355 @@ +/** + * Read file tool - Read a single file from a source. + * + * Provides file reading functionality for the readFile tool with: + * - Line numbers (cat -n format) + * - View range (partial file reading) + * - Output truncation + * - Regex search with context lines + * - Path auto-correction suggestions + * + * Requires a Source to be configured in the tool context. + * + * @module tools/read-file + */ + +import type { ToolContext } from "./types.js"; + +/** Default maximum output length in characters */ +const DEFAULT_MAX_OUTPUT = 50000; + +/** Truncation message appended when output is clipped */ +const TRUNCATION_MESSAGE = "\nTo save on context only part of this file has been shown to you."; + +/** + * Options for reading a file. + */ +export interface ReadFileOptions { + /** + * First line to read (1-based, inclusive). + * @default 1 + */ + startLine?: number; + /** + * Last line to read (1-based, inclusive). Use -1 for end of file. + * @default -1 + */ + endLine?: number; + /** + * Include line numbers in output (cat -n format). + * @default true + */ + includeLineNumbers?: boolean; + /** + * Maximum characters in output. Truncates with message if exceeded. + * @default 50000 + */ + maxOutputLength?: number; + /** + * Regex pattern to search for within the file. + * When specified, only matching lines (with context) are returned. + */ + searchPattern?: string; + /** + * Case-sensitive regex matching. + * @default false + */ + caseSensitive?: boolean; + /** + * Lines of context to show before each match. + * @default 5 + */ + contextLinesBefore?: number; + /** + * Lines of context to show after each match. + * @default 5 + */ + contextLinesAfter?: number; +} + +/** + * Result from reading a file. + */ +export interface ReadFileResult { + /** The path that was requested */ + path: string; + /** Formatted file contents if successful, null if not found */ + contents: string | null; + /** Total number of lines in the file */ + totalLines?: number; + /** Whether output was truncated */ + truncated?: boolean; + /** Error message if the file couldn't be read */ + error?: string; + /** Suggested similar paths if file not found */ + suggestions?: string[]; +} + +/** + * Format a line with line number (cat -n format). + * Line numbers are right-padded to 6 characters. + */ +function formatLine(lineNum: number, content: string): string { + return `${String(lineNum).padStart(6, " ")}\t${content}`; +} + +/** + * Truncate output if it exceeds maxLength. + */ +function maybeTruncate( + output: string, + maxLength: number +): { text: string; truncated: boolean } { + if (output.length <= maxLength) { + return { text: output, truncated: false }; + } + const truncateAt = maxLength - TRUNCATION_MESSAGE.length; + return { + text: output.slice(0, truncateAt) + TRUNCATION_MESSAGE, + truncated: true, + }; +} + +/** + * Validate and normalize view range. + */ +function normalizeRange( + startLine: number | undefined, + endLine: number | undefined, + totalLines: number +): { start: number; end: number } { + let start = startLine ?? 1; + let end = endLine ?? -1; + + // Clamp start + if (start < 1) start = 1; + if (start > totalLines) start = totalLines; + + // Handle -1 as "end of file" + if (end === -1) end = totalLines; + + // Clamp end + if (end < start) end = start; + if (end > totalLines) end = totalLines; + + return { start, end }; +} + +/** + * Perform regex search and return matching lines with context. + */ +function searchWithContext( + lines: string[], + pattern: string, + caseSensitive: boolean, + contextBefore: number, + contextAfter: number +): { lineNumbers: Set; matchingLines: Set } { + const flags = caseSensitive ? "g" : "gi"; + const regex = new RegExp(pattern, flags); + + const matchingLines = new Set(); + const lineNumbers = new Set(); + + // Find all matching lines + for (let i = 0; i < lines.length; i++) { + if (regex.test(lines[i])) { + matchingLines.add(i); + // Add context lines + for (let j = Math.max(0, i - contextBefore); j <= Math.min(lines.length - 1, i + contextAfter); j++) { + lineNumbers.add(j); + } + } + // Reset regex lastIndex for global flag + regex.lastIndex = 0; + } + + return { lineNumbers, matchingLines }; +} + +/** + * Read a single file from the source. + * + * This function requires a Source to be configured in the context. + * When called in search-only mode (no Source), it throws an error. + * + * Features: + * - Line numbers in cat -n format (default: on) + * - View range for partial file reading + * - Output truncation to prevent context overflow + * - Regex search with configurable context lines + * - Path suggestions on file not found + * + * @param ctx - Tool context (must have source configured) + * @param path - Relative path to the file + * @param options - Optional reading options + * @returns Result with contents or error + * @throws Error if no Source is configured + * + * @example + * ```typescript + * // Basic usage with line numbers + * const result = await readFile(ctx, "src/index.ts"); + * + * // Read specific range + * const result = await readFile(ctx, "src/index.ts", { + * startLine: 10, + * endLine: 50, + * }); + * + * // Search within file + * const result = await readFile(ctx, "src/index.ts", { + * searchPattern: "function.*export", + * contextLinesBefore: 3, + * contextLinesAfter: 10, + * }); + * ``` + */ +export async function readFile( + ctx: ToolContext, + path: string, + options: ReadFileOptions = {} +): Promise { + if (!ctx.source) { + throw new Error("Source not configured. Cannot read files in search-only mode."); + } + + const { + includeLineNumbers = true, + maxOutputLength = DEFAULT_MAX_OUTPUT, + searchPattern, + caseSensitive = false, + contextLinesBefore = 5, + contextLinesAfter = 5, + } = options; + + const rawContents = await ctx.source.readFile(path); + + if (rawContents === null) { + // Try to find similar paths for suggestions + const suggestions = await findSimilarPaths(ctx, path); + return { + path, + contents: null, + error: "File not found or not readable", + suggestions: suggestions.length > 0 ? suggestions : undefined, + }; + } + + const lines = rawContents.split("\n"); + const totalLines = lines.length; + + // Normalize view range + const { start, end } = normalizeRange(options.startLine, options.endLine, totalLines); + + let output: string; + + if (searchPattern) { + // Regex search mode + const { lineNumbers, matchingLines } = searchWithContext( + lines, + searchPattern, + caseSensitive, + contextLinesBefore, + contextLinesAfter + ); + + if (lineNumbers.size === 0) { + return { + path, + contents: `No matches found for pattern: ${searchPattern}`, + totalLines, + truncated: false, + }; + } + + // Build output with gaps shown as "..." + const sortedLines = Array.from(lineNumbers).sort((a, b) => a - b); + const outputLines: string[] = []; + let lastLine = -2; // -2 so first line doesn't trigger gap + + for (const lineIdx of sortedLines) { + // Skip lines outside view range + const lineNum = lineIdx + 1; + if (lineNum < start || lineNum > end) continue; + + // Add gap marker if there's a discontinuity + if (lineIdx > lastLine + 1) { + outputLines.push("..."); + } + + // Format line with optional match marker + const prefix = matchingLines.has(lineIdx) ? ">" : " "; + if (includeLineNumbers) { + outputLines.push(`${prefix}${formatLine(lineNum, lines[lineIdx])}`); + } else { + outputLines.push(`${prefix} ${lines[lineIdx]}`); + } + lastLine = lineIdx; + } + + output = `Here's the result of searching for '${searchPattern}' in ${path}:\n${outputLines.join("\n")}\nTotal lines in file: ${totalLines}`; + } else { + // Normal file viewing mode + const selectedLines = lines.slice(start - 1, end); + + if (includeLineNumbers) { + const formattedLines = selectedLines.map((line, idx) => + formatLine(start + idx, line) + ); + output = `Here's the result of running \`cat -n\` on ${path}:\n${formattedLines.join("\n")}\nTotal lines in file: ${totalLines}`; + } else { + output = selectedLines.join("\n"); + } + } + + // Apply truncation + const { text, truncated } = maybeTruncate(output, maxOutputLength); + + return { + path, + contents: text, + totalLines, + truncated, + }; +} + +/** + * Find similar file paths for suggestions. + * Uses filename matching and path similarity. + */ +async function findSimilarPaths(ctx: ToolContext, path: string): Promise { + if (!ctx.source) return []; + + // Extract filename from path + const parts = path.split("/"); + const filename = parts[parts.length - 1]; + + // List files from root and search for similar names + // This is a simplified approach; could be enhanced with LCS ranking + const suggestions: string[] = []; + + try { + // Try to list files from the parent directories + const parentPath = parts.slice(0, -1).join("/"); + const entries = await ctx.source.listFiles(parentPath || undefined); + + for (const entry of entries) { + if (entry.type === "file") { + const entryName = entry.path.split("/").pop() || ""; + // Simple similarity: same extension or contains filename + if ( + entryName.toLowerCase().includes(filename.toLowerCase()) || + filename.toLowerCase().includes(entryName.toLowerCase()) + ) { + suggestions.push(entry.path); + } + } + } + } catch { + // Ignore errors in suggestion finding + } + + return suggestions.slice(0, 5); // Max 5 suggestions +} + diff --git a/context-connectors/src/tools/search.test.ts b/context-connectors/src/tools/search.test.ts new file mode 100644 index 0000000..2a9bd73 --- /dev/null +++ b/context-connectors/src/tools/search.test.ts @@ -0,0 +1,85 @@ +/** + * Tests for search tool + */ + +import { describe, it, expect, vi } from "vitest"; +import type { DirectContext } from "@augmentcode/auggie-sdk"; +import type { ToolContext } from "./types.js"; +import { search } from "./search.js"; + +describe("search tool", () => { + // Create mock DirectContext + const createMockContext = (searchResult: string | undefined) => { + return { + search: vi.fn().mockResolvedValue(searchResult), + } as unknown as DirectContext; + }; + + // Create mock ToolContext + const createToolContext = (context: DirectContext): ToolContext => ({ + context, + source: null, + state: { + contextState: {} as any, + source: { + type: "filesystem", + identifier: "/test", + syncedAt: new Date().toISOString(), + }, + }, + }); + + it("returns results from DirectContext.search", async () => { + const mockContext = createMockContext("Search result: file.ts line 1"); + const ctx = createToolContext(mockContext); + + const result = await search(ctx, "test query"); + + expect(result.query).toBe("test query"); + expect(result.results).toBe("Search result: file.ts line 1"); + expect(mockContext.search).toHaveBeenCalledWith("test query", { + maxOutputLength: undefined, + }); + }); + + it("passes maxOutputLength option", async () => { + const mockContext = createMockContext("Result"); + const ctx = createToolContext(mockContext); + + await search(ctx, "query", { maxOutputLength: 5000 }); + + expect(mockContext.search).toHaveBeenCalledWith("query", { + maxOutputLength: 5000, + }); + }); + + it("returns empty string when search returns undefined", async () => { + const mockContext = createMockContext(undefined); + const ctx = createToolContext(mockContext); + + const result = await search(ctx, "query"); + + expect(result.results).toBe(""); + }); + + it("works without source configured", async () => { + const mockContext = createMockContext("Result"); + const ctx: ToolContext = { + context: mockContext, + source: null, + state: { + contextState: {} as any, + source: { + type: "filesystem", + identifier: "/test", + syncedAt: new Date().toISOString(), + }, + }, + }; + + const result = await search(ctx, "query"); + + expect(result.results).toBe("Result"); + }); +}); + diff --git a/context-connectors/src/tools/search.ts b/context-connectors/src/tools/search.ts new file mode 100644 index 0000000..51fe8ef --- /dev/null +++ b/context-connectors/src/tools/search.ts @@ -0,0 +1,51 @@ +/** + * Search tool - Semantic search across indexed content. + * + * Uses DirectContext to find relevant code snippets based on + * natural language queries. + * + * @module tools/search + */ + +import type { ToolContext, SearchOptions } from "./types.js"; + +/** + * Result from a search operation. + */ +export interface SearchResult { + /** Formatted search results from DirectContext (code snippets with context) */ + results: string; + /** The original query that was searched */ + query: string; +} + +/** + * Search the indexed content using natural language. + * + * This is the core search function used by SearchClient and tool interfaces. + * It delegates to DirectContext.search() and wraps the result. + * + * @param ctx - Tool context containing the DirectContext instance + * @param query - Natural language search query + * @param options - Optional search options (e.g., maxOutputLength) + * @returns Search result containing matching code snippets + * + * @example + * ```typescript + * const result = await search(ctx, "database connection pooling", { + * maxOutputLength: 5000, + * }); + * console.log(result.results); + * ``` + */ +export async function search( + ctx: ToolContext, + query: string, + options?: SearchOptions +): Promise { + const results = await ctx.context.search(query, { + maxOutputLength: options?.maxOutputLength, + }); + return { results: results ?? "", query }; +} + diff --git a/context-connectors/src/tools/types.ts b/context-connectors/src/tools/types.ts new file mode 100644 index 0000000..b07f658 --- /dev/null +++ b/context-connectors/src/tools/types.ts @@ -0,0 +1,65 @@ +/** + * Tool context and types for client tool implementations. + * + * Tools are the low-level functions that power client operations: + * - `search`: Semantic search using DirectContext + * - `listFiles`: List files from the source + * - `readFile`: Read file contents from the source + * + * These tools are used by: + * - SearchClient (programmatic access) + * - MCP Server (Claude Desktop) + * - AI SDK Tools (Vercel AI SDK) + * + * @module tools/types + */ + +import type { DirectContext } from "@augmentcode/auggie-sdk"; +import type { Source } from "../sources/types.js"; +import type { FileInfo, IndexState } from "../core/types.js"; + +// Re-export FileInfo for convenience +export type { FileInfo }; + +/** + * Context passed to tool implementations. + * + * Contains all the resources needed for tool operations: + * - DirectContext for search + * - Source for file operations (optional) + * - IndexState for metadata + * + * @example + * ```typescript + * const ctx: ToolContext = { + * context: directContext, + * source: filesystemSource, // or null for search-only + * state: indexState, + * }; + * + * const result = await search(ctx, "authentication"); + * ``` + */ +export interface ToolContext { + /** DirectContext instance for search operations */ + context: DirectContext; + /** + * Source for file operations. + * Null if client is in search-only mode (no listFiles/readFile). + */ + source: Source | null; + /** The loaded IndexState for metadata access */ + state: IndexState; +} + +/** + * Options for the search tool. + */ +export interface SearchOptions { + /** + * Maximum characters in the search response. + * Useful for limiting context size when used with LLMs. + */ + maxOutputLength?: number; +} + diff --git a/context-connectors/templates/github-workflow.yml b/context-connectors/templates/github-workflow.yml new file mode 100644 index 0000000..7be2c83 --- /dev/null +++ b/context-connectors/templates/github-workflow.yml @@ -0,0 +1,48 @@ +name: Index Repository + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + index: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install context-connectors + run: npm install -g @augmentcode/context-connectors + + - name: Restore index cache + uses: actions/cache@v4 + with: + path: .context-connectors + key: index-${{ github.repository }}-${{ github.ref_name }} + restore-keys: | + index-${{ github.repository }}- + + - name: Index repository + run: | + context-connectors index \ + -s github \ + --owner ${{ github.repository_owner }} \ + --repo ${{ github.event.repository.name }} \ + --ref ${{ github.sha }} \ + -k ${{ github.ref_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + AUGMENT_API_TOKEN: ${{ secrets.AUGMENT_API_TOKEN }} + AUGMENT_API_URL: ${{ secrets.AUGMENT_API_URL }} + + - name: Upload index artifact + uses: actions/upload-artifact@v4 + with: + name: context-index-${{ github.ref_name }} + path: .context-connectors/ + retention-days: 30 + diff --git a/context-connectors/tsconfig.json b/context-connectors/tsconfig.json new file mode 100644 index 0000000..cb6e0de --- /dev/null +++ b/context-connectors/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022"], + "outDir": "dist", + "rootDir": "src", + "strict": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} + diff --git a/examples/typescript-sdk/context/github-action-indexer/src/github-client.ts b/examples/typescript-sdk/context/github-action-indexer/src/github-client.ts index b4cb077..2462f8f 100644 --- a/examples/typescript-sdk/context/github-action-indexer/src/github-client.ts +++ b/examples/typescript-sdk/context/github-action-indexer/src/github-client.ts @@ -319,6 +319,12 @@ export class GitHubClient { /** * Check if the push was a force push + * + * Force push detection cases: + * 1. Compare API fails - base commit no longer exists + * 2. status: "diverged" - histories have diverged + * 3. status: "behind" - head is behind base (revert to older commit) + * 4. behind_by > 0 - additional indicator of non-linear history */ async isForcePush( owner: string, @@ -327,12 +333,25 @@ export class GitHubClient { head: string ): Promise { try { - await this.octokit.repos.compareCommits({ + const { data } = await this.octokit.repos.compareCommits({ owner, repo, base, head, }); + + // Check for non-linear history indicators + // "diverged" means histories have diverged (typical force push) + // "behind" means head is an ancestor of base (revert to older commit) + if (data.status === "diverged" || data.status === "behind") { + return true; + } + + // Additional safety check: behind_by > 0 indicates head is behind base + if (data.behind_by > 0) { + return true; + } + return false; } catch (_error) { // If comparison fails, it's likely a force push