diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..41ed6e5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + groups: + runtime-dependencies: + dependency-type: "production" + development-dependencies: + dependency-type: "development" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 diff --git a/.github/workflows/junie.yml b/.github/workflows/junie.yml index 9860e79..3c67b47 100644 --- a/.github/workflows/junie.yml +++ b/.github/workflows/junie.yml @@ -16,6 +16,6 @@ on: jobs: call-workflow-passing-data: - uses: jetbrains-junie/junie-workflows/.github/workflows/ej-issue.yml@main + uses: jetbrains-junie/junie-workflows/.github/workflows/ej-issue.yml@b4f71597a0a5aaa196d1191efe33f6f77517f792 with: workflow_params: ${{ inputs.workflow_params }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5b76871..c3e0414 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,26 +12,26 @@ on: type: boolean permissions: - contents: write - issues: write - pull-requests: write - id-token: write + contents: read jobs: test: runs-on: ubuntu-latest - if: github.event_name != 'push' || !contains(github.event.head_commit.message, '[skip ci]') + permissions: + contents: read steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + persist-credentials: false - name: Setup Bun - uses: oven-sh/setup-bun@v1 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 with: - bun-version: latest + bun-version: 1.3.14 - name: Install dependencies - run: bun install + run: bun install --frozen-lockfile - name: Build CLI run: bun run build @@ -45,37 +45,44 @@ jobs: release: runs-on: ubuntu-latest needs: test - if: github.ref == 'refs/heads/main' && (github.event_name != 'push' || !contains(github.event.head_commit.message, '[skip ci]')) + permissions: + contents: write + id-token: write + if: github.ref == 'refs/heads/main' && (github.event_name != 'push' || !startsWith(github.event.head_commit.message, 'chore(release):')) + env: + NPM_CONFIG_ACCESS: public + NPM_CONFIG_PROVENANCE: "true" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: fetch-depth: 0 + persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 with: - node-version: 'lts/*' - registry-url: 'https://registry.npmjs.org' + node-version: "22" + registry-url: "https://registry.npmjs.org" - name: Setup Bun - uses: oven-sh/setup-bun@v1 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 with: - bun-version: latest + bun-version: 1.3.14 - name: Install dependencies - run: bun install + run: bun install --frozen-lockfile - name: Build CLI run: bun run build - name: Update npm for OIDC support - run: npm install -g npm@latest + run: npm install -g npm@11.16.0 - name: Publish current tagged version if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_current_version }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | PACKAGE_NAME=$(node --input-type=module -e "import pkg from './package.json' with { type: 'json' }; console.log(pkg.name)") PACKAGE_VERSION=$(node --input-type=module -e "import pkg from './package.json' with { type: 'json' }; console.log(pkg.version)") @@ -100,6 +107,4 @@ jobs: - name: Run semantic-release if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.publish_current_version) }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npx semantic-release + run: bun run release diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e259d1c..834a053 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,81 +2,86 @@ name: Test and Build on: push: - branches: [ main, develop ] + branches: [main, develop] pull_request: - branches: [ main ] + branches: [main] + +permissions: + contents: read jobs: test: runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[skip ci]')" - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Bun - uses: oven-sh/setup-bun@v1 - with: - bun-version: latest - - - name: Install dependencies - run: bun install - - - name: Build CLI - run: bun run build - - - name: Run tests - run: bun test - - - name: Test CLI executable - run: ./dist/index.js --help - - - name: Verify CLI commands - run: | - ./dist/index.js generate --help - ./dist/index.js edit --help - ./dist/index.js generate-sequential --help - ./dist/index.js edit-sequential --help + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + persist-credentials: false + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 + with: + bun-version: 1.3.14 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Build CLI + run: bun run build + + - name: Run tests + run: bun test + + - name: Test CLI executable + run: ./dist/index.js --help + + - name: Verify CLI commands + run: | + ./dist/index.js generate --help + ./dist/index.js edit --help + ./dist/index.js generate-sequential --help + ./dist/index.js edit-sequential --help validate-implementation: runs-on: ubuntu-latest needs: test - + steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Bun - uses: oven-sh/setup-bun@v1 - with: - bun-version: latest - - - name: Install dependencies - run: bun install - - - name: Build CLI - run: bun run build - - - name: Test sync mode parameter - run: | - # Test that sync mode is available in all commands - ./dist/index.js generate --help | grep -q "sync.*mode" || exit 1 - ./dist/index.js edit --help | grep -q "sync.*mode" || exit 1 - ./dist/index.js generate-sequential --help | grep -q "sync.*mode" || exit 1 - ./dist/index.js edit-sequential --help | grep -q "sync.*mode" || exit 1 - - - name: Test file upload support - run: | - # Test that file path support is documented - ./dist/index.js edit --help | grep -q "file paths" || exit 1 - ./dist/index.js edit-sequential --help | grep -q "file paths" || exit 1 - - - name: Test parameter validation - env: - WAVESPEED_API_KEY: test-key - run: | - # Test validation without API calls - ! ./dist/index.js edit 2>&1 | grep -q "Prompt is required" || echo "✓ Prompt validation works" - ! ./dist/index.js edit -p "test" 2>&1 | grep -q "Images are required" || echo "✓ Images validation works" - ! ./dist/index.js edit -p "test" -i "https://example.com/test.jpg" -s "invalid" 2>&1 | grep -q "Size must be WIDTH" || echo "✓ Size validation works" \ No newline at end of file + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + persist-credentials: false + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 + with: + bun-version: 1.3.14 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Build CLI + run: bun run build + + - name: Test sync mode parameter + run: | + # Test that sync mode is available in all commands + ./dist/index.js generate --help | grep -q "sync.*mode" || exit 1 + ./dist/index.js edit --help | grep -q "sync.*mode" || exit 1 + ./dist/index.js generate-sequential --help | grep -q "sync.*mode" || exit 1 + ./dist/index.js edit-sequential --help | grep -q "sync.*mode" || exit 1 + + - name: Test file upload support + run: | + # Test that file path support is documented for the local CLI + ./dist/index.js edit --help | grep -q "file paths" || exit 1 + ./dist/index.js edit-sequential --help | grep -q "file paths" || exit 1 + + - name: Test parameter validation + env: + WAVESPEED_API_KEY: test-key + run: | + # Test validation without API calls + ./dist/index.js edit 2>&1 | grep -q "Prompt is required" + ./dist/index.js edit -p "test" 2>&1 | grep -q "Images are required" + ./dist/index.js edit -p "test" -i "https://example.com/test.jpg" -s "invalid" 2>&1 | grep -q "Size must be WIDTH" diff --git a/.gitignore b/.gitignore index 4087d6a..88cb579 100644 --- a/.gitignore +++ b/.gitignore @@ -18,16 +18,21 @@ logs _.log report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json -# dotenv environment variable files +# dotenv and local credential/config files .env -.env.development.local -.env.test.local -.env.production.local -.env.local +.env.* +!.env.example +.envrc +.npmrc +.wavespeedrc* +wavespeed.config.* +!.wavespeedrc.example +!wavespeed.config.example.* # caches .eslintcache .cache +.pi/orchestrator/runs/ *.tsbuildinfo # IntelliJ based IDEs diff --git a/.releaserc.json b/.releaserc.json index b2ea99d..3398286 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -27,7 +27,7 @@ "@semantic-release/github", ["@semantic-release/git", { "assets": ["CHANGELOG.md", "package.json"], - "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}" }] ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index a50f3fc..8bf5dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ All notable changes to wavespeed-cli will be documented in this file. ### Bug Fixes * unify CLI and MCP model resolution and submit routing -* derive submit URLs from the selected model instead of fixed Seedream V4 endpoints +* derive submit URLs from the selected model instead of fixed legacy default-model endpoints * harden config model resolution for canonical ids and cache fallback behavior * make live MCP E2E opt-in and stabilize home-config test isolation diff --git a/README.md b/README.md index c0e18cf..620723c 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,14 @@ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-Runtime-f9f1e1.svg)](https://bun.sh) -A powerful command-line interface for [Wavespeed AI](https://wavespeed.ai)'s image generation and editing APIs. Generate stunning images from text prompts, edit existing images, and create consistent image sequences—all from your terminal. +A Wavespeed AI image generation toolkit for both agentic workflows and terminal use. Run it as a stdio MCP server for coding agents, or use the `wavespeed` CLI directly to generate images from text prompts, edit existing images, and create consistent image sequences. ## Table of Contents - [Quick Start](#quick-start) - [Features](#features) - [Installation](#installation) +- [Agent Skill and MCP Usage](#agent-skill-and-mcp-usage) - [Configuration](#configuration) - [Usage](#usage) - [Multi-model Selection](#multi-model-selection) @@ -37,7 +38,10 @@ bun install && bun run build && bun link # Set your API key export WAVESPEED_API_KEY="your_api_key_here" -# Generate your first image +# Start the MCP server for agentic coding tools +wavespeed mcp + +# Or generate your first image from the CLI wavespeed generate --prompt "a photorealistic cat sitting on a windowsill" ``` @@ -51,12 +55,14 @@ wavespeed generate --prompt "a photorealistic cat sitting on a windowsill" | **Image Editing** | Modify existing images with natural language prompts | | **Sequential Generation** | Generate consistent image sequences for animations or storyboards | | **Sequential Editing** | Edit multiple images while maintaining visual consistency | +| **MCP Server** | Expose image generation/editing tools to agentic coding environments | ### Developer Experience - **Auto-Download**: Images are automatically downloaded when generation completes - **Base64 Support**: Seamlessly decode and save base64-encoded image outputs -- **Multi-Model Support**: Switch between different AI providers and models via configuration +- **MCP-first Agent Workflows**: Ships a standard Agent Skill and stdio MCP server for coding agents +- **Multi-Model Support**: Discover Wavespeed's current model catalog and switch models via config or `--model` - **Flexible Configuration**: JSON/YAML config files with environment variable interpolation - **Comprehensive Validation**: Clear error messages with actionable guidance @@ -90,7 +96,7 @@ bun link ### Set Up Your API Key -The CLI requires a Wavespeed API key. Set it as an environment variable: +The MCP server and CLI require a Wavespeed API key. Set it as an environment variable: **Bash/Zsh:** ```bash @@ -103,16 +109,54 @@ export WAVESPEED_API_KEY="your_api_key_here" set -Ux WAVESPEED_API_KEY "your_api_key_here" ``` +## Agent Skill and MCP Usage + +This package is designed to be used by agentic coding tools through MCP first, with CLI commands as a fallback. + +### Start the MCP server + +```bash +wavespeed mcp +``` + +The stdio MCP server exposes tools for: + +- `list_models` - discover current Wavespeed models and model types +- `generate` - text-to-image generation +- `edit` - image-to-image editing +- `generate_sequential` - consistent multi-image sequences +- `edit_sequential` - sequential image editing + +Use MCP output mode `paths` when an agent should save files, `urls` for compact responses, and `base64` only when inline image bytes are required. + +MCP safety defaults: + +- Local input file paths are disabled by default for MCP image-edit tools. Set `WAVESPEED_MCP_INPUT_DIR=/trusted/images` to allow local image reads only under that directory. +- `paths` output is confined to `WAVESPEED_MCP_OUTPUT_DIR` when set, otherwise the MCP server working directory. Relative `outputDir` values are resolved under that root. +- Custom/non-Wavespeed API base URLs in MCP mode require `WAVESPEED_ALLOW_CUSTOM_API_BASE_URL=1`. + +### Agent Skill + +A portable Agent Skill is included at: + +```text +skills/wavespeed-image-generation/SKILL.md +``` + +Install or reference that skill from your agent harness to teach agents to prefer the Wavespeed MCP server, discover models at runtime, save outputs safely, and fall back to CLI commands when MCP is unavailable. + ## Configuration ### Default Configuration -By default, the CLI uses the built-in Wavespeed Seedream V4 configuration with `WAVESPEED_API_KEY` from your environment. No config file is required for basic usage. +By default, the CLI and MCP server use the built-in Wavespeed configuration with `WAVESPEED_API_KEY` from your environment. No config file is required for basic usage. -If no config file is present, all commands: +If no config file is present, commands: - Use `https://api.wavespeed.ai` - Use `WAVESPEED_API_KEY` -- Target the Bytedance Seedream V4 endpoints +- Resolve to the built-in default model alias for backwards-compatible basic generation/editing + +For best results, use `wavespeed models` or the MCP `list_models` tool to discover current Wavespeed models and pass a model id explicitly with `--model` or the MCP `model` argument. ### Config file discovery (multi-model support) @@ -147,26 +191,29 @@ Minimal JSON example: ```json { "models": { - "seedream-v4": { + "fast-image": { "provider": "wavespeed", "apiBaseUrl": "https://api.wavespeed.ai", "apiKeyEnv": "WAVESPEED_API_KEY", - "modelName": "bytedance/seedream-v4" + "modelName": "wavespeed-ai/flux-dev" }, - "my-alt-model": { + "image-editor": { + "provider": "wavespeed", + "apiBaseUrl": "https://api.wavespeed.ai", + "apiKeyEnv": "WAVESPEED_API_KEY", + "modelName": "google/nano-banana-2" + }, + "my-gateway-model": { "provider": "openai-compatible", "apiBaseUrl": "https://api.my-gateway.example", "apiKeyEnv": "MY_GATEWAY_API_KEY", - "modelName": "bytedance/seedream-v4" + "modelName": "provider/model-name" } }, "defaults": { - "globalModel": "seedream-v4", "commands": { - "generate": "seedream-v4", - "edit": "seedream-v4", - "generate-sequential": "seedream-v4", - "edit-sequential": "seedream-v4" + "generate": "fast-image", + "edit": "image-editor" } } } @@ -185,7 +232,7 @@ Key points: - Environment variable name holding the API key. - Defaults to `WAVESPEED_API_KEY` for `wavespeed` if omitted. - `modelName`: - - Base remote model identifier for configured aliases (e.g. `"bytedance/seedream-v4"`). + - Base remote model identifier for configured aliases (e.g. `"wavespeed-ai/flux-dev"`). - The CLI/MCP derive command-specific routes such as `/edit` or `/sequential` from this value. - If you want to use a fully canonical, operation-specific model ID such as `"google/nano-banana-2/text-to-image"`, pass it directly with `--model` instead of storing it as an ergonomic cross-command alias. - `type`, `requestDefaults`: @@ -228,6 +275,15 @@ Example: If `GATEWAY_BASE_URL` or `GATEWAY_API_KEY` are missing at runtime when this model is selected, the CLI exits with a configuration/secret error as described below. +### Custom endpoint safety + +To reduce accidental credential leaks from project-local config files: + +- `apiBaseUrl` must be `https://` by default and must not include credentials, query strings, or fragments. +- Localhost/private-network API bases are blocked by default. Use `WAVESPEED_ALLOW_INSECURE_API_BASE_URL=1` only for trusted local testing. +- Sending `WAVESPEED_API_KEY` (or a `wavespeed` provider model) to a non-Wavespeed host is blocked unless `WAVESPEED_ALLOW_CUSTOM_API_BASE_URL=1` is set. +- In MCP mode, any non-Wavespeed API base requires `WAVESPEED_ALLOW_CUSTOM_API_BASE_URL=1`. + ## Usage ### Basic Commands @@ -326,7 +382,7 @@ Model selection for each command follows: 1. CLI flag `--model `: - Accepts: - a configured model alias from `models` - - a built-in registry id such as `seedream-v4` + - a built-in registry id shown by `wavespeed models` - a raw Wavespeed API model id such as `google/nano-banana-2/text-to-image` - Unknown plain ids still fail with configuration error (exit code 3). 2. Command-level default: @@ -334,7 +390,7 @@ Model selection for each command follows: 3. Global default: - `defaults.globalModel` if present. 4. Built-in fallback: - - Wavespeed Seedream V4 using `WAVESPEED_API_KEY`. + - the built-in Wavespeed default model using `WAVESPEED_API_KEY`. - If `WAVESPEED_API_KEY` is missing here: exit code 2. This behavior is implemented centrally and used by all commands. @@ -355,57 +411,29 @@ wavespeed generate --model google/nano-banana-2/text-to-image --prompt "A dragon wavespeed edit --prompt "style it" --images "https://example.com/img.png" ``` -## Listing configured models +## Listing models -The CLI provides global flags: +Use the `models` subcommand to see configured aliases, built-in registry entries, and live Wavespeed API models when an API key is available: ```bash -wavespeed --list-models -wavespeed --list-models-json +wavespeed models +wavespeed models --json ``` -Behavior: - -- When either flag is present: - - The CLI lists models and exits with code 0. - - If used alongside a subcommand, the listing still runs and the command is not executed. - -Text output example: - -```text -Config source: /path/to/.wavespeedrc.json -seedream-v4 * [cmd=generate,edit] provider=wavespeed baseUrl=https://api.wavespeed.ai modelName=bytedance/seedream-v4 keyEnv=WAVESPEED_API_KEY -my-alt-model [cmd=generate] provider=openai-compatible baseUrl=https://api.my-gateway.example modelName=bytedance/seedream-v4 keyEnv=MY_GATEWAY_API_KEY -``` +Text output includes: -- `*` marks the global default model. -- `[cmd=...]` lists commands for which this model is the default. +- Configured local aliases from `.wavespeedrc*` / `wavespeed.config.*` +- Built-in registry entries +- Live API models fetched from Wavespeed when `WAVESPEED_API_KEY` is set +- Model type and price metadata when available -JSON output example: +JSON output is best for agentic callers and scripts: ```bash -wavespeed --list-models-json +wavespeed models --json ``` -```json -{ - "source": "/path/to/.wavespeedrc.json", - "models": [ - { - "id": "seedream-v4", - "provider": "wavespeed", - "apiBaseUrl": "https://api.wavespeed.ai", - "modelName": "bytedance/seedream-v4", - "apiKeyEnv": "WAVESPEED_API_KEY", - "isDefaultGlobal": true, - "defaultForCommands": ["generate", "edit"] - } - ] -} -``` - -If no config file is present: -- Listing shows a single built-in Seedream V4 model as the global default. +The MCP `list_models` tool provides the same discovery path for agents and should be preferred when using the MCP server. ## Error Handling and Exit Codes @@ -422,7 +450,7 @@ The CLI validates inputs and configuration and returns: Typical messages include guidance such as: - Unknown model: - - "Unknown model 'X'. Use --list-models to see available models." + - "Unknown model 'X'. Use `wavespeed models` to see available models." - Invalid config defaults: - "defaults.globalModel 'X' does not exist in models" - Missing secrets: @@ -490,7 +518,7 @@ bun run src/index.ts generate --prompt "test" ``` **"Unknown model 'X'"** -- Check your config file with `wavespeed --list-models` +- Check your config file and live model catalog with `wavespeed models` - Ensure the model ID matches exactly (case-sensitive) **"Invalid size format"** @@ -504,19 +532,15 @@ bun run src/index.ts generate --prompt "test" ## API Documentation -For detailed API specifications, see: +For current model-specific API details, use: -- [Bytedance Seedream V4 (Generate)](https://wavespeed.ai/docs/docs-api/bytedance/bytedance-seedream-v4) -- [Bytedance Seedream V4 Edit](https://wavespeed.ai/docs/docs-api/bytedance/bytedance-seedream-v4-edit) -- [Bytedance Seedream V4 Sequential](https://wavespeed.ai/docs/docs-api/bytedance/bytedance-seedream-v4-sequential) -- [Bytedance Seedream V4 Edit Sequential](https://wavespeed.ai/docs/docs-api/bytedance/bytedance-seedream-v4-edit-sequential) +- [Wavespeed API documentation](https://wavespeed.ai/docs) +- [Wavespeed model catalog](https://wavespeed.ai/models) +- `wavespeed models` or MCP `list_models` for runtime model ids, types, and prices ## Pricing -From the API documentation: - -- Standard generation: $0.027 per image -- Sequential generation: $0.027 × `max_images` +Pricing is model-specific and changes as Wavespeed adds models. Use `wavespeed models`, MCP `list_models`, or the Wavespeed model pages for current pricing before running large batches. ## Contributing diff --git a/WARP.md b/WARP.md deleted file mode 100644 index 97c4711..0000000 --- a/WARP.md +++ /dev/null @@ -1,130 +0,0 @@ -# WARP.md - -This file provides guidance to WARP (warp.dev) when working with code in this repository. - -## Essential Development Commands - -```bash -# Install dependencies -bun install - -# Run in development mode with hot reload -bun run dev - -# Build for production -bun run build - -# Link CLI globally (makes 'wavespeed' command available system-wide) -bun link -``` - -## High-Level Architecture - -This is a TypeScript CLI for the Wavespeed AI Bytedance Seedream V4 API, built with Bun. - -### Command Structure -The CLI exposes 4 commands via Commander.js: -- `generate`: Text-to-image generation -- `edit`: Image-to-image editing -- `generate-sequential`: Generate consistent image sequences -- `edit-sequential`: Edit images while maintaining consistency - -### API Client Layer (`src/api/`) -- Authenticates with `WAVESPEED_API_KEY` environment variable -- Sends HTTP requests to `https://api.wavespeed.ai` -- Creates tasks and returns task IDs immediately -- Results retrieved by polling `/api/v3/predictions/{id}/result` - -### Polling Mechanism (`src/utils/polling.ts`) -- **Default interval**: 2.5 seconds -- **Timeout**: 10 minutes -- **Network retries**: 3 attempts with exponential backoff -- Continues until task status is `completed` or `failed` - -### Validation Layer (`src/utils/validation.ts`) -- **Prompt**: Required, non-empty string -- **Size**: Must be between 1024×1024 and 4096×4096 - - Accepts both `*` and `x` separators (e.g., `2048*2048` or `2048x2048`) -- **Images**: Max 10 image URLs for edit commands -- **max-images**: Integer between 1 and 15 for sequential commands - -## Key Technical Notes - -### Runtime Requirements -- Built for **Bun runtime** (not Node.js) -- Entry point uses Bun shebang: `#!/usr/bin/env bun` -- Requires `WAVESPEED_API_KEY` environment variable - -### Image Download Behavior -- **Auto-download**: Images are automatically downloaded when tasks complete -- **Output directory**: Default `./output/` (configurable via `--output-dir` flag) -- **File naming**: `{taskId}_1.png`, `{taskId}_2.png`, etc. -- **Base64 support**: When using `--base64`, images are decoded and saved as PNG files - -### CLI Flags -- `--output-dir `: Directory to save downloaded images (default: `./output/`) -- `--base64`: Requests base64-encoded outputs from API (auto-decoded and saved) - -### API Behavior -- All operations create a task asynchronously -- CLI always polls until completion (or failure) -- Images are automatically downloaded upon successful completion - -## Code Structure - -``` -src/ - index.ts # Entry point, registers all commands - commands/ # Command implementations - generate.ts - edit.ts - generate-sequential.ts - edit-sequential.ts - api/ # API client and type definitions - client.ts # HTTP client with auth - types.ts # Request/response types and endpoints - utils/ # Shared utilities - polling.ts # Async task polling logic - validation.ts # Input validation functions - images.ts # Image download and save utilities -``` - -## API Documentation - -Official Wavespeed API docs: -- [Bytedance Seedream V4 (Generate)](https://wavespeed.ai/docs/docs-api/bytedance/bytedance-seedream-v4) -- [Bytedance Seedream V4 Edit](https://wavespeed.ai/docs/docs-api/bytedance/bytedance-seedream-v4-edit) -- [Bytedance Seedream V4 Sequential](https://wavespeed.ai/docs/docs-api/bytedance/bytedance-seedream-v4-sequential) -- [Bytedance Seedream V4 Edit Sequential](https://wavespeed.ai/docs/docs-api/bytedance/bytedance-seedream-v4-edit-sequential) - -## Image Size Guidelines - -The API supports resolutions from 1024×1024 to 4096×4096. - -**Recommended resolutions by aspect ratio:** -- **1:1** — 2048×2048, 3072×3072, 4096×4096 -- **16:9** — 2048×1152, 3072×1728, 4096×2304 -- **4:3** — 2048×1536, 3072×2304, 4096×3072 -- **9:16** — 1152×2048, 1728×3072, 2304×4096 -- **3:4** — 1536×2048, 2304×3072, 3072×4096 - -## Environment Setup - -### Fish Shell -```fish -set -Ux WAVESPEED_API_KEY "your_api_key_here" -source ~/.config/fish/config.fish -``` - -### Bash/Zsh -```bash -export WAVESPEED_API_KEY="your_api_key_here" -source ~/.bashrc # or source ~/.zshrc -``` - -## Error Handling - -Exit codes: -- `0`: Success -- `1`: Command error (validation, API error, task failed) -- `2`: Missing API key diff --git a/bun.lock b/bun.lock index 3418ed0..7cd3ee1 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,6 @@ { "lockfileVersion": 1, - "configVersion": 0, + "configVersion": 1, "workspaces": { "": { "name": "wavespeed-cli", @@ -16,58 +16,62 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^7.1.0", "@semantic-release/git": "^10.0.1", + "@semantic-release/npm": "13.1.5", "@types/bun": "^1.3.11", "@types/prompts": "^2.4.9", "bun-types": "^1.3.11", - "semantic-release": "^25.0.3", - "typescript": "^6.0.2", + "semantic-release": "25.0.3", + "typescript": "6.0.2", }, }, }, + "overrides": { + "@semantic-release/npm": "13.1.5", + "js-yaml": "4.1.1", + "npm": "11.16.0", + }, "packages": { - "@actions/core": ["@actions/core@1.11.1", "", { "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1" } }, "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A=="], + "@actions/core": ["@actions/core@3.0.1", "", { "dependencies": { "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0" } }, "sha512-a6d/Nwahm9fliVGRhdhofo40HjHQasUPusmc7vBfyky+7Z+P2A1J68zyFVaNcEclc/Se+eO595oAr5nwEIoIUA=="], - "@actions/exec": ["@actions/exec@1.1.1", "", { "dependencies": { "@actions/io": "^1.0.1" } }, "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w=="], + "@actions/exec": ["@actions/exec@3.0.0", "", { "dependencies": { "@actions/io": "^3.0.2" } }, "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw=="], - "@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="], + "@actions/http-client": ["@actions/http-client@4.0.1", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^6.23.0" } }, "sha512-+Nvd1ImaOZBSoPbsUtEhv+1z99H12xzncCkz0a3RuehINE81FZSe2QTj3uvAPTcJX/SCzUQHQ0D1GrPMbrPitg=="], - "@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="], + "@actions/io": ["@actions/io@3.0.2", "", {}, "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw=="], - "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@biomejs/biome": ["@biomejs/biome@2.4.9", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.9", "@biomejs/cli-darwin-x64": "2.4.9", "@biomejs/cli-linux-arm64": "2.4.9", "@biomejs/cli-linux-arm64-musl": "2.4.9", "@biomejs/cli-linux-x64": "2.4.9", "@biomejs/cli-linux-x64-musl": "2.4.9", "@biomejs/cli-win32-arm64": "2.4.9", "@biomejs/cli-win32-x64": "2.4.9" }, "bin": { "biome": "bin/biome" } }, "sha512-wvZW92FrwitTcacvCBT8xdAbfbxWfDLwjYMmU3djjqQTh7Ni4ZdiWIT/x5VcZ+RQuxiKzIOzi5D+dcyJDFZMsA=="], + "@biomejs/biome": ["@biomejs/biome@2.4.16", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.16", "@biomejs/cli-darwin-x64": "2.4.16", "@biomejs/cli-linux-arm64": "2.4.16", "@biomejs/cli-linux-arm64-musl": "2.4.16", "@biomejs/cli-linux-x64": "2.4.16", "@biomejs/cli-linux-x64-musl": "2.4.16", "@biomejs/cli-win32-arm64": "2.4.16", "@biomejs/cli-win32-x64": "2.4.16" }, "bin": { "biome": "bin/biome" } }, "sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-d5G8Gf2RpH5pYwiHLPA+UpG3G9TLQu4WM+VK6sfL7K68AmhcEQ9r+nkj/DvR/GYhYox6twsHUtmWWWIKfcfQQA=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-LNCLNgqDMG7BLdc3a8aY/dwKPK7+R8/JXJoXjCvZh2gx8KseqBdFDKbhrr7HCWF8SzNhbTaALhTBoh/I6rf9lA=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-4adnkAUi6K4C/emPRgYznMOcLlUqZdXWM6aIui4VP4LraE764g6Q4YguygnAUoxKjKIXIWPteKMgRbN0wsgwcg=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-8RCww5xnPn2wpK4L/QDGDOW0dq80uVWfppPxHIUg6mOs9B6gRmqPp32h1Ls3T8GnW8Wo5A8u7vpTwz4fExN+sw=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.9", "", { "os": "linux", "cpu": "x64" }, "sha512-L10na7POF0Ks/cgLFNF1ZvIe+X4onLkTi5oP9hY+Rh60Q+7fWzKDDCeGyiHUFf1nGIa9dQOOUPGe2MyYg8nMSQ=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.16", "", { "os": "linux", "cpu": "x64" }, "sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.9", "", { "os": "linux", "cpu": "x64" }, "sha512-5TD+WS9v5vzXKzjetF0hgoaNFHMcpQeBUwKKVi3JbG1e9UCrFuUK3Gt185fyTzvRdwYkJJEMqglRPjmesmVv4A=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.16", "", { "os": "linux", "cpu": "x64" }, "sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-aDZr0RBC3sMGJOU10BvG7eZIlWLK/i51HRIfScE2lVhfts2dQTreowLiJJd+UYg/tHKxS470IbzpuKmd0MiD6g=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.9", "", { "os": "win32", "cpu": "x64" }, "sha512-NS4g/2G9SoQ4ktKtz31pvyc/rmgzlcIDCGU/zWbmHJAqx6gcRj2gj5Q/guXhoWTzCUaQZDIqiCQXHS7BcGYc0w=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.16", "", { "os": "win32", "cpu": "x64" }, "sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw=="], "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], - "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], - - "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="], + "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.28.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-gmloF+i+flI8ouQK7MWW4mOwuMh4RePBuPFAEPC6+pdqyWOUMDOixb6qZ69owLJpz6XmyllCouc4t8YWO+E2Nw=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], "@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], "@octokit/core": ["@octokit/core@7.0.6", "", { "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" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], - "@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + "@octokit/endpoint": ["@octokit/endpoint@11.0.3", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag=="], "@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="], @@ -75,11 +79,11 @@ "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@14.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw=="], - "@octokit/plugin-retry": ["@octokit/plugin-retry@8.0.3", "", { "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" }, "peerDependencies": { "@octokit/core": ">=7" } }, "sha512-vKGx1i3MC0za53IzYBSBXcrhmd+daQDzuZfYDd52X5S0M2otf3kVZTVP8bLA3EkU0lTvd1WEC2OlNNa4G+dohA=="], + "@octokit/plugin-retry": ["@octokit/plugin-retry@8.1.0", "", { "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" }, "peerDependencies": { "@octokit/core": ">=7" } }, "sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw=="], "@octokit/plugin-throttling": ["@octokit/plugin-throttling@11.0.3", "", { "dependencies": { "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" }, "peerDependencies": { "@octokit/core": "^7.0.0" } }, "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg=="], - "@octokit/request": ["@octokit/request@10.0.7", "", { "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" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="], + "@octokit/request": ["@octokit/request@10.0.10", "", { "dependencies": { "@octokit/endpoint": "^11.0.3", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "content-type": "^2.0.0", "json-with-bigint": "^3.5.3", "universal-user-agent": "^7.0.2" } }, "sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w=="], "@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], @@ -89,7 +93,7 @@ "@pnpm/network.ca-file": ["@pnpm/network.ca-file@1.0.2", "", { "dependencies": { "graceful-fs": "4.2.10" } }, "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA=="], - "@pnpm/npm-conf": ["@pnpm/npm-conf@2.3.1", "", { "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" } }, "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw=="], + "@pnpm/npm-conf": ["@pnpm/npm-conf@3.0.2", "", { "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" } }, "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA=="], "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], @@ -103,19 +107,21 @@ "@semantic-release/git": ["@semantic-release/git@10.0.1", "", { "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "dir-glob": "^3.0.0", "execa": "^5.0.0", "lodash": "^4.17.4", "micromatch": "^4.0.0", "p-reduce": "^2.0.0" }, "peerDependencies": { "semantic-release": ">=18.0.0" } }, "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w=="], - "@semantic-release/github": ["@semantic-release/github@12.0.2", "", { "dependencies": { "@octokit/core": "^7.0.0", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-retry": "^8.0.0", "@octokit/plugin-throttling": "^11.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "debug": "^4.3.4", "dir-glob": "^3.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "issue-parser": "^7.0.0", "lodash-es": "^4.17.21", "mime": "^4.0.0", "p-filter": "^4.0.0", "tinyglobby": "^0.2.14", "undici": "^7.0.0", "url-join": "^5.0.0" }, "peerDependencies": { "semantic-release": ">=24.1.0" } }, "sha512-qyqLS+aSGH1SfXIooBKjs7mvrv0deg8v+jemegfJg1kq6ji+GJV8CO08VJDEsvjp3O8XJmTTIAjjZbMzagzsdw=="], + "@semantic-release/github": ["@semantic-release/github@12.0.8", "", { "dependencies": { "@octokit/core": "^7.0.0", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-retry": "^8.0.0", "@octokit/plugin-throttling": "^11.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "debug": "^4.3.4", "dir-glob": "^3.0.1", "http-proxy-agent": "^9.0.0", "https-proxy-agent": "^9.0.0", "issue-parser": "^7.0.0", "lodash-es": "^4.17.21", "mime": "^4.0.0", "p-filter": "^4.0.0", "tinyglobby": "^0.2.14", "undici": "^7.0.0", "url-join": "^5.0.0" }, "peerDependencies": { "semantic-release": ">=24.1.0" } }, "sha512-tej5AAgK5X9wHRoDmYhecMXEHEkFeGOY1XsEblKxu8pIQwahzf1STYyr7iPU6Lpbg6C5I3N2w/ocXrBo+L7jhw=="], + + "@semantic-release/npm": ["@semantic-release/npm@13.1.5", "", { "dependencies": { "@actions/core": "^3.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "env-ci": "^11.2.0", "execa": "^9.0.0", "fs-extra": "^11.0.0", "lodash-es": "^4.17.21", "nerf-dart": "^1.0.0", "normalize-url": "^9.0.0", "npm": "^11.6.2", "rc": "^1.2.8", "read-pkg": "^10.0.0", "registry-auth-token": "^5.0.0", "semver": "^7.1.2", "tempy": "^3.0.0" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "sha512-Hq5UxzoatN3LHiq2rTsWS54nCdqJHlsssGERCo8WlvdfFA9LoN0vO+OuKVSjtNapIc/S8C2LBj206wKLHg62mg=="], - "@semantic-release/npm": ["@semantic-release/npm@13.1.2", "", { "dependencies": { "@actions/core": "^1.11.1", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "env-ci": "^11.2.0", "execa": "^9.0.0", "fs-extra": "^11.0.0", "lodash-es": "^4.17.21", "nerf-dart": "^1.0.0", "normalize-url": "^8.0.0", "npm": "^11.6.2", "rc": "^1.2.8", "read-pkg": "^10.0.0", "registry-auth-token": "^5.0.0", "semver": "^7.1.2", "tempy": "^3.0.0" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "sha512-9rtshDTNlzYrC7uSBtB1vHqFzFZaNHigqkkCH5Ls4N/BSlVOenN5vtwHYxjAR4jf1hNvWSVwL4eIFTHONYckkw=="], + "@semantic-release/release-notes-generator": ["@semantic-release/release-notes-generator@14.1.1", "", { "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "import-from-esm": "^2.0.0", "lodash-es": "^4.17.21", "read-package-up": "^11.0.0" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "sha512-Pbd2e2XRMUD0OxehHpgd5/YghsE76cddkRHSoDvKLK+OCy4Ewxn49rWR631MEUU01lgwF/uyVXvbnVuu6+Z6VA=="], - "@semantic-release/release-notes-generator": ["@semantic-release/release-notes-generator@14.1.0", "", { "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "get-stream": "^7.0.0", "import-from-esm": "^2.0.0", "into-stream": "^7.0.0", "lodash-es": "^4.17.21", "read-package-up": "^11.0.0" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "sha512-CcyDRk7xq+ON/20YNR+1I/jP7BYKICr1uKd1HHpROSnnTdGqOTburi4jcRiTYz0cpfhxSloQO3cGhnoot7IEkA=="], + "@simple-libs/stream-utils": ["@simple-libs/stream-utils@1.2.0", "", {}, "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA=="], "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], - "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], - "@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="], + "@types/node": ["@types/node@25.9.2", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw=="], "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], @@ -123,15 +129,15 @@ "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], - "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "agent-base": ["agent-base@9.0.0", "", {}, "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA=="], "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], - "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], - "ansi-escapes": ["ansi-escapes@7.2.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw=="], + "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], @@ -147,13 +153,13 @@ "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], - "body-parser": ["body-parser@2.2.1", "", { "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" } }, "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw=="], + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], "bottleneck": ["bottleneck@2.19.5", "", {}, "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -185,17 +191,17 @@ "config-chain": ["config-chain@1.1.13", "", { "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ=="], - "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], + "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], - "conventional-changelog-angular": ["conventional-changelog-angular@8.1.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w=="], + "conventional-changelog-angular": ["conventional-changelog-angular@8.3.1", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg=="], - "conventional-changelog-writer": ["conventional-changelog-writer@8.2.0", "", { "dependencies": { "conventional-commits-filter": "^5.0.0", "handlebars": "^4.7.7", "meow": "^13.0.0", "semver": "^7.5.2" }, "bin": { "conventional-changelog-writer": "dist/cli/index.js" } }, "sha512-Y2aW4596l9AEvFJRwFGJGiQjt2sBYTjPD18DdvxX9Vpz0Z7HQ+g1Z+6iYDAm1vR3QOJrDBkRHixHK/+FhkR6Pw=="], + "conventional-changelog-writer": ["conventional-changelog-writer@8.4.0", "", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0", "conventional-commits-filter": "^5.0.0", "handlebars": "^4.7.7", "meow": "^13.0.0", "semver": "^7.5.2" }, "bin": { "conventional-changelog-writer": "dist/cli/index.js" } }, "sha512-HHBFkk1EECxxmCi4CTu091iuDpQv5/OavuCUAuZmrkWpmYfyD816nom1CvtfXJ/uYfAAjavgHvXHX291tSLK8g=="], "conventional-commits-filter": ["conventional-commits-filter@5.0.0", "", {}, "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q=="], - "conventional-commits-parser": ["conventional-commits-parser@6.2.1", "", { "dependencies": { "meow": "^13.0.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" } }, "sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA=="], + "conventional-commits-parser": ["conventional-commits-parser@6.4.0", "", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0", "meow": "^13.0.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" } }, "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw=="], "convert-hrtime": ["convert-hrtime@5.0.0", "", {}, "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg=="], @@ -205,15 +211,15 @@ "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], - "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], - "cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="], + "cosmiconfig": ["cosmiconfig@9.0.2", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "crypto-random-string": ["crypto-random-string@4.0.0", "", { "dependencies": { "type-fest": "^1.0.1" } }, "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA=="], - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], @@ -247,7 +253,7 @@ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -259,19 +265,17 @@ "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], - "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + "eventsource-parser": ["eventsource-parser@3.1.0", "", {}, "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="], "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], "express": ["express@5.2.1", "", { "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" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], - "express-rate-limit": ["express-rate-limit@8.3.1", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw=="], - - "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], + "express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], @@ -291,9 +295,7 @@ "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - "from2": ["from2@2.3.0", "", { "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" } }, "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g=="], - - "fs-extra": ["fs-extra@11.3.2", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A=="], + "fs-extra": ["fs-extra@11.3.5", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -301,7 +303,7 @@ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], "get-intrinsic": ["get-intrinsic@1.3.0", "", { "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" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], @@ -315,31 +317,31 @@ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - "handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="], + "handlebars": ["handlebars@4.7.9", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], - "hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="], + "hono": ["hono@4.12.25", "", {}, "sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ=="], "hook-std": ["hook-std@4.0.0", "", {}, "sha512-IHI4bEVOt3vRUDJ+bFA9VUJlo7SzvFARPNLw75pqSmAOP2HmTWfFJtPvLBrDrlgjEYXY9zs7SFdHPQaJShkSCQ=="], - "hosted-git-info": ["hosted-git-info@9.0.2", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg=="], + "hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="], "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], - "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + "http-proxy-agent": ["http-proxy-agent@9.1.0", "", { "dependencies": { "agent-base": "9.0.0", "debug": "^4.3.4", "proxy-agent-negotiate": "1.1.0" } }, "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig=="], - "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "https-proxy-agent": ["https-proxy-agent@9.1.0", "", { "dependencies": { "agent-base": "9.0.0", "debug": "^4.3.4", "proxy-agent-negotiate": "1.1.0" } }, "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA=="], "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], - "iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], @@ -355,9 +357,7 @@ "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - "into-stream": ["into-stream@7.0.0", "", { "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" } }, "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw=="], - - "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], @@ -381,11 +381,11 @@ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "issue-parser": ["issue-parser@7.0.1", "", { "dependencies": { "lodash.capitalize": "^4.2.1", "lodash.escaperegexp": "^4.1.2", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.uniqby": "^4.7.0" } }, "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg=="], + "issue-parser": ["issue-parser@7.0.2", "", { "dependencies": { "lodash.capitalize": "^4.2.1", "lodash.escaperegexp": "^4.1.2", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.uniqby": "^4.7.0" } }, "sha512-7atWPjhGEIX3JEtMrOYd8TKzboYlq+5sNbdl9POiLYOI14G5HZiQbZP0Xj5EZdrufQVXfJlpTV0hys0CuxwxZw=="], "java-properties": ["java-properties@1.0.2", "", {}, "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ=="], - "jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + "jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], @@ -399,7 +399,9 @@ "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], - "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], + "json-with-bigint": ["json-with-bigint@3.5.8", "", {}, "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw=="], + + "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], @@ -409,9 +411,9 @@ "locate-path": ["locate-path@2.0.0", "", { "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" } }, "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA=="], - "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], - "lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], + "lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="], "lodash.capitalize": ["lodash.capitalize@4.2.1", "", {}, "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw=="], @@ -423,9 +425,9 @@ "lodash.uniqby": ["lodash.uniqby@4.7.0", "", {}, "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww=="], - "lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + "lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], - "make-asynchronous": ["make-asynchronous@1.0.1", "", { "dependencies": { "p-event": "^6.0.0", "type-fest": "^4.6.0", "web-worker": "1.2.0" } }, "sha512-T9BPOmEOhp6SmV25SwLVcHK4E6JyG/coH3C6F1NjNXSziv/fd4GmsqMk8YR6qpPOswfaOCApSNkZv6fxoaYFcQ=="], + "make-asynchronous": ["make-asynchronous@1.1.0", "", { "dependencies": { "p-event": "^6.0.0", "type-fest": "^4.6.0", "web-worker": "^1.5.0" } }, "sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg=="], "marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], @@ -467,9 +469,9 @@ "normalize-package-data": ["normalize-package-data@8.0.0", "", { "dependencies": { "hosted-git-info": "^9.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ=="], - "normalize-url": ["normalize-url@8.1.0", "", {}, "sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w=="], + "normalize-url": ["normalize-url@9.0.1", "", {}, "sha512-ARftfC5HdUNu9jJeL8pHj8debUIHA2b91FizCoMzY4lG6dDX13jdvTK0TBe24IBDRf2HvJSzzwEPvmbkQWHRSg=="], - "npm": ["npm@11.6.4", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^9.1.8", "@npmcli/config": "^10.4.4", "@npmcli/fs": "^5.0.0", "@npmcli/map-workspaces": "^5.0.3", "@npmcli/metavuln-calculator": "^9.0.3", "@npmcli/package-json": "^7.0.4", "@npmcli/promise-spawn": "^9.0.1", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.3", "@sigstore/tuf": "^4.0.0", "abbrev": "^4.0.0", "archy": "~1.0.0", "cacache": "^20.0.3", "chalk": "^5.6.2", "ci-info": "^4.3.1", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^13.0.0", "graceful-fs": "^4.2.11", "hosted-git-info": "^9.0.2", "ini": "^6.0.0", "init-package-json": "^8.2.4", "is-cidr": "^6.0.1", "json-parse-even-better-errors": "^5.0.0", "libnpmaccess": "^10.0.3", "libnpmdiff": "^8.0.11", "libnpmexec": "^10.1.10", "libnpmfund": "^7.0.11", "libnpmorg": "^8.0.1", "libnpmpack": "^9.0.11", "libnpmpublish": "^11.1.3", "libnpmsearch": "^9.0.1", "libnpmteam": "^8.0.2", "libnpmversion": "^8.0.3", "make-fetch-happen": "^15.0.3", "minimatch": "^10.1.1", "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^12.1.0", "nopt": "^9.0.0", "npm-audit-report": "^7.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.2", "npm-pick-manifest": "^11.0.3", "npm-profile": "^12.0.1", "npm-registry-fetch": "^19.1.1", "npm-user-validate": "^4.0.0", "p-map": "^7.0.4", "pacote": "^21.0.4", "parse-conflict-json": "^5.0.1", "proc-log": "^6.1.0", "qrcode-terminal": "^0.12.0", "read": "^5.0.1", "semver": "^7.7.3", "spdx-expression-parse": "^4.0.0", "ssri": "^13.0.0", "supports-color": "^10.2.2", "tar": "^7.5.2", "text-table": "~0.2.0", "tiny-relative-date": "^2.0.2", "treeverse": "^3.0.0", "validate-npm-package-name": "^7.0.0", "which": "^6.0.0" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" } }, "sha512-ERjKtGoFpQrua/9bG0+h3xiv/4nVdGViCjUYA1AmlV24fFvfnSB7B7dIfZnySQ1FDLd0ZVrWPsLLp78dCtJdRQ=="], + "npm": ["npm@11.16.0", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^9.7.0", "@npmcli/config": "^10.10.0", "@npmcli/fs": "^5.0.0", "@npmcli/map-workspaces": "^5.0.3", "@npmcli/metavuln-calculator": "^9.0.3", "@npmcli/package-json": "^7.0.5", "@npmcli/promise-spawn": "^9.0.1", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.4", "@sigstore/tuf": "^4.0.2", "abbrev": "^4.0.0", "archy": "~1.0.0", "cacache": "^20.0.4", "chalk": "^5.6.2", "ci-info": "^4.4.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^13.0.6", "graceful-fs": "^4.2.11", "hosted-git-info": "^9.0.3", "ini": "^6.0.0", "init-package-json": "^8.2.5", "is-cidr": "^6.0.4", "json-parse-even-better-errors": "^5.0.0", "libnpmaccess": "^10.0.3", "libnpmdiff": "^8.1.9", "libnpmexec": "^10.2.9", "libnpmfund": "^7.0.23", "libnpmorg": "^8.0.1", "libnpmpack": "^9.1.9", "libnpmpublish": "^11.2.0", "libnpmsearch": "^9.0.1", "libnpmteam": "^8.0.2", "libnpmversion": "^8.0.4", "make-fetch-happen": "^15.0.6", "minimatch": "^10.2.5", "minipass": "^7.1.3", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^12.3.0", "nopt": "^9.0.0", "npm-audit-report": "^7.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.2", "npm-pick-manifest": "^11.0.3", "npm-profile": "^12.0.1", "npm-registry-fetch": "^19.1.1", "npm-user-validate": "^4.0.0", "p-map": "^7.0.4", "pacote": "^21.5.0", "parse-conflict-json": "^5.0.1", "proc-log": "^6.1.0", "qrcode-terminal": "^0.12.0", "read": "^5.0.1", "semver": "^7.8.1", "spdx-expression-parse": "^4.0.0", "ssri": "^13.0.1", "supports-color": "^10.2.2", "tar": "^7.5.15", "text-table": "~0.2.0", "tiny-relative-date": "^2.0.2", "treeverse": "^3.0.0", "validate-npm-package-name": "^7.0.2", "which": "^6.0.1" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" } }, "sha512-A74XL8OxmcegZDMWPkWb5bEQppg8HdYwW3rBD2sPoS4UQHVajfaxBkqyzLeJ3wR0kZ+5xoTjItxXaF7eIXUsyw=="], "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], @@ -489,8 +491,6 @@ "p-filter": ["p-filter@4.1.0", "", { "dependencies": { "p-map": "^7.0.1" } }, "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw=="], - "p-is-promise": ["p-is-promise@3.0.0", "", {}, "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ=="], - "p-limit": ["p-limit@1.3.0", "", { "dependencies": { "p-try": "^1.0.0" } }, "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q=="], "p-locate": ["p-locate@2.0.0", "", { "dependencies": { "p-limit": "^1.1.0" } }, "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg=="], @@ -519,13 +519,13 @@ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], @@ -543,7 +543,9 @@ "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], - "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "proxy-agent-negotiate": ["proxy-agent-negotiate@1.1.0", "", { "peerDependencies": { "kerberos": "^2.0.0" }, "optionalPeers": ["kerberos"] }, "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ=="], + + "qs": ["qs@6.15.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw=="], "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], @@ -553,11 +555,11 @@ "read-package-up": ["read-package-up@12.0.0", "", { "dependencies": { "find-up-simple": "^1.0.1", "read-pkg": "^10.0.0", "type-fest": "^5.2.0" } }, "sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw=="], - "read-pkg": ["read-pkg@10.0.0", "", { "dependencies": { "@types/normalize-package-data": "^2.4.4", "normalize-package-data": "^8.0.0", "parse-json": "^8.3.0", "type-fest": "^5.2.0", "unicorn-magic": "^0.3.0" } }, "sha512-A70UlgfNdKI5NSvTTfHzLQj7NJRpJ4mT5tGafkllJ4wh71oYuGm/pzphHcmW4s35iox56KSK721AihodoXSc/A=="], + "read-pkg": ["read-pkg@10.1.0", "", { "dependencies": { "@types/normalize-package-data": "^2.4.4", "normalize-package-data": "^8.0.0", "parse-json": "^8.3.0", "type-fest": "^5.4.4", "unicorn-magic": "^0.4.0" } }, "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg=="], "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "registry-auth-token": ["registry-auth-token@5.1.0", "", { "dependencies": { "@pnpm/npm-conf": "^2.1.0" } }, "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw=="], + "registry-auth-token": ["registry-auth-token@5.1.1", "", { "dependencies": { "@pnpm/npm-conf": "^3.0.2" } }, "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q=="], "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], @@ -573,13 +575,13 @@ "semantic-release": ["semantic-release@25.0.3", "", { "dependencies": { "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/error": "^4.0.0", "@semantic-release/github": "^12.0.0", "@semantic-release/npm": "^13.1.1", "@semantic-release/release-notes-generator": "^14.1.0", "aggregate-error": "^5.0.0", "cosmiconfig": "^9.0.0", "debug": "^4.0.0", "env-ci": "^11.0.0", "execa": "^9.0.0", "figures": "^6.0.0", "find-versions": "^6.0.0", "get-stream": "^6.0.0", "git-log-parser": "^1.2.0", "hook-std": "^4.0.0", "hosted-git-info": "^9.0.0", "import-from-esm": "^2.0.0", "lodash-es": "^4.17.21", "marked": "^15.0.0", "marked-terminal": "^7.3.0", "micromatch": "^4.0.2", "p-each-series": "^3.0.0", "p-reduce": "^3.0.0", "read-package-up": "^12.0.0", "resolve-from": "^5.0.0", "semver": "^7.3.2", "signale": "^1.2.1", "yargs": "^18.0.0" }, "bin": { "semantic-release": "bin/semantic-release.js" } }, "sha512-WRgl5GcypwramYX4HV+eQGzUbD7UUbljVmS+5G1uMwX/wLgYuJAxGeerXJDMO2xshng4+FXqCgyB5QfClV6WjA=="], - "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "semver": ["semver@7.8.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg=="], "semver-regex": ["semver-regex@4.0.5", "", {}, "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw=="], - "send": ["send@1.2.0", "", { "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" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], - "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], @@ -587,9 +589,9 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "side-channel": ["side-channel@1.1.0", "", { "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" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], @@ -613,7 +615,7 @@ "spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], - "spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="], + "spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="], "split2": ["split2@1.0.0", "", { "dependencies": { "through2": "~2.0.0" } }, "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg=="], @@ -625,7 +627,7 @@ "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], @@ -635,7 +637,7 @@ "super-regex": ["super-regex@1.1.0", "", { "dependencies": { "function-timeout": "^1.0.1", "make-asynchronous": "^1.0.1", "time-span": "^5.1.0" } }, "sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ=="], - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], "supports-hyperlinks": ["supports-hyperlinks@3.2.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig=="], @@ -643,7 +645,7 @@ "temp-dir": ["temp-dir@3.0.0", "", {}, "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw=="], - "tempy": ["tempy@3.1.0", "", { "dependencies": { "is-stream": "^3.0.0", "temp-dir": "^3.0.0", "type-fest": "^2.12.2", "unique-string": "^3.0.0" } }, "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g=="], + "tempy": ["tempy@3.2.0", "", { "dependencies": { "is-stream": "^3.0.0", "temp-dir": "^3.0.0", "type-fest": "^2.12.2", "unique-string": "^3.0.0" } }, "sha512-d79HhZya5Djd7am0q+W4RTsSU+D/aJzM+4Y4AGJGuGlgM2L6sx5ZvOYTmZjqPhrDrV6xJTtRSm1JCLj6V6LHLQ=="], "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], @@ -653,7 +655,7 @@ "time-span": ["time-span@5.1.0", "", { "dependencies": { "convert-hrtime": "^5.0.0" } }, "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA=="], - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -665,19 +667,19 @@ "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], "typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="], "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], - "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="], + "undici": ["undici@7.27.2", "", {}, "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA=="], - "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="], "unicode-emoji-modifier-base": ["unicode-emoji-modifier-base@1.0.0", "", {}, "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g=="], - "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + "unicorn-magic": ["unicorn-magic@0.4.0", "", {}, "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw=="], "unique-string": ["unique-string@3.0.0", "", { "dependencies": { "crypto-random-string": "^4.0.0" } }, "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ=="], @@ -695,7 +697,7 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - "web-worker": ["web-worker@1.2.0", "", {}, "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA=="], + "web-worker": ["web-worker@1.5.0", "", {}, "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -709,7 +711,7 @@ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], "yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], @@ -717,11 +719,13 @@ "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], - "@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + "@actions/http-client/undici": ["undici@6.26.0", "", {}, "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A=="], + + "@octokit/request/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], @@ -737,12 +741,8 @@ "@semantic-release/npm/aggregate-error": ["aggregate-error@5.0.0", "", { "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" } }, "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw=="], - "@semantic-release/release-notes-generator/get-stream": ["get-stream@7.0.1", "", {}, "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ=="], - "@semantic-release/release-notes-generator/read-package-up": ["read-package-up@11.0.0", "", { "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" } }, "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ=="], - "bun-types/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], - "cli-highlight/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "cli-highlight/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], @@ -757,29 +757,27 @@ "execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], - "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "fdir/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "load-json-file/parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="], - "npm/@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], - - "npm/@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + "npm/@gar/promise-retry": ["@gar/promise-retry@1.0.3", "", {}, "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA=="], "npm/@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], "npm/@isaacs/string-locale-compare": ["@isaacs/string-locale-compare@1.1.0", "", { "bundled": true }, "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ=="], - "npm/@npmcli/agent": ["@npmcli/agent@4.0.0", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA=="], + "npm/@npmcli/agent": ["@npmcli/agent@4.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg=="], - "npm/@npmcli/arborist": ["@npmcli/arborist@9.1.8", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^4.0.0", "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^5.0.0", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", "common-ancestor-path": "^1.0.1", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", "minimatch": "^10.0.3", "nopt": "^9.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.0", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "pacote": "^21.0.2", "parse-conflict-json": "^5.0.1", "proc-log": "^6.0.0", "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "semver": "^7.3.7", "ssri": "^13.0.0", "treeverse": "^3.0.0", "walk-up-path": "^4.0.0" }, "bundled": true, "bin": { "arborist": "bin/index.js" } }, "sha512-TYAzq0oaXQU+uLfXFbR2wYx62qHIOSg/TYhGWJSphJDypyjdNXC7B/+k29ElC2vWlWfX4OJnhmSY5DTwSFiNpg=="], + "npm/@npmcli/arborist": ["@npmcli/arborist@9.7.0", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^4.0.0", "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^5.0.0", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", "common-ancestor-path": "^2.0.0", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", "minimatch": "^10.0.3", "nopt": "^9.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.0", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "pacote": "^21.0.2", "parse-conflict-json": "^5.0.1", "proc-log": "^6.0.0", "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "semver": "^7.3.7", "ssri": "^13.0.0", "treeverse": "^3.0.0", "walk-up-path": "^4.0.0" }, "bundled": true, "bin": { "arborist": "bin/index.js" } }, "sha512-vmGB7um0yvzZlBi0GzmSRJ/oW2KzjiBdgD5AGbMdiqxg/xemfCsDMyD/DtkXPhZrWsPNtGByiBF1kx//gi/5xA=="], - "npm/@npmcli/config": ["@npmcli/config@10.4.4", "", { "dependencies": { "@npmcli/map-workspaces": "^5.0.0", "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^6.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "walk-up-path": "^4.0.0" }, "bundled": true }, "sha512-Zs4ZLFLb4ENdcHPS6wYKTPfNOV0OZxgivJ4gvqmTI+pjj71WWIJD7/1qbCZyyo8dSLgbVIfdDwKHwOnZmil0aQ=="], + "npm/@npmcli/config": ["@npmcli/config@10.10.0", "", { "dependencies": { "@npmcli/map-workspaces": "^5.0.0", "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^6.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "walk-up-path": "^4.0.0" }, "bundled": true }, "sha512-Qpv1lFxJle2EmvskCmkVMPxWiiUeNZ7xyuUNHnvpmaellpVBIrYXQWysuKwHo/5G8WDaRkgnTyarL36/2Db76g=="], "npm/@npmcli/fs": ["@npmcli/fs@5.0.0", "", { "dependencies": { "semver": "^7.3.5" }, "bundled": true }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="], - "npm/@npmcli/git": ["@npmcli/git@7.0.1", "", { "dependencies": { "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", "promise-retry": "^2.0.1", "semver": "^7.3.5", "which": "^6.0.0" } }, "sha512-+XTFxK2jJF/EJJ5SoAzXk3qwIDfvFc5/g+bD274LZ7uY7LE8sTfG6Z8rOanPl2ZEvZWqNvmEdtXC25cE54VcoA=="], + "npm/@npmcli/git": ["@npmcli/git@7.0.2", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", "semver": "^7.3.5", "which": "^6.0.0" } }, "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg=="], "npm/@npmcli/installed-package-contents": ["@npmcli/installed-package-contents@4.0.0", "", { "dependencies": { "npm-bundled": "^5.0.0", "npm-normalize-package-bin": "^5.0.0" }, "bin": { "installed-package-contents": "bin/index.js" } }, "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA=="], @@ -791,7 +789,7 @@ "npm/@npmcli/node-gyp": ["@npmcli/node-gyp@5.0.0", "", {}, "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ=="], - "npm/@npmcli/package-json": ["@npmcli/package-json@7.0.4", "", { "dependencies": { "@npmcli/git": "^7.0.0", "glob": "^13.0.0", "hosted-git-info": "^9.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.5.3", "validate-npm-package-license": "^3.0.4" }, "bundled": true }, "sha512-0wInJG3j/K40OJt/33ax47WfWMzZTm6OQxB9cDhTt5huCP2a9g2GnlsxmfN+PulItNPIpPrZ+kfwwUil7eHcZQ=="], + "npm/@npmcli/package-json": ["@npmcli/package-json@7.0.5", "", { "dependencies": { "@npmcli/git": "^7.0.0", "glob": "^13.0.0", "hosted-git-info": "^9.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.5.3", "spdx-expression-parse": "^4.0.0" }, "bundled": true }, "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ=="], "npm/@npmcli/promise-spawn": ["@npmcli/promise-spawn@9.0.1", "", { "dependencies": { "which": "^6.0.0" }, "bundled": true }, "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q=="], @@ -799,72 +797,62 @@ "npm/@npmcli/redact": ["@npmcli/redact@4.0.0", "", { "bundled": true }, "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q=="], - "npm/@npmcli/run-script": ["@npmcli/run-script@10.0.3", "", { "dependencies": { "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", "proc-log": "^6.0.0", "which": "^6.0.0" }, "bundled": true }, "sha512-ER2N6itRkzWbbtVmZ9WKaWxVlKlOeBFF1/7xx+KA5J1xKa4JjUwBdb6tDpk0v1qA+d+VDwHI9qmLcXSWcmi+Rw=="], + "npm/@npmcli/run-script": ["@npmcli/run-script@10.0.4", "", { "dependencies": { "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", "proc-log": "^6.0.0" }, "bundled": true }, "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg=="], "npm/@sigstore/bundle": ["@sigstore/bundle@4.0.0", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A=="], - "npm/@sigstore/core": ["@sigstore/core@3.0.0", "", {}, "sha512-NgbJ+aW9gQl/25+GIEGYcCyi8M+ng2/5X04BMuIgoDfgvp18vDcoNHOQjQsG9418HGNYRxG3vfEXaR1ayD37gg=="], + "npm/@sigstore/core": ["@sigstore/core@3.2.1", "", {}, "sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g=="], - "npm/@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.5.0", "", {}, "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA=="], + "npm/@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.5.1", "", {}, "sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g=="], - "npm/@sigstore/sign": ["@sigstore/sign@4.0.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.0.0", "@sigstore/protobuf-specs": "^0.5.0", "make-fetch-happen": "^15.0.2", "proc-log": "^5.0.0", "promise-retry": "^2.0.1" } }, "sha512-KFNGy01gx9Y3IBPG/CergxR9RZpN43N+lt3EozEfeoyqm8vEiLxwRl3ZO5sPx3Obv1ix/p7FWOlPc2Jgwfp9PA=="], + "npm/@sigstore/sign": ["@sigstore/sign@4.1.1", "", { "dependencies": { "@gar/promise-retry": "^1.0.2", "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.0", "@sigstore/protobuf-specs": "^0.5.0", "make-fetch-happen": "^15.0.4", "proc-log": "^6.1.0" } }, "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ=="], - "npm/@sigstore/tuf": ["@sigstore/tuf@4.0.0", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", "tuf-js": "^4.0.0" }, "bundled": true }, "sha512-0QFuWDHOQmz7t66gfpfNO6aEjoFrdhkJaej/AOqb4kqWZVbPWFZifXZzkxyQBB1OwTbkhdT3LNpMFxwkTvf+2w=="], + "npm/@sigstore/tuf": ["@sigstore/tuf@4.0.2", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", "tuf-js": "^4.1.0" }, "bundled": true }, "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ=="], - "npm/@sigstore/verify": ["@sigstore/verify@3.0.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.0.0", "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-moXtHH33AobOhTZF8xcX1MpOFqdvfCk7v6+teJL8zymBiDXwEsQH6XG9HGx2VIxnJZNm4cNSzflTLDnQLmIdmw=="], + "npm/@sigstore/verify": ["@sigstore/verify@3.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA=="], "npm/@tufjs/canonical-json": ["@tufjs/canonical-json@2.0.0", "", {}, "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA=="], - "npm/@tufjs/models": ["@tufjs/models@4.0.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^9.0.5" } }, "sha512-h5x5ga/hh82COe+GoD4+gKUeV4T3iaYOxqLt41GRKApinPI7DMidhCmNVTjKfhCWFJIGXaFJee07XczdT4jdZQ=="], + "npm/@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="], "npm/abbrev": ["abbrev@4.0.0", "", { "bundled": true }, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="], "npm/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "npm/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "npm/aproba": ["aproba@2.1.0", "", {}, "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew=="], "npm/archy": ["archy@1.0.0", "", { "bundled": true }, "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw=="], - "npm/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "npm/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - "npm/bin-links": ["bin-links@6.0.0", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w=="], + "npm/bin-links": ["bin-links@6.0.2", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-frE1t78WOwJ45PKV2cF2tNPjTcs9L1J9s6VkrV59wanRP4GlaomuxYPVma7BwthMg8WnfSory4w5PTE6FZZ81w=="], "npm/binary-extensions": ["binary-extensions@3.1.0", "", {}, "sha512-Jvvd9hy1w+xUad8+ckQsWA/V1AoyubOvqn0aygjMOVM4BfIaRav1NFS3LsTSDaV4n4FtcCtQXvzep1E6MboqwQ=="], - "npm/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "npm/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], - "npm/cacache": ["cacache@20.0.3", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0", "unique-filename": "^5.0.0" }, "bundled": true }, "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw=="], + "npm/cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" }, "bundled": true }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], "npm/chalk": ["chalk@5.6.2", "", { "bundled": true }, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "npm/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], - "npm/ci-info": ["ci-info@4.3.1", "", { "bundled": true }, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], - - "npm/cidr-regex": ["cidr-regex@5.0.1", "", { "dependencies": { "ip-regex": "5.0.0" } }, "sha512-2Apfc6qH9uwF3QHmlYBA8ExB9VHq+1/Doj9sEMY55TVBcpQ3y/+gmMpcNIBBtfb5k54Vphmta+1IxjMqPlWWAA=="], + "npm/ci-info": ["ci-info@4.4.0", "", { "bundled": true }, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], - "npm/cli-columns": ["cli-columns@4.0.0", "", { "dependencies": { "string-width": "^4.2.3", "strip-ansi": "^6.0.1" }, "bundled": true }, "sha512-XW2Vg+w+L9on9wtwKpyzluIPCWXjaBahI7mTcYjx+BVIYD9c3yqcv/yKC7CmdCZat4rq2yiE1UMSJC5ivKfMtQ=="], + "npm/cidr-regex": ["cidr-regex@5.0.5", "", {}, "sha512-59tdLZcC+BJXa4C5rOmVSuJTy/UneqfJJtCraqwdx5BDHTkGrBtKCUl3u2uiCFvXu+wk0kVuX8axX7yHCZOI9w=="], "npm/cmd-shim": ["cmd-shim@8.0.0", "", {}, "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA=="], - "npm/common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + "npm/common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="], "npm/cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], - "npm/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "npm/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "npm/diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], - - "npm/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "npm/encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], + "npm/diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], "npm/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], - "npm/err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="], - "npm/exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], "npm/fastest-levenshtein": ["fastest-levenshtein@1.0.16", "", { "bundled": true }, "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="], @@ -873,11 +861,11 @@ "npm/fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" }, "bundled": true }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="], - "npm/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" }, "bundled": true }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], + "npm/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" }, "bundled": true }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], "npm/graceful-fs": ["graceful-fs@4.2.11", "", { "bundled": true }, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - "npm/hosted-git-info": ["hosted-git-info@9.0.2", "", { "dependencies": { "lru-cache": "^11.1.0" }, "bundled": true }, "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg=="], + "npm/hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" }, "bundled": true }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="], "npm/http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], @@ -885,25 +873,19 @@ "npm/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], - "npm/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "npm/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "npm/ignore-walk": ["ignore-walk@8.0.0", "", { "dependencies": { "minimatch": "^10.0.3" } }, "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A=="], - "npm/imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - "npm/ini": ["ini@6.0.0", "", { "bundled": true }, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], - "npm/init-package-json": ["init-package-json@8.2.4", "", { "dependencies": { "@npmcli/package-json": "^7.0.0", "npm-package-arg": "^13.0.0", "promzard": "^3.0.1", "read": "^5.0.1", "semver": "^7.7.2", "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^7.0.0" }, "bundled": true }, "sha512-SqX/+tPl3sZD+IY0EuMiM1kK1B45h+P6JQPo3Q9zlqNINX2XiX3x/WSbYGFqS6YCkODNbGb3L5RawMrYE/cfKw=="], - - "npm/ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "npm/init-package-json": ["init-package-json@8.2.5", "", { "dependencies": { "@npmcli/package-json": "^7.0.0", "npm-package-arg": "^13.0.0", "promzard": "^3.0.1", "read": "^5.0.1", "semver": "^7.7.2", "validate-npm-package-name": "^7.0.0" }, "bundled": true }, "sha512-IknQ+upLuJU6t3p0uo9wS3GjFD/1GtxIwcIGYOWR8zL2HxQeJwvxYTgZr9brJ8pyZ4kvpkebM8ZKcyqOeLOHSg=="], - "npm/ip-regex": ["ip-regex@5.0.0", "", {}, "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw=="], + "npm/ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], - "npm/is-cidr": ["is-cidr@6.0.1", "", { "dependencies": { "cidr-regex": "5.0.1" }, "bundled": true }, "sha512-JIJlvXodfsoWFAvvjB7Elqu8qQcys2SZjkIJCLdk4XherUqZ6+zH7WIpXkp4B3ZxMH0Fz7zIsZwyvs6JfM0csw=="], + "npm/is-cidr": ["is-cidr@6.0.4", "", { "dependencies": { "cidr-regex": "^5.0.4" }, "bundled": true }, "sha512-tOIBU3QiXy0W4LvHbcKWAWSuQfGwDiEILphFCAZtDqj7C57uv3ClO6K8aNEGV4VTA7bWJlpQ0suKQkUe6Rd6ag=="], - "npm/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - - "npm/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + "npm/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], "npm/json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", { "bundled": true }, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="], @@ -917,41 +899,41 @@ "npm/libnpmaccess": ["libnpmaccess@10.0.3", "", { "dependencies": { "npm-package-arg": "^13.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-JPHTfWJxIK+NVPdNMNGnkz4XGX56iijPbe0qFWbdt68HL+kIvSzh+euBL8npLZvl2fpaxo+1eZSdoG15f5YdIQ=="], - "npm/libnpmdiff": ["libnpmdiff@8.0.11", "", { "dependencies": { "@npmcli/arborist": "^9.1.8", "@npmcli/installed-package-contents": "^4.0.0", "binary-extensions": "^3.0.0", "diff": "^8.0.2", "minimatch": "^10.0.3", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "tar": "^7.5.1" }, "bundled": true }, "sha512-aLBZ2/Htxn3KwLAktRv7kjn8zvxBGDTX1xCwpLy2pNL2QzihqG0Cf7USPuBvuIDJGAOKf/+OAo0qBxYPCzT7Dg=="], + "npm/libnpmdiff": ["libnpmdiff@8.1.9", "", { "dependencies": { "@npmcli/arborist": "^9.7.0", "@npmcli/installed-package-contents": "^4.0.0", "binary-extensions": "^3.0.0", "diff": "^8.0.2", "minimatch": "^10.0.3", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "tar": "^7.5.1" }, "bundled": true }, "sha512-UfAzZ7mTkq4X534LO2oGFMss0un+b3esOcbT7R7DZc1XJeyTfWA3R/7yiJ3AlFqSlBCiSqilr01vBMsBF6cbqw=="], - "npm/libnpmexec": ["libnpmexec@10.1.10", "", { "dependencies": { "@npmcli/arborist": "^9.1.8", "@npmcli/package-json": "^7.0.0", "@npmcli/run-script": "^10.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "proc-log": "^6.0.0", "promise-retry": "^2.0.1", "read": "^5.0.1", "semver": "^7.3.7", "signal-exit": "^4.1.0", "walk-up-path": "^4.0.0" }, "bundled": true }, "sha512-9HxpH2AMKMcqmI+0KBP1wJYVMFKO5gW1iGHRUSc6shJ3qPwiiol9JH4lj76QYvLSiDeZUObM5mI9bR4fQutPaA=="], + "npm/libnpmexec": ["libnpmexec@10.2.9", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/arborist": "^9.7.0", "@npmcli/package-json": "^7.0.0", "@npmcli/run-script": "^10.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "proc-log": "^6.0.0", "read": "^5.0.1", "semver": "^7.3.7", "signal-exit": "^4.1.0", "walk-up-path": "^4.0.0" }, "bundled": true }, "sha512-ApnGrjjIiAm5qzXGiU8dtN9Kw0HiYNVzbIcVL5lVlDZKvfKLfQyHxASVxGWrjKy4rOaahcx3Nh/E/uY3eSXYfQ=="], - "npm/libnpmfund": ["libnpmfund@7.0.11", "", { "dependencies": { "@npmcli/arborist": "^9.1.8" }, "bundled": true }, "sha512-80Ye6BKpISzQT+sjHKIUc9HO8hbFFVe1JoOLfeQ9F+4c0lpLiERTITcpGpZbH6KzzpYj3l1vuhYQGg+sBok3zg=="], + "npm/libnpmfund": ["libnpmfund@7.0.23", "", { "dependencies": { "@npmcli/arborist": "^9.7.0" }, "bundled": true }, "sha512-6GqIw+r85FaM4smeYwV2iWuFuHR7g3Azs4HzmTU3fNVxNcTec6xY0U39XzaLRQV8gKP0hS0IKqOTyFkQdoOlQw=="], "npm/libnpmorg": ["libnpmorg@8.0.1", "", { "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-/QeyXXg4hqMw0ESM7pERjIT2wbR29qtFOWIOug/xO4fRjS3jJJhoAPQNsnHtdwnCqgBdFpGQ45aIdFFZx2YhTA=="], - "npm/libnpmpack": ["libnpmpack@9.0.11", "", { "dependencies": { "@npmcli/arborist": "^9.1.8", "@npmcli/run-script": "^10.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2" }, "bundled": true }, "sha512-02jxLb9qSBHPdfFXsJU1zzrBla/lKbvKIiW97x813lzsU8qVKoARW8b3QNXUwHVTV3LxPr5UEJosH7P4hAHFxQ=="], + "npm/libnpmpack": ["libnpmpack@9.1.9", "", { "dependencies": { "@npmcli/arborist": "^9.7.0", "@npmcli/run-script": "^10.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2" }, "bundled": true }, "sha512-da2ovGiFLBFebJL2yp1rbbF4cuuhdEpwCHrCu2iXfZBC1vgIlw37RTvs0x58V9bk+lfEDr+Gs6/NjOWs56wpDA=="], - "npm/libnpmpublish": ["libnpmpublish@11.1.3", "", { "dependencies": { "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "semver": "^7.3.7", "sigstore": "^4.0.0", "ssri": "^13.0.0" }, "bundled": true }, "sha512-NVPTth/71cfbdYHqypcO9Lt5WFGTzFEcx81lWd7GDJIgZ95ERdYHGUfCtFejHCyqodKsQkNEx2JCkMpreDty/A=="], + "npm/libnpmpublish": ["libnpmpublish@11.2.0", "", { "dependencies": { "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "semver": "^7.3.7", "sigstore": "^4.0.0", "ssri": "^13.0.0" }, "bundled": true }, "sha512-fAEts7UCM2r1xhI82Dv9PK4gFGZoyD7Z5sfIwBQKoO/f67VNVDC0DeH3uH1Yi+T4kwt5iBuCKkZ5XcpxfvsGHQ=="], "npm/libnpmsearch": ["libnpmsearch@9.0.1", "", { "dependencies": { "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-oKw58X415ERY/BOGV3jQPVMcep8YeMRWMzuuqB0BAIM5VxicOU1tQt19ExCu4SV77SiTOEoziHxGEgJGw3FBYQ=="], "npm/libnpmteam": ["libnpmteam@8.0.2", "", { "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-ypLrDUQoi8EhG+gzx5ENMcYq23YjPV17Mfvx4nOnQiHOi8vp47+4GvZBrMsEM4yeHPwxguF/HZoXH4rJfHdH/w=="], - "npm/libnpmversion": ["libnpmversion@8.0.3", "", { "dependencies": { "@npmcli/git": "^7.0.0", "@npmcli/run-script": "^10.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.3.7" }, "bundled": true }, "sha512-Avj1GG3DT6MGzWOOk3yA7rORcMDUPizkIGbI8glHCO7WoYn3NYNmskLDwxg2NMY1Tyf2vrHAqTuSG58uqd1lJg=="], + "npm/libnpmversion": ["libnpmversion@8.0.4", "", { "dependencies": { "@npmcli/git": "^7.0.0", "@npmcli/run-script": "^10.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.3.7" }, "bundled": true }, "sha512-5NiNpLxXkNeLHCYVTLxX/qRgdAoRAjiR0arFdVpQ5kZOJ2b0pHdXS9G3qC7uiqVsLa/gfQQIbQIEPdmSMKXF2A=="], - "npm/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + "npm/lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], - "npm/make-fetch-happen": ["make-fetch-happen@15.0.3", "", { "dependencies": { "@npmcli/agent": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "promise-retry": "^2.0.1", "ssri": "^13.0.0" }, "bundled": true }, "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw=="], + "npm/make-fetch-happen": ["make-fetch-happen@15.0.6", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" }, "bundled": true }, "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw=="], - "npm/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, "bundled": true }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + "npm/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" }, "bundled": true }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], - "npm/minipass": ["minipass@7.1.2", "", { "bundled": true }, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "npm/minipass": ["minipass@7.1.3", "", { "bundled": true }, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], "npm/minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="], - "npm/minipass-fetch": ["minipass-fetch@5.0.0", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^3.0.1" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A=="], + "npm/minipass-fetch": ["minipass-fetch@5.0.2", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^2.0.0", "minizlib": "^3.0.1" }, "optionalDependencies": { "iconv-lite": "^0.7.2" } }, "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ=="], - "npm/minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="], + "npm/minipass-flush": ["minipass-flush@1.0.7", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA=="], "npm/minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" }, "bundled": true }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="], - "npm/minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="], + "npm/minipass-sized": ["minipass-sized@2.0.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA=="], "npm/minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], @@ -961,7 +943,7 @@ "npm/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "npm/node-gyp": ["node-gyp@12.1.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^15.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.2", "tinyglobby": "^0.2.12", "which": "^6.0.0" }, "bundled": true, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-W+RYA8jBnhSr2vrTtlPYPc1K+CSjGpVDRZxcqJcERZ8ND3A1ThWPHRwctTx3qC3oW99jt726jhdz3Y6ky87J4g=="], + "npm/node-gyp": ["node-gyp@12.4.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "undici": "^6.25.0", "which": "^6.0.0" }, "bundled": true, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw=="], "npm/nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bundled": true, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], @@ -975,7 +957,7 @@ "npm/npm-package-arg": ["npm-package-arg@13.0.2", "", { "dependencies": { "hosted-git-info": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^7.0.0" }, "bundled": true }, "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA=="], - "npm/npm-packlist": ["npm-packlist@10.0.3", "", { "dependencies": { "ignore-walk": "^8.0.0", "proc-log": "^6.0.0" } }, "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg=="], + "npm/npm-packlist": ["npm-packlist@10.0.4", "", { "dependencies": { "ignore-walk": "^8.0.0", "proc-log": "^6.0.0" } }, "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng=="], "npm/npm-pick-manifest": ["npm-pick-manifest@11.0.3", "", { "dependencies": { "npm-install-checks": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "npm-package-arg": "^13.0.0", "semver": "^7.3.5" }, "bundled": true }, "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ=="], @@ -987,15 +969,15 @@ "npm/p-map": ["p-map@7.0.4", "", { "bundled": true }, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], - "npm/pacote": ["pacote@21.0.4", "", { "dependencies": { "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "promise-retry": "^2.0.1", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bundled": true, "bin": { "pacote": "bin/index.js" } }, "sha512-RplP/pDW0NNNDh3pnaoIWYPvNenS7UqMbXyvMqJczosiFWTeGGwJC2NQBLqKf4rGLFfwCOnntw1aEp9Jiqm1MA=="], + "npm/pacote": ["pacote@21.5.1", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bundled": true, "bin": { "pacote": "bin/index.js" } }, "sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg=="], "npm/parse-conflict-json": ["parse-conflict-json@5.0.1", "", { "dependencies": { "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "bundled": true }, "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ=="], - "npm/path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], + "npm/path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], - "npm/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "npm/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], - "npm/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + "npm/postcss-selector-parser": ["postcss-selector-parser@7.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Wjvt4scRFouioIInHf51IFNP4ltJ2EngJM+cZPGiqbKetBfmP3vpdPV8ID2S6JS6/jdo74N8+aEYH9lQr2C6sA=="], "npm/proc-log": ["proc-log@6.1.0", "", { "bundled": true }, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], @@ -1005,8 +987,6 @@ "npm/promise-call-limit": ["promise-call-limit@3.0.2", "", {}, "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw=="], - "npm/promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], - "npm/promzard": ["promzard@3.0.1", "", { "dependencies": { "read": "^5.0.0" } }, "sha512-M5mHhWh+Adz0BIxgSrqcc6GTCSconR7zWQV9vnOSptNtr6cSFlApLc28GbQhuN6oOWBQeV2C0bNE47JCY/zu3Q=="], "npm/qrcode-terminal": ["qrcode-terminal@0.12.0", "", { "bundled": true, "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="], @@ -1015,75 +995,65 @@ "npm/read-cmd-shim": ["read-cmd-shim@6.0.0", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="], - "npm/retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], - "npm/safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "npm/semver": ["semver@7.7.3", "", { "bundled": true, "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "npm/semver": ["semver@7.8.3", "", { "bundled": true, "bin": { "semver": "bin/semver.js" } }, "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg=="], "npm/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "npm/sigstore": ["sigstore@4.0.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.0.0", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.0.0", "@sigstore/tuf": "^4.0.0", "@sigstore/verify": "^3.0.0" } }, "sha512-Gw/FgHtrLM9WP8P5lLcSGh9OQcrTruWCELAiS48ik1QbL0cH+dfjomiRTUE9zzz+D1N6rOLkwXUvVmXZAsNE0Q=="], + "npm/sigstore": ["sigstore@4.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.1", "@sigstore/tuf": "^4.0.2", "@sigstore/verify": "^3.1.1" } }, "sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w=="], "npm/smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], - "npm/socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="], + "npm/socks": ["socks@2.8.9", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw=="], "npm/socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], - "npm/spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="], - "npm/spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], "npm/spdx-expression-parse": ["spdx-expression-parse@4.0.0", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" }, "bundled": true }, "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ=="], - "npm/spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="], - - "npm/ssri": ["ssri@13.0.0", "", { "dependencies": { "minipass": "^7.0.3" }, "bundled": true }, "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng=="], + "npm/spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="], - "npm/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "npm/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "npm/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" }, "bundled": true }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], "npm/supports-color": ["supports-color@10.2.2", "", { "bundled": true }, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], - "npm/tar": ["tar@7.5.2", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "bundled": true }, "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg=="], + "npm/tar": ["tar@7.5.16", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "bundled": true }, "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w=="], "npm/text-table": ["text-table@0.2.0", "", { "bundled": true }, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], "npm/tiny-relative-date": ["tiny-relative-date@2.0.2", "", { "bundled": true }, "sha512-rGxAbeL9z3J4pI2GtBEoFaavHdO4RKAU54hEuOef5kfx5aPqiQtbhYktMOTL5OA33db8BjsDcLXuNp+/v19PHw=="], - "npm/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "npm/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], "npm/treeverse": ["treeverse@3.0.0", "", { "bundled": true }, "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ=="], - "npm/tuf-js": ["tuf-js@4.0.0", "", { "dependencies": { "@tufjs/models": "4.0.0", "debug": "^4.4.1", "make-fetch-happen": "^15.0.0" } }, "sha512-Lq7ieeGvXDXwpoSmOSgLWVdsGGV9J4a77oDTAPe/Ltrqnnm/ETaRlBAQTH5JatEh8KXuE6sddf9qAv1Q2282Hg=="], - - "npm/unique-filename": ["unique-filename@5.0.0", "", { "dependencies": { "unique-slug": "^6.0.0" } }, "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg=="], + "npm/tuf-js": ["tuf-js@4.1.0", "", { "dependencies": { "@tufjs/models": "4.1.0", "debug": "^4.4.3", "make-fetch-happen": "^15.0.1" } }, "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ=="], - "npm/unique-slug": ["unique-slug@6.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw=="], + "npm/undici": ["undici@6.26.0", "", {}, "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A=="], "npm/util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - "npm/validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], - - "npm/validate-npm-package-name": ["validate-npm-package-name@7.0.0", "", { "bundled": true }, "sha512-bwVk/OK+Qu108aJcMAEiU4yavHUI7aN20TgZNBj9MR2iU1zPUl1Z1Otr7771ExfYTPTvfN8ZJ1pbr5Iklgt4xg=="], + "npm/validate-npm-package-name": ["validate-npm-package-name@7.0.2", "", { "bundled": true }, "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A=="], "npm/walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="], - "npm/which": ["which@6.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bundled": true, "bin": { "node-which": "bin/which.js" } }, "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg=="], + "npm/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bundled": true, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], - "npm/write-file-atomic": ["write-file-atomic@7.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg=="], + "npm/write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], "npm/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + "npm-run-path/unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="], - "read-package-up/type-fest": ["type-fest@5.2.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-xxCJm+Bckc6kQBknN7i9fnP/xobQRsRQxR01CztFkp/h++yfVxUUcmMgfR2HttJx/dpWjS9ubVuyspJv24Q9DA=="], + "read-package-up/type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="], - "read-pkg/type-fest": ["type-fest@5.2.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-xxCJm+Bckc6kQBknN7i9fnP/xobQRsRQxR01CztFkp/h++yfVxUUcmMgfR2HttJx/dpWjS9ubVuyspJv24Q9DA=="], + "read-pkg/type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="], "semantic-release/@semantic-release/error": ["@semantic-release/error@4.0.0", "", {}, "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ=="], @@ -1095,11 +1065,15 @@ "signale/figures": ["figures@2.0.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA=="], + "supports-hyperlinks/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "tempy/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="], "tempy/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], - "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "tinyglobby/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -1123,10 +1097,10 @@ "@semantic-release/release-notes-generator/read-package-up/read-pkg": ["read-pkg@9.0.1", "", { "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" } }, "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA=="], - "bun-types/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], - "cli-highlight/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "cli-highlight/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "cli-highlight/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], "cli-highlight/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -1149,20 +1123,10 @@ "env-ci/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], - "npm/@sigstore/sign/proc-log": ["proc-log@5.0.0", "", {}, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="], - - "npm/@tufjs/models/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - "npm/minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], "npm/minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - "npm/minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - - "npm/spdx-correct/spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], - - "npm/validate-npm-package-license/spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], - "semantic-release/aggregate-error/clean-stack": ["clean-stack@5.3.0", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg=="], "semantic-release/aggregate-error/indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], @@ -1197,8 +1161,6 @@ "npm/minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - "npm/minipass-sized/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - "signale/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], "@semantic-release/release-notes-generator/read-package-up/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="], diff --git a/package.json b/package.json index bed8ca0..6483691 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,15 @@ { "name": "wavespeed-cli", "version": "1.3.2", - "description": "CLI for Wavespeed AI Bytedance Seedream V4 API", + "description": "MCP server and CLI for Wavespeed AI image generation and editing", "type": "module", "bin": { "wavespeed": "dist/index.js" }, "files": [ "dist", - "README.md" + "README.md", + "skills" ], "repository": { "type": "git", @@ -19,7 +20,8 @@ "url": "https://github.com/aditzel/wavespeed-cli/issues" }, "publishConfig": { - "access": "public" + "access": "public", + "provenance": true }, "keywords": [ "wavespeed", @@ -27,7 +29,7 @@ "image-generation", "video-generation", "cli", - "seedream" + "mcp" ], "scripts": { "build": "bun build src/index.ts --outdir dist --target bun --format esm && chmod +x dist/index.js", @@ -37,7 +39,13 @@ "test:watch": "bun test --watch", "lint": "biome check src tests", "lint:fix": "biome check --write src tests", - "format": "biome format src tests" + "format": "biome format src tests", + "release": "semantic-release" + }, + "overrides": { + "@semantic-release/npm": "13.1.5", + "js-yaml": "4.1.1", + "npm": "11.16.0" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.28.0", @@ -51,11 +59,12 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^7.1.0", "@semantic-release/git": "^10.0.1", + "@semantic-release/npm": "13.1.5", "@types/bun": "^1.3.11", "@types/prompts": "^2.4.9", "bun-types": "^1.3.11", - "semantic-release": "^25.0.3", - "typescript": "^6.0.2" + "semantic-release": "25.0.3", + "typescript": "6.0.2" }, "license": "MIT" } diff --git a/skills/wavespeed-image-generation/SKILL.md b/skills/wavespeed-image-generation/SKILL.md new file mode 100644 index 0000000..3b217a7 --- /dev/null +++ b/skills/wavespeed-image-generation/SKILL.md @@ -0,0 +1,77 @@ +--- +name: wavespeed-image-generation +description: Generate and edit AI images with wavespeed-cli, preferring its MCP server and falling back to CLI commands when MCP is unavailable or the agent prefers shell execution. Use when a user asks to create images, edit existing images, generate image sequences or storyboards, choose Wavespeed models, configure Wavespeed API access, save outputs, or connect image generation to an agentic coding workflow. +license: MIT +compatibility: Requires a Wavespeed API key in WAVESPEED_API_KEY and either a configured wavespeed MCP server or the wavespeed CLI running on Bun. +--- + +# Wavespeed Image Generation + +Use `wavespeed-cli` to generate and edit images. Prefer MCP tools because they return structured, token-efficient results and can save files for the user. Use CLI commands when MCP is not configured, when the user asks for terminal commands, or when shell execution is more convenient. + +## Safety and cost rules + +- Never print, persist, or commit API keys. Use `WAVESPEED_API_KEY`. +- Ask before running large batches, high-resolution generations, or repeated retries that may incur cost. +- Save outputs to an explicit directory when the user expects files. +- Prefer URL or file-path outputs over base64 unless the user specifically needs inline image data. +- If the user asks for disallowed, unsafe, or rights-infringing content, refuse or redirect to a safe alternative. + +## MCP-first workflow + +1. Check whether a Wavespeed MCP server/toolset is available. +2. Call the model listing tool before selecting a non-default model. Prefer a model whose type matches the task (`text-to-image`, `image-to-image`, sequential/storyboard-capable, or a specific requested model family). +3. Use MCP image tools with structured arguments: + - `list_models`: discover valid model ids and current model types/prices. + - `generate`: text-to-image. + - `edit`: image-to-image with URLs, local files converted by the tool, or base64 data. + - `generate_sequential`: multiple consistent images from a prompt. + - `edit_sequential`: sequential outputs with optional reference images. +4. Choose output mode: + - `paths` when the user wants downloaded files. + - `urls` when the user only needs links or a compact response. + - `base64` only for systems that require inline image bytes. +5. Return the task id, model used if known, output paths/URLs, and any warnings. + +Typical MCP arguments: + +```json +{ + "prompt": "a cinematic robot gardener in a greenhouse", + "size": "2048*2048", + "model": "optional-model-id", + "output": "paths", + "outputDir": "./output" +} +``` + +## CLI fallback + +If MCP is unavailable, use the `wavespeed` command: + +```bash +export WAVESPEED_API_KEY="..." # user-provided; do not echo it back +wavespeed models +wavespeed generate --prompt "a cinematic robot gardener in a greenhouse" --output-dir ./output +wavespeed edit --prompt "make it watercolor" --images ./input.png --output-dir ./output +wavespeed generate-sequential --prompt "spaceship launch storyboard in 4 panels" --max-images 4 --output-dir ./output +wavespeed edit-sequential --prompt "turn this into a comic strip" --images ./input.png --max-images 4 --output-dir ./output +``` + +Use `wavespeed models --json` when parsing model metadata programmatically. + +## Model selection guidance + +- Do not assume one default model is best for every task. Wavespeed has many current models; discover available models at runtime. +- For one-off image generation, default model resolution is acceptable if the user has no preference. +- For explicit model requests, pass the requested model id via MCP `model` or CLI `--model`. +- Raw Wavespeed model ids such as `provider/model/text-to-image` or `provider/model/edit` can be passed directly. +- If a plain model alias is unknown, list models and ask the user to choose. + +## Troubleshooting + +- Missing API key: ask the user to set `WAVESPEED_API_KEY`; do not ask them to paste secrets into a committed file. +- Unknown model: run `list_models` or `wavespeed models` and retry with a valid id. +- Invalid size: use `WIDTH*HEIGHT` or `WIDTHxHEIGHT`; each dimension must be 1024-4096. +- No saved files: retry with output mode `paths` or CLI `--output-dir` and report any download errors. +- API/task failure: report the task id and API error, then suggest a smaller size, safer prompt, or different model. diff --git a/src/api/client.ts b/src/api/client.ts index 009dbfe..f272c94 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -1,16 +1,47 @@ import type { ResolvedModel } from "../config/types"; import type { CommandType } from "../core/types"; +import { debugLog, redactUrl, truncateForLog } from "../utils/logging.ts"; import { isAiRemoverModel } from "../utils/model-routing.ts"; import { type ApiEnvelope, endpoints, type TaskData } from "./types.ts"; +const MODEL_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/; + +function joinApiUrl(baseUrl: string, apiPath: string): string { + const trimmedBase = baseUrl.replace(/\/+$/, ""); + const normalizedPath = apiPath.startsWith("/") ? apiPath : `/${apiPath}`; + return `${trimmedBase}${normalizedPath}`; +} + +function encodeModelPath(modelRef: string): string { + const segments = modelRef.split("/"); + if (!segments.length) { + throw new Error("Model id is required"); + } + + for (const segment of segments) { + if ( + !segment || + segment === "." || + segment === ".." || + /%2f|%5c/i.test(segment) || + !MODEL_SEGMENT_PATTERN.test(segment) + ) { + throw new Error(`Invalid model id segment '${segment}'`); + } + } + + return segments.map((segment) => encodeURIComponent(segment)).join("/"); +} + async function httpJson( method: string, model: ResolvedModel, url: string, body?: unknown, ): Promise { - const fullUrl = `${model.apiBaseUrl}${url}`; - console.error(`[DEBUG] HTTP ${method} ${fullUrl} (model=${model.id})`); + const fullUrl = joinApiUrl(model.apiBaseUrl, url); + const safeUrl = redactUrl(fullUrl); + debugLog(`[DEBUG] HTTP ${method} ${safeUrl} (model=${model.id})`); const res = await fetch(fullUrl, { method, @@ -23,20 +54,21 @@ async function httpJson( }); const text = await res.text(); - console.error(`[DEBUG] HTTP ${method} ${fullUrl} -> ${res.status} ${res.statusText}`); + debugLog(`[DEBUG] HTTP ${method} ${safeUrl} -> ${res.status} ${res.statusText}`); let json: unknown; try { json = text ? JSON.parse(text) : {}; } catch { - console.error(`[DEBUG] Failed to parse JSON response: ${text.substring(0, 200)}`); + debugLog(`[DEBUG] Failed to parse JSON response: ${truncateForLog(text, 200)}`); throw new Error(`Non JSON response with status ${res.status}`); } if (!res.ok) { const errorBody = json as Record | undefined; - const msg = errorBody?.error || errorBody?.message || res.statusText; - console.error(`[DEBUG] HTTP error ${res.status}: ${msg}`); + const rawMsg = errorBody?.error || errorBody?.message || res.statusText; + const msg = truncateForLog(rawMsg, 500); + debugLog(`[DEBUG] HTTP error ${res.status}: ${msg}`); throw new Error(`HTTP ${res.status}: ${msg}`); } @@ -79,7 +111,7 @@ export async function getModels(apiKey: string): Promise { // We use the default Wavespeed API base URL const url = `https://api.wavespeed.ai${endpoints.models}`; - console.error(`[DEBUG] Fetching models from ${url}`); + debugLog(`[DEBUG] Fetching models from ${url}`); try { const res = await fetch(url, { @@ -90,24 +122,25 @@ export async function getModels(apiKey: string): Promise { }, }); - console.error(`[DEBUG] GET ${url} -> ${res.status} ${res.statusText}`); + debugLog(`[DEBUG] GET ${url} -> ${res.status} ${res.statusText}`); if (!res.ok) { const text = await res.text(); - console.error(`[DEBUG] Failed to fetch models: HTTP ${res.status}: ${text}`); - throw new Error(`HTTP ${res.status}: ${text}`); + const msg = truncateForLog(text, 500); + debugLog(`[DEBUG] Failed to fetch models: HTTP ${res.status}: ${msg}`); + throw new Error(`HTTP ${res.status}: ${msg}`); } const json = await res.json(); // The API returns { code: 200, data: [...] } if (json && Array.isArray(json.data)) { - console.error(`[DEBUG] Successfully fetched ${json.data.length} models`); + debugLog(`[DEBUG] Successfully fetched ${json.data.length} models`); return json.data; } - console.error(`[DEBUG] Unexpected response format, returning empty array`); + debugLog(`[DEBUG] Unexpected response format, returning empty array`); return []; } catch (err) { - console.error(`[DEBUG] Exception fetching models: ${(err as Error).message}`); + debugLog(`[DEBUG] Exception fetching models: ${(err as Error).message}`); throw new Error(`Failed to fetch models: ${(err as Error).message}`); } } @@ -135,7 +168,7 @@ export function buildSubmitTarget( return { model: canonicalModel, - path: `/api/v3/${canonicalModel}`, + path: `/api/v3/${encodeModelPath(canonicalModel)}`, }; } diff --git a/src/api/types.ts b/src/api/types.ts index 7b77b78..295147e 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,4 +1,4 @@ -export type JobStatus = "created" | "processing" | "completed" | "failed"; +export type JobStatus = "created" | "processing" | "completed" | "succeeded" | "failed"; export interface Timings { inference?: number; @@ -52,8 +52,17 @@ export interface ModelsListResponse { data: ModelSchema[]; } +const REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]+$/; + +function encodeRequestId(id: string): string { + if (!id || !REQUEST_ID_PATTERN.test(id)) { + throw new Error("Invalid prediction request id"); + } + return encodeURIComponent(id); +} + export const BASE_URL = "https://api.wavespeed.ai"; export const endpoints = { - result: (id: string) => `/api/v3/predictions/${id}/result`, + result: (id: string) => `/api/v3/predictions/${encodeRequestId(id)}/result`, models: "/api/v3/models", }; diff --git a/src/commands/mcp.ts b/src/commands/mcp.ts index 0aed8a3..c5878be 100644 --- a/src/commands/mcp.ts +++ b/src/commands/mcp.ts @@ -10,6 +10,7 @@ export function registerMCP(program: Command) { .description("Start the Wavespeed MCP server (stdio-based)") .action(async () => { try { + process.env.WAVESPEED_MCP_MODE = "1"; const server = new WavespeedMCPServer(); await server.start(); } catch (err) { diff --git a/src/commands/models.ts b/src/commands/models.ts index d4fbcd9..cb34fa5 100644 --- a/src/commands/models.ts +++ b/src/commands/models.ts @@ -93,7 +93,7 @@ export function registerModels(program: Command) { // Add registry models for (const rm of registryModels) { displayMap.set(rm.modelName, { - // Key by modelName (e.g. "bytedance/seedream-v4") + // Key by modelName (e.g. "provider/model-id") name: rm.name, desc: rm.description || "", source: ["Registry"], diff --git a/src/config/models.ts b/src/config/models.ts index 8bd3e82..edc1f40 100644 --- a/src/config/models.ts +++ b/src/config/models.ts @@ -1,3 +1,5 @@ +import { lookup } from "node:dns/promises"; +import net from "node:net"; import { ModelCache } from "../cache"; import { inferApiModelType } from "../utils/model-routing.ts"; import { ConfigError } from "./load"; @@ -13,6 +15,7 @@ export interface ApiModelCache { } const BUILTIN_MODEL_ID = "seedream-v4"; +const WAVESPEED_HOST_SUFFIX = ".wavespeed.ai"; const CANONICAL_MODEL_SUFFIXES = [ "/edit", "/sequential", @@ -38,6 +41,209 @@ const BUILTIN_MODEL_BASE: Omit = { submitMode: "base", }; +function envFlag(name: string): boolean { + return /^(1|true|yes|on)$/i.test(process.env[name] ?? ""); +} + +function normalizeHostnameForPolicy(hostname: string): string { + return hostname + .toLowerCase() + .replace(/^\[|\]$/g, "") + .replace(/\.+$/g, ""); +} + +function isKnownWavespeedHost(hostname: string): boolean { + const normalized = normalizeHostnameForPolicy(hostname); + return normalized === "wavespeed.ai" || normalized.endsWith(WAVESPEED_HOST_SUFFIX); +} + +function isPrivateIPv4(address: string): boolean { + const parts = address.split(".").map((part) => Number(part)); + if ( + parts.length !== 4 || + parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255) + ) { + return true; + } + + const [a, b] = parts; + return ( + a === 0 || + a === 10 || + a === 127 || + (a === 169 && b === 254) || + (a === 172 && b >= 16 && b <= 31) || + (a === 192 && b === 168) || + (a === 100 && b >= 64 && b <= 127) || + (a === 192 && b === 0) || + (a === 198 && (b === 18 || b === 19)) || + a >= 224 + ); +} + +function extractIPv4FromMappedIPv6(address: string): string | undefined { + const match = /^::ffff:(?:(?:0:){0,2})?(.+)$/i.exec(address); + if (!match) return undefined; + + const embedded = match[1]; + if (net.isIP(embedded) === 4) { + return embedded; + } + + const hextets = embedded.split(":"); + if (hextets.length !== 2) { + return undefined; + } + + const high = Number.parseInt(hextets[0], 16); + const low = Number.parseInt(hextets[1], 16); + if ( + !Number.isInteger(high) || + !Number.isInteger(low) || + high < 0 || + high > 0xffff || + low < 0 || + low > 0xffff + ) { + return undefined; + } + + return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`; +} + +function isPrivateIPv6(address: string): boolean { + const normalized = address.toLowerCase(); + const mappedIPv4 = extractIPv4FromMappedIPv6(normalized); + if (mappedIPv4) { + return isPrivateIPv4(mappedIPv4); + } + + return ( + normalized === "::" || + normalized === "::1" || + normalized.startsWith("fc") || + normalized.startsWith("fd") || + normalized.startsWith("fe8") || + normalized.startsWith("fe9") || + normalized.startsWith("fea") || + normalized.startsWith("feb") || + normalized.startsWith("2001:db8:") + ); +} + +function isLocalOrPrivateHost(hostname: string): boolean { + const normalized = normalizeHostnameForPolicy(hostname); + if (normalized === "localhost" || normalized.endsWith(".localhost")) { + return true; + } + + const family = net.isIP(normalized); + if (family === 4) return isPrivateIPv4(normalized); + if (family === 6) return isPrivateIPv6(normalized); + return false; +} + +async function resolvedHostIsLocalOrPrivate(hostname: string): Promise { + const normalized = normalizeHostnameForPolicy(hostname); + if (isLocalOrPrivateHost(normalized)) { + return true; + } + + const addresses = await lookup(normalized, { all: true, verbatim: true }); + return addresses.some((entry) => { + const family = net.isIP(entry.address); + if (family === 4) return isPrivateIPv4(entry.address); + if (family === 6) return isPrivateIPv6(entry.address); + return true; + }); +} + +async function validateResolvedApiBaseUrlHost(modelId: string, apiBaseUrl: string): Promise { + if (envFlag("WAVESPEED_ALLOW_INSECURE_API_BASE_URL")) { + return; + } + + const parsed = new URL(apiBaseUrl); + if (isKnownWavespeedHost(parsed.hostname)) { + return; + } + + let resolvesPrivate = false; + try { + resolvesPrivate = await resolvedHostIsLocalOrPrivate(parsed.hostname); + } catch (err) { + throw new ConfigError( + `Model '${modelId}' apiBaseUrl host '${parsed.hostname}' could not be resolved for private-network validation: ${(err as Error).message}`, + 3, + ); + } + + if (resolvesPrivate) { + throw new ConfigError( + `Model '${modelId}' apiBaseUrl must not resolve to localhost/private networks. Set WAVESPEED_ALLOW_INSECURE_API_BASE_URL=1 only for trusted local testing.`, + 3, + ); + } +} + +function validateApiBaseUrl( + modelId: string, + provider: ResolvedModel["provider"], + apiBaseUrl: string, + apiKeyEnv: string, +): string { + let parsed: URL; + try { + parsed = new URL(apiBaseUrl); + } catch { + throw new ConfigError(`Model '${modelId}' has invalid apiBaseUrl '${apiBaseUrl}'.`, 3); + } + + if (parsed.protocol !== "https:" && parsed.protocol !== "http:") { + throw new ConfigError(`Model '${modelId}' apiBaseUrl must use http(s).`, 3); + } + + const allowInsecure = envFlag("WAVESPEED_ALLOW_INSECURE_API_BASE_URL"); + if (parsed.protocol !== "https:" && !allowInsecure) { + throw new ConfigError( + `Model '${modelId}' apiBaseUrl must use HTTPS. Set WAVESPEED_ALLOW_INSECURE_API_BASE_URL=1 only for trusted local testing.`, + 3, + ); + } + + if (isLocalOrPrivateHost(parsed.hostname) && !allowInsecure) { + throw new ConfigError( + `Model '${modelId}' apiBaseUrl must not point to localhost/private networks. Set WAVESPEED_ALLOW_INSECURE_API_BASE_URL=1 only for trusted local testing.`, + 3, + ); + } + + if (parsed.username || parsed.password) { + throw new ConfigError(`Model '${modelId}' apiBaseUrl must not include credentials.`, 3); + } + + if (parsed.search || parsed.hash) { + throw new ConfigError(`Model '${modelId}' apiBaseUrl must not include query or fragment.`, 3); + } + + const isWavespeedHost = isKnownWavespeedHost(parsed.hostname); + const allowCustom = envFlag("WAVESPEED_ALLOW_CUSTOM_API_BASE_URL"); + if ( + (envFlag("WAVESPEED_MCP_MODE") || + provider === "wavespeed" || + apiKeyEnv === "WAVESPEED_API_KEY") && + !isWavespeedHost && + !allowCustom + ) { + throw new ConfigError( + `Model '${modelId}' would send ${apiKeyEnv} to non-Wavespeed host '${parsed.hostname}'. Set WAVESPEED_ALLOW_CUSTOM_API_BASE_URL=1 only for trusted custom gateways.`, + 3, + ); + } + + return parsed.toString().replace(/\/+$/, ""); +} + /** * Commands that submit model-backed generation tasks. */ @@ -136,7 +342,9 @@ export async function resolveModelForRequest( // through config, registry, or built-in defaults when cache reads fail. } - return resolveModel(commandName, cliModelFlag, config, apiCache); + const model = resolveModel(commandName, cliModelFlag, config, apiCache); + await validateResolvedApiBaseUrlHost(model.id, model.apiBaseUrl); + return model; } /** @@ -155,7 +363,7 @@ export function resolveModel( cliModelFlag, config, apiCache, - `Unknown model '${cliModelFlag}'. Use --list-models to see available models.`, + `Unknown model '${cliModelFlag}'. Use 'wavespeed models' to see available models.`, ); } @@ -339,6 +547,8 @@ function resolveFromConfigModel( } } + apiBaseUrl = validateApiBaseUrl(id, provider as ResolvedModel["provider"], apiBaseUrl, apiKeyEnv); + const type = model.type ?? "image"; const requestDefaults = model.requestDefaults ?? {}; diff --git a/src/core/operations.ts b/src/core/operations.ts index 4437136..cc9a25d 100644 --- a/src/core/operations.ts +++ b/src/core/operations.ts @@ -1,4 +1,5 @@ import { submitTask } from "../api/client"; +import { debugLog } from "../utils/logging.ts"; import { buildEditPayload } from "../utils/model-routing.ts"; import { pollUntilDone } from "../utils/polling"; import type { @@ -23,19 +24,15 @@ export async function generateImage(params: GenerateParams): Promise { try { const payload = buildEditPayload(params); - console.error( + debugLog( `[DEBUG] editImage: Submitting task with model=${model.id}, images=${images.length}, aiRemover=${"image" in payload}, payloadKeys=${Object.keys(payload).join(",")}`, ); const created = await submitTask(model, "edit", payload); - console.error(`[DEBUG] editImage: Task submitted, id=${created.id}, status=${created.status}`); + debugLog(`[DEBUG] editImage: Task submitted, id=${created.id}, status=${created.status}`); const final = await pollUntilDone(model, created.id); if (final.status === "failed") { - console.error( - `[DEBUG] editImage: Task ${final.id} failed: ${final.error || "Unknown error"}`, - ); + debugLog(`[DEBUG] editImage: Task ${final.id} failed: ${final.error || "Unknown error"}`); return { success: false, taskId: final.id, @@ -96,7 +91,7 @@ export async function editImage(params: EditParams): Promise { }; } - console.error( + debugLog( `[DEBUG] editImage: Task ${final.id} completed successfully, outputs=${final.outputs?.length || 0}`, ); return { @@ -108,7 +103,7 @@ export async function editImage(params: EditParams): Promise { hasNsfw: final.has_nsfw_contents, }; } catch (err) { - console.error(`[DEBUG] editImage: Exception caught: ${(err as Error).message}`); + debugLog(`[DEBUG] editImage: Exception caught: ${(err as Error).message}`); return { success: false, taskId: "", @@ -143,17 +138,17 @@ export async function generateSequential( }; try { - console.error( + debugLog( `[DEBUG] generateSequential: Submitting task with model=${model.id}, maxImages=${maxImages}, size=${size}, syncMode=${syncMode}`, ); const created = await submitTask(model, "generate-sequential", payload); - console.error( + debugLog( `[DEBUG] generateSequential: Task submitted, id=${created.id}, status=${created.status}`, ); const final = await pollUntilDone(model, created.id); if (final.status === "failed") { - console.error( + debugLog( `[DEBUG] generateSequential: Task ${final.id} failed: ${final.error || "Unknown error"}`, ); return { @@ -165,7 +160,7 @@ export async function generateSequential( }; } - console.error( + debugLog( `[DEBUG] generateSequential: Task ${final.id} completed successfully, outputs=${final.outputs?.length || 0}`, ); return { @@ -177,7 +172,7 @@ export async function generateSequential( hasNsfw: final.has_nsfw_contents, }; } catch (err) { - console.error(`[DEBUG] generateSequential: Exception caught: ${(err as Error).message}`); + debugLog(`[DEBUG] generateSequential: Exception caught: ${(err as Error).message}`); return { success: false, taskId: "", @@ -215,17 +210,15 @@ export async function editSequential(params: EditSequentialParams): Promise { // Handle errors if (!result.success) { @@ -99,13 +100,18 @@ export async function formatForMCP( case "paths": { // URLs + saved file paths - const { savedPaths } = await saveImagesFromOutputs(result.outputs, outputDir, result.taskId); + const { savedByIndex } = await saveImagesFromOutputs( + result.outputs, + outputDir, + result.taskId, + saveOptions, + ); for (let i = 0; i < result.outputs.length; i++) { images.push({ index: i, url: result.outputs[i], - path: savedPaths[i] || undefined, + path: savedByIndex[i], }); } break; diff --git a/src/mcp/tools.ts b/src/mcp/tools.ts index fb97f6a..21099db 100644 --- a/src/mcp/tools.ts +++ b/src/mcp/tools.ts @@ -13,7 +13,14 @@ import { generateSequential, type OutputMode, } from "../core"; -import { ensurePrompt, parseImagesList, parseMaxImages, parseSize } from "../utils/validation"; +import { debugLog, redactUrl } from "../utils/logging.ts"; +import { + ensurePrompt, + type ParseImagesListOptions, + parseImagesList, + parseMaxImages, + parseSize, +} from "../utils/validation"; // Shared schemas const outputModeSchema = z.enum(["urls", "paths", "base64"]).default("urls"); @@ -21,6 +28,22 @@ const sizeSchema = z.string().default("2048*2048"); const modelSchema = z.string().optional(); const outputDirSchema = z.string().default("./output"); +function getMcpOutputRoot(): string { + return process.env.WAVESPEED_MCP_OUTPUT_DIR || process.cwd(); +} + +function getMcpImageParseOptions(): ParseImagesListOptions { + const localFileRoot = process.env.WAVESPEED_MCP_INPUT_DIR; + return { + allowLocalFiles: Boolean(localFileRoot), + localFileRoot, + }; +} + +function redactModelBaseUrl(apiBaseUrl: string): string { + return redactUrl(apiBaseUrl); +} + /** * Register all MCP tools */ @@ -53,7 +76,7 @@ function registerListModelsTool(server: McpServer): void { search: z .string() .optional() - .describe("Search models by name or ID (e.g., 'flux', 'seedream')"), + .describe("Search models by name or ID (e.g., 'flux', 'banana', 'qwen')"), limit: z .number() .int() @@ -186,11 +209,13 @@ function registerGenerateTool(server: McpServer): void { size: sizeSchema.describe("Image size WxH (1024-4096, default: 2048*2048)"), model: modelSchema.describe("Model ID (optional, uses config default)"), output: outputModeSchema.describe("Output format: urls, paths (save files), or base64"), - outputDir: outputDirSchema.describe("Directory to save images (for paths mode)"), + outputDir: outputDirSchema.describe( + "Directory to save images, relative to MCP output root (for paths mode)", + ), }, }, async ({ prompt, size, model, output, outputDir }) => { - console.error( + debugLog( `[DEBUG] MCP generate tool invoked: prompt length=${prompt.length}, size=${size}, model=${model || "default"}, output=${output}`, ); try { @@ -198,15 +223,15 @@ function registerGenerateTool(server: McpServer): void { const validatedSize = parseSize(size); const outputMode = output as OutputMode; - console.error(`[DEBUG] MCP generate: Loading config...`); + debugLog(`[DEBUG] MCP generate: Loading config...`); const { config } = loadConfig(); - console.error(`[DEBUG] MCP generate: Config loaded, resolving model...`); + debugLog(`[DEBUG] MCP generate: Config loaded, resolving model...`); const resolvedModel = await resolveModelForRequest("generate", model, config); - console.error( - `[DEBUG] MCP generate: Model resolved: id=${resolvedModel.id}, provider=${resolvedModel.provider}, apiBaseUrl=${resolvedModel.apiBaseUrl}`, + debugLog( + `[DEBUG] MCP generate: Model resolved: id=${resolvedModel.id}, provider=${resolvedModel.provider}, apiBaseUrl=${redactModelBaseUrl(resolvedModel.apiBaseUrl)}`, ); - console.error(`[DEBUG] MCP generate: Calling generateImage operation...`); + debugLog(`[DEBUG] MCP generate: Calling generateImage operation...`); const result = await generateImage({ prompt: validatedPrompt, size: validatedSize, @@ -216,7 +241,7 @@ function registerGenerateTool(server: McpServer): void { }); if (!result.success) { - console.error(`[DEBUG] MCP generate: Operation failed: ${result.error}`); + debugLog(`[DEBUG] MCP generate: Operation failed: ${result.error}`); return { content: [ { @@ -234,9 +259,11 @@ function registerGenerateTool(server: McpServer): void { }; } - console.error(`[DEBUG] MCP generate: Operation succeeded, formatting response...`); - const formatted = await formatForMCP(result, outputMode, outputDir); - console.error( + debugLog(`[DEBUG] MCP generate: Operation succeeded, formatting response...`); + const formatted = await formatForMCP(result, outputMode, outputDir, { + outputRoot: getMcpOutputRoot(), + }); + debugLog( `[DEBUG] MCP generate: Response formatted, returning ${formatted.images.length} images`, ); return { @@ -244,7 +271,7 @@ function registerGenerateTool(server: McpServer): void { }; } catch (err) { const error = err as Error | ConfigError; - console.error( + debugLog( `[DEBUG] MCP generate: Exception caught: ${error.message}${error instanceof ConfigError ? " (ConfigError)" : ""}`, ); if (error instanceof ConfigError) { @@ -279,28 +306,30 @@ function registerEditTool(server: McpServer): void { size: sizeSchema.describe("Output image size WxH (default: 2048*2048)"), model: modelSchema.describe("Model ID (optional)"), output: outputModeSchema.describe("Output format: urls, paths, or base64"), - outputDir: outputDirSchema.describe("Directory to save images"), + outputDir: outputDirSchema.describe( + "Directory to save images, relative to MCP output root", + ), }, }, async ({ prompt, images, size, model, output, outputDir }) => { - console.error( + debugLog( `[DEBUG] MCP edit tool invoked: prompt length=${prompt.length}, images=${images.length}, size=${size}, model=${model || "default"}, output=${output}`, ); try { const validatedPrompt = ensurePrompt(prompt); - const validatedImages = await parseImagesList(images.join(","), true, 10); + const validatedImages = await parseImagesList(images, true, 10, getMcpImageParseOptions()); const validatedSize = parseSize(size); const outputMode = output as OutputMode; - console.error(`[DEBUG] MCP edit: Loading config...`); + debugLog(`[DEBUG] MCP edit: Loading config...`); const { config } = loadConfig(); - console.error(`[DEBUG] MCP edit: Config loaded, resolving model...`); + debugLog(`[DEBUG] MCP edit: Config loaded, resolving model...`); const resolvedModel = await resolveModelForRequest("edit", model, config); - console.error( - `[DEBUG] MCP edit: Model resolved: id=${resolvedModel.id}, provider=${resolvedModel.provider}, apiBaseUrl=${resolvedModel.apiBaseUrl}`, + debugLog( + `[DEBUG] MCP edit: Model resolved: id=${resolvedModel.id}, provider=${resolvedModel.provider}, apiBaseUrl=${redactModelBaseUrl(resolvedModel.apiBaseUrl)}`, ); - console.error(`[DEBUG] MCP edit: Calling editImage operation...`); + debugLog(`[DEBUG] MCP edit: Calling editImage operation...`); const result = await editImage({ prompt: validatedPrompt, images: validatedImages, @@ -311,7 +340,7 @@ function registerEditTool(server: McpServer): void { }); if (!result.success) { - console.error(`[DEBUG] MCP edit: Operation failed: ${result.error}`); + debugLog(`[DEBUG] MCP edit: Operation failed: ${result.error}`); return { content: [ { @@ -323,9 +352,11 @@ function registerEditTool(server: McpServer): void { }; } - console.error(`[DEBUG] MCP edit: Operation succeeded, formatting response...`); - const formatted = await formatForMCP(result, outputMode, outputDir); - console.error( + debugLog(`[DEBUG] MCP edit: Operation succeeded, formatting response...`); + const formatted = await formatForMCP(result, outputMode, outputDir, { + outputRoot: getMcpOutputRoot(), + }); + debugLog( `[DEBUG] MCP edit: Response formatted, returning ${formatted.images.length} images`, ); return { @@ -333,7 +364,7 @@ function registerEditTool(server: McpServer): void { }; } catch (err) { const error = err as Error | ConfigError; - console.error( + debugLog( `[DEBUG] MCP edit: Exception caught: ${error.message}${error instanceof ConfigError ? " (ConfigError)" : ""}`, ); if (error instanceof ConfigError) { @@ -368,11 +399,13 @@ function registerGenerateSequentialTool(server: McpServer): void { size: sizeSchema.describe("Image size WxH (default: 2048*2048)"), model: modelSchema.describe("Model ID (optional)"), output: outputModeSchema.describe("Output format: urls, paths, or base64"), - outputDir: outputDirSchema.describe("Directory to save images"), + outputDir: outputDirSchema.describe( + "Directory to save images, relative to MCP output root", + ), }, }, async ({ prompt, count, size, model, output, outputDir }) => { - console.error( + debugLog( `[DEBUG] MCP generate_sequential tool invoked: prompt length=${prompt.length}, count=${count}, size=${size}, model=${model || "default"}, output=${output}`, ); try { @@ -381,15 +414,15 @@ function registerGenerateSequentialTool(server: McpServer): void { const validatedSize = parseSize(size); const outputMode = output as OutputMode; - console.error(`[DEBUG] MCP generate_sequential: Loading config...`); + debugLog(`[DEBUG] MCP generate_sequential: Loading config...`); const { config } = loadConfig(); - console.error(`[DEBUG] MCP generate_sequential: Config loaded, resolving model...`); + debugLog(`[DEBUG] MCP generate_sequential: Config loaded, resolving model...`); const resolvedModel = await resolveModelForRequest("generate-sequential", model, config); - console.error( - `[DEBUG] MCP generate_sequential: Model resolved: id=${resolvedModel.id}, provider=${resolvedModel.provider}, apiBaseUrl=${resolvedModel.apiBaseUrl}`, + debugLog( + `[DEBUG] MCP generate_sequential: Model resolved: id=${resolvedModel.id}, provider=${resolvedModel.provider}, apiBaseUrl=${redactModelBaseUrl(resolvedModel.apiBaseUrl)}`, ); - console.error(`[DEBUG] MCP generate_sequential: Calling generateSequential operation...`); + debugLog(`[DEBUG] MCP generate_sequential: Calling generateSequential operation...`); const result = await generateSequential({ prompt: validatedPrompt, maxImages: validatedCount, @@ -400,7 +433,7 @@ function registerGenerateSequentialTool(server: McpServer): void { }); if (!result.success) { - console.error(`[DEBUG] MCP generate_sequential: Operation failed: ${result.error}`); + debugLog(`[DEBUG] MCP generate_sequential: Operation failed: ${result.error}`); return { content: [ { @@ -414,11 +447,11 @@ function registerGenerateSequentialTool(server: McpServer): void { }; } - console.error( - `[DEBUG] MCP generate_sequential: Operation succeeded, formatting response...`, - ); - const formatted = await formatForMCP(result, outputMode, outputDir); - console.error( + debugLog(`[DEBUG] MCP generate_sequential: Operation succeeded, formatting response...`); + const formatted = await formatForMCP(result, outputMode, outputDir, { + outputRoot: getMcpOutputRoot(), + }); + debugLog( `[DEBUG] MCP generate_sequential: Response formatted, returning ${formatted.images.length} images`, ); return { @@ -426,7 +459,7 @@ function registerGenerateSequentialTool(server: McpServer): void { }; } catch (err) { const error = err as Error | ConfigError; - console.error( + debugLog( `[DEBUG] MCP generate_sequential: Exception caught: ${error.message}${error instanceof ConfigError ? " (ConfigError)" : ""}`, ); if (error instanceof ConfigError) { @@ -468,31 +501,33 @@ function registerEditSequentialTool(server: McpServer): void { size: sizeSchema.describe("Output image size WxH (default: 2048*2048)"), model: modelSchema.describe("Model ID (optional)"), output: outputModeSchema.describe("Output format: urls, paths, or base64"), - outputDir: outputDirSchema.describe("Directory to save images"), + outputDir: outputDirSchema.describe( + "Directory to save images, relative to MCP output root", + ), }, }, async ({ prompt, images, count, size, model, output, outputDir }) => { - console.error( + debugLog( `[DEBUG] MCP edit_sequential tool invoked: prompt length=${prompt.length}, images=${images?.length || 0}, count=${count}, size=${size}, model=${model || "default"}, output=${output}`, ); try { const validatedPrompt = ensurePrompt(prompt); const validatedImages = images?.length - ? await parseImagesList(images.join(","), false, 10) + ? await parseImagesList(images, false, 10, getMcpImageParseOptions()) : undefined; const validatedCount = parseMaxImages(count?.toString()); const validatedSize = parseSize(size); const outputMode = output as OutputMode; - console.error(`[DEBUG] MCP edit_sequential: Loading config...`); + debugLog(`[DEBUG] MCP edit_sequential: Loading config...`); const { config } = loadConfig(); - console.error(`[DEBUG] MCP edit_sequential: Config loaded, resolving model...`); + debugLog(`[DEBUG] MCP edit_sequential: Config loaded, resolving model...`); const resolvedModel = await resolveModelForRequest("edit-sequential", model, config); - console.error( - `[DEBUG] MCP edit_sequential: Model resolved: id=${resolvedModel.id}, provider=${resolvedModel.provider}, apiBaseUrl=${resolvedModel.apiBaseUrl}`, + debugLog( + `[DEBUG] MCP edit_sequential: Model resolved: id=${resolvedModel.id}, provider=${resolvedModel.provider}, apiBaseUrl=${redactModelBaseUrl(resolvedModel.apiBaseUrl)}`, ); - console.error(`[DEBUG] MCP edit_sequential: Calling editSequential operation...`); + debugLog(`[DEBUG] MCP edit_sequential: Calling editSequential operation...`); const result = await editSequential({ prompt: validatedPrompt, images: validatedImages, @@ -504,7 +539,7 @@ function registerEditSequentialTool(server: McpServer): void { }); if (!result.success) { - console.error(`[DEBUG] MCP edit_sequential: Operation failed: ${result.error}`); + debugLog(`[DEBUG] MCP edit_sequential: Operation failed: ${result.error}`); return { content: [ { @@ -516,9 +551,11 @@ function registerEditSequentialTool(server: McpServer): void { }; } - console.error(`[DEBUG] MCP edit_sequential: Operation succeeded, formatting response...`); - const formatted = await formatForMCP(result, outputMode, outputDir); - console.error( + debugLog(`[DEBUG] MCP edit_sequential: Operation succeeded, formatting response...`); + const formatted = await formatForMCP(result, outputMode, outputDir, { + outputRoot: getMcpOutputRoot(), + }); + debugLog( `[DEBUG] MCP edit_sequential: Response formatted, returning ${formatted.images.length} images`, ); return { @@ -526,7 +563,7 @@ function registerEditSequentialTool(server: McpServer): void { }; } catch (err) { const error = err as Error | ConfigError; - console.error( + debugLog( `[DEBUG] MCP edit_sequential: Exception caught: ${error.message}${error instanceof ConfigError ? " (ConfigError)" : ""}`, ); if (error instanceof ConfigError) { diff --git a/src/utils/images.ts b/src/utils/images.ts index 60b2b55..0895464 100644 --- a/src/utils/images.ts +++ b/src/utils/images.ts @@ -1,48 +1,263 @@ import { Buffer } from "node:buffer"; -import { mkdir, stat } from "node:fs/promises"; +import { lookup } from "node:dns/promises"; +import { constants as fsConstants } from "node:fs"; +import { lstat, mkdir, open, realpath, stat } from "node:fs/promises"; +import http, { type IncomingHttpHeaders, type IncomingMessage } from "node:http"; +import https from "node:https"; +import net from "node:net"; import path from "node:path"; +import { redactUrl } from "./logging.ts"; + +const DEFAULT_MAX_IMAGE_BYTES = 25 * 1024 * 1024; // 25 MiB +const DEFAULT_DOWNLOAD_TIMEOUT_MS = 60_000; +const MAX_REDIRECTS = 5; + +export interface LocalImageReadOptions { + rootDir?: string; + maxBytes?: number; +} + +export interface DownloadImageOptions { + maxBytes?: number; + timeoutMs?: number; + allowPrivateNetwork?: boolean; + requireHttps?: boolean; +} + +export interface SaveImagesOptions extends DownloadImageOptions { + outputRoot?: string; +} /** * Ensure the destination directory for saved outputs exists. */ export async function ensureOutputDir(dir: string): Promise { await mkdir(dir, { recursive: true }); + const info = await stat(dir); + if (!info.isDirectory()) { + throw new Error(`Output path is not a directory: ${dir}`); + } } -/** - * Download an image URL and write it to a local destination path. - */ -export async function downloadImageFromUrl(url: string, destPath: string): Promise { - const res = await fetch(url); - if (!res.ok) { - throw new Error(`Download failed ${res.status} ${res.statusText} for ${url}`); +function isSubpath(candidate: string, root: string): boolean { + const relative = path.relative(root, candidate); + return ( + relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative)) + ); +} + +function sanitizeFileComponent(value: string): string { + const safe = value + .replace(/[^A-Za-z0-9._-]/g, "_") + .replace(/\.\.+/g, "_") + .replace(/^\.+$/, "") + .slice(0, 120); + return safe || "task"; +} + +function resolveOutputDir(outputDir: string, outputRoot?: string): string { + if (!outputRoot) { + return path.resolve(outputDir); + } + + const root = path.resolve(outputRoot); + const requested = path.isAbsolute(outputDir) + ? path.resolve(outputDir) + : path.resolve(root, outputDir); + + if (!isSubpath(requested, root)) { + throw new Error(`Output directory must stay within configured output root: ${root}`); + } + + return requested; +} + +async function nearestExistingAncestor(candidate: string): Promise { + let current = path.resolve(candidate); + + while (true) { + try { + await stat(current); + return current; + } catch (err) { + if ((err as NodeJS.ErrnoException).code !== "ENOENT") { + throw err; + } + + const parent = path.dirname(current); + if (parent === current) { + throw err; + } + current = parent; + } } - const ab = await res.arrayBuffer(); - await Bun.write(destPath, new Uint8Array(ab)); +} + +async function validateOutputAncestorWithinRoot( + resolvedOutputDir: string, + outputRoot: string, +): Promise<{ rootRealPath: string }> { + const rootRealPath = await realpath(path.resolve(outputRoot)); + const outputAncestor = await nearestExistingAncestor(resolvedOutputDir); + const outputAncestorRealPath = await realpath(outputAncestor); + + if (!isSubpath(outputAncestorRealPath, rootRealPath)) { + throw new Error(`Output directory must stay within configured output root: ${rootRealPath}`); + } + + return { rootRealPath }; +} + +async function writeUniqueFile(destPath: string, data: Uint8Array | Buffer): Promise { + const parsed = path.parse(destPath); + + for (let attempt = 0; attempt < 100; attempt++) { + const candidate = + attempt === 0 ? destPath : path.join(parsed.dir, `${parsed.name}-${attempt}${parsed.ext}`); + + try { + const handle = await open( + candidate, + fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, + 0o600, + ); + try { + await handle.writeFile(data); + } finally { + await handle.close(); + } + return candidate; + } catch (err) { + if ((err as NodeJS.ErrnoException).code !== "EEXIST") { + throw err; + } + } + } + + throw new Error(`Unable to choose a unique output path for ${destPath}`); +} + +export function detectImageMime(bytes: Uint8Array): string | undefined { + if (bytes.length >= 8) { + const png = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]; + if (png.every((byte, index) => bytes[index] === byte)) { + return "image/png"; + } + } + + if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) { + return "image/jpeg"; + } + + if (bytes.length >= 6) { + const header = Buffer.from(bytes.subarray(0, 6)).toString("ascii"); + if (header === "GIF87a" || header === "GIF89a") { + return "image/gif"; + } + } + + if (bytes.length >= 12) { + const riff = Buffer.from(bytes.subarray(0, 4)).toString("ascii"); + const webp = Buffer.from(bytes.subarray(8, 12)).toString("ascii"); + if (riff === "RIFF" && webp === "WEBP") { + return "image/webp"; + } + } + + if (bytes.length >= 2 && bytes[0] === 0x42 && bytes[1] === 0x4d) { + return "image/bmp"; + } + + return undefined; +} + +function assertSupportedImage(bytes: Uint8Array, context: string): string { + const mime = detectImageMime(bytes); + if (!mime) { + throw new Error(`${context} is not a supported image type`); + } + return mime; +} + +function extensionForImageMime(mime: string): string { + switch (mime) { + case "image/jpeg": + return ".jpg"; + case "image/png": + return ".png"; + case "image/gif": + return ".gif"; + case "image/webp": + return ".webp"; + case "image/bmp": + return ".bmp"; + default: + return ".img"; + } +} + +function withImageExtension(destPath: string, mime: string): string { + const parsed = path.parse(destPath); + return path.join(parsed.dir, `${parsed.name}${extensionForImageMime(mime)}`); } function normalizeBase64(input: string): string { - if (input.startsWith("data:")) { - const idx = input.indexOf("base64,"); - return idx !== -1 ? input.slice(idx + "base64,".length) : input; + const match = /^data:[^,]*;base64,/i.exec(input.trim()); + return match ? input.trim().slice(match[0].length) : input; +} + +export function decodeBase64Image(input: string, maxBytes = DEFAULT_MAX_IMAGE_BYTES): Buffer { + const normalized = normalizeBase64(input).trim(); + const buf = Buffer.from(normalized, "base64"); + + if (!buf.length) { + throw new Error("Image data is empty"); + } + + if (buf.length > maxBytes) { + throw new Error(`Image exceeds maximum size of ${maxBytes} bytes`); + } + + assertSupportedImage(buf, "Image data"); + return buf; +} + +export function isDataUriImage(input: string): boolean { + return /^data:image\/[A-Za-z0-9.+-]+;base64,/i.test(input.trim()); +} + +export function isBase64Image(input: string): boolean { + try { + decodeBase64Image(input, DEFAULT_MAX_IMAGE_BYTES); + return true; + } catch { + return false; } - return input; } /** * Decode a base64 or data-URI image string and write it to disk. */ -export async function saveBase64Image(base64: string, destPath: string): Promise { - const normalized = normalizeBase64(base64); - const buf = Buffer.from(normalized, "base64"); - await Bun.write(destPath, buf); +export async function saveBase64Image( + base64: string, + destPath: string, + maxBytes = DEFAULT_MAX_IMAGE_BYTES, +): Promise { + const buf = decodeBase64Image(base64, maxBytes); + const mime = assertSupportedImage(buf, "Image data"); + return writeUniqueFile(withImageExtension(destPath, mime), buf); } /** * Return true when the provided string is an HTTP(S) URL. */ export function isUrl(s: string): boolean { - return s.startsWith("http://") || s.startsWith("https://"); + try { + const url = new URL(s); + return url.protocol === "http:" || url.protocol === "https:"; + } catch { + return false; + } } /** @@ -57,13 +272,416 @@ export async function fileExists(filePath: string): Promise { } } +function isPrivateIPv4(address: string): boolean { + const parts = address.split(".").map((part) => Number(part)); + if ( + parts.length !== 4 || + parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255) + ) { + return true; + } + + const [a, b] = parts; + return ( + a === 0 || + a === 10 || + a === 127 || + (a === 169 && b === 254) || + (a === 172 && b >= 16 && b <= 31) || + (a === 192 && b === 168) || + (a === 100 && b >= 64 && b <= 127) || + (a === 192 && b === 0) || + (a === 198 && (b === 18 || b === 19)) || + a >= 224 + ); +} + +function extractIPv4FromMappedIPv6(address: string): string | undefined { + const match = /^::ffff:(?:(?:0:){0,2})?(.+)$/i.exec(address); + if (!match) return undefined; + + const embedded = match[1]; + if (net.isIP(embedded) === 4) { + return embedded; + } + + const hextets = embedded.split(":"); + if (hextets.length !== 2) { + return undefined; + } + + const high = Number.parseInt(hextets[0], 16); + const low = Number.parseInt(hextets[1], 16); + if ( + !Number.isInteger(high) || + !Number.isInteger(low) || + high < 0 || + high > 0xffff || + low < 0 || + low > 0xffff + ) { + return undefined; + } + + return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`; +} + +function isPrivateIPv6(address: string): boolean { + const normalized = address.toLowerCase(); + const mappedIPv4 = extractIPv4FromMappedIPv6(normalized); + if (mappedIPv4) { + return isPrivateIPv4(mappedIPv4); + } + + return ( + normalized === "::" || + normalized === "::1" || + normalized.startsWith("fc") || + normalized.startsWith("fd") || + normalized.startsWith("fe8") || + normalized.startsWith("fe9") || + normalized.startsWith("fea") || + normalized.startsWith("feb") || + normalized.startsWith("2001:db8:") + ); +} + +function isPrivateAddress(address: string): boolean { + const family = net.isIP(address); + if (family === 4) { + return isPrivateIPv4(address); + } + if (family === 6) { + return isPrivateIPv6(address); + } + return false; +} + +interface PinnedAddress { + address: string; + family: 4 | 6; +} + +interface PinnedImageResponse { + statusCode: number; + statusMessage: string; + headers: IncomingHttpHeaders; + body: IncomingMessage; +} + +function normalizedHostname(url: URL): string { + return url.hostname.toLowerCase().replace(/^\[|\]$/g, ""); +} + +async function resolvePublicHttpUrl( + url: URL, + { allowPrivateNetwork = false, requireHttps = false }: DownloadImageOptions, +): Promise { + if (url.protocol !== "http:" && url.protocol !== "https:") { + throw new Error(`Unsupported URL protocol for image download: ${url.protocol}`); + } + + if (requireHttps && url.protocol !== "https:") { + throw new Error("Image download URL must use HTTPS"); + } + + const hostname = normalizedHostname(url); + if (!allowPrivateNetwork && (hostname === "localhost" || hostname.endsWith(".localhost"))) { + throw new Error("Refusing to download from localhost/private network URL"); + } + + const literalFamily = net.isIP(hostname); + if (literalFamily) { + if (!allowPrivateNetwork && isPrivateAddress(hostname)) { + throw new Error("Refusing to download from localhost/private network URL"); + } + return [{ address: hostname, family: literalFamily as 4 | 6 }]; + } + + const addresses = (await lookup(hostname, { all: true, verbatim: true })).map((entry) => ({ + address: entry.address, + family: entry.family as 4 | 6, + })); + + if (!addresses.length) { + throw new Error(`Unable to resolve image download host: ${hostname}`); + } + + if (!allowPrivateNetwork && addresses.some((entry) => isPrivateAddress(entry.address))) { + throw new Error("Refusing to download from localhost/private network URL"); + } + + return addresses; +} + +function getHeader(headers: IncomingHttpHeaders, name: string): string | undefined { + const value = headers[name.toLowerCase()]; + if (Array.isArray(value)) { + return value[0]; + } + return value; +} + +function discardResponseBody(body: IncomingMessage): void { + body.resume(); + body.destroy(); +} + +function requestPinnedUrl( + url: URL, + pinnedAddress: PinnedAddress, + options: DownloadImageOptions, +): Promise { + const timeoutMs = options.timeoutMs ?? DEFAULT_DOWNLOAD_TIMEOUT_MS; + const hostname = normalizedHostname(url); + const transport = url.protocol === "https:" ? https : http; + + return new Promise((resolve, reject) => { + const req = transport.request( + { + protocol: url.protocol, + hostname: url.hostname, + port: url.port, + path: `${url.pathname}${url.search}`, + method: "GET", + headers: { + Accept: "image/*,*/*;q=0.8", + Host: url.host, + }, + lookup: (_hostname, _options, callback) => { + callback(null, pinnedAddress.address, pinnedAddress.family); + }, + servername: url.protocol === "https:" && net.isIP(hostname) === 0 ? hostname : undefined, + timeout: timeoutMs, + }, + (res) => { + resolve({ + statusCode: res.statusCode ?? 0, + statusMessage: res.statusMessage ?? "", + headers: res.headers, + body: res, + }); + }, + ); + + req.on("timeout", () => { + req.destroy(new Error(`Image download timed out after ${timeoutMs}ms`)); + }); + req.on("error", reject); + req.end(); + }); +} + +async function requestImageWithRedirects( + inputUrl: string, + options: DownloadImageOptions, + redirectCount = 0, +): Promise { + if (redirectCount > MAX_REDIRECTS) { + throw new Error("Image download exceeded redirect limit"); + } + + const url = new URL(inputUrl); + const addresses = await resolvePublicHttpUrl(url, options); + let lastError: unknown; + + for (const address of addresses) { + try { + const res = await requestPinnedUrl(url, address, options); + + if (res.statusCode >= 300 && res.statusCode < 400) { + discardResponseBody(res.body); + const location = getHeader(res.headers, "location"); + if (!location) { + throw new Error(`Download redirect missing location for ${redactUrl(inputUrl)}`); + } + const nextUrl = new URL(location, url).toString(); + return requestImageWithRedirects(nextUrl, options, redirectCount + 1); + } + + return res; + } catch (err) { + lastError = err; + } + } + + throw lastError instanceof Error + ? lastError + : new Error(`Failed to download ${redactUrl(inputUrl)}`); +} + +function readMessageWithLimit( + res: IncomingMessage, + headers: IncomingHttpHeaders, + maxBytes: number, + timeoutMs: number, +): Promise { + const contentLength = getHeader(headers, "content-length"); + if (contentLength) { + const parsedLength = Number(contentLength); + if (Number.isFinite(parsedLength) && parsedLength > maxBytes) { + res.destroy(); + return Promise.reject(new Error(`Image exceeds maximum size of ${maxBytes} bytes`)); + } + } + + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + let total = 0; + let settled = false; + + const finish = (err?: Error) => { + if (settled) return; + settled = true; + clearTimeout(timer); + if (err) { + res.destroy(err); + reject(err); + return; + } + resolve(Buffer.concat(chunks, total)); + }; + + const timer = setTimeout(() => { + finish(new Error(`Image download timed out after ${timeoutMs}ms`)); + }, timeoutMs); + + res.on("data", (chunk: Buffer | Uint8Array) => { + const buffer = Buffer.from(chunk); + total += buffer.byteLength; + if (total > maxBytes) { + finish(new Error(`Image exceeds maximum size of ${maxBytes} bytes`)); + return; + } + chunks.push(buffer); + }); + res.on("end", () => finish()); + res.on("error", (err) => finish(err instanceof Error ? err : new Error(String(err)))); + }); +} + +/** + * Download an image URL and write it to a local destination path. + */ +export async function downloadImageFromUrl( + url: string, + destPath: string, + options: DownloadImageOptions = {}, +): Promise { + const res = await requestImageWithRedirects(url, options); + if (res.statusCode < 200 || res.statusCode >= 300) { + discardResponseBody(res.body); + throw new Error(`Download failed ${res.statusCode} ${res.statusMessage} for ${redactUrl(url)}`); + } + + const contentType = getHeader(res.headers, "content-type"); + const normalizedContentType = contentType?.toLowerCase().split(";", 1)[0].trim(); + if ( + normalizedContentType && + !normalizedContentType.startsWith("image/") && + normalizedContentType !== "application/octet-stream" + ) { + discardResponseBody(res.body); + throw new Error(`Download did not return an image content type for ${redactUrl(url)}`); + } + + const bytes = await readMessageWithLimit( + res.body, + res.headers, + options.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES, + options.timeoutMs ?? DEFAULT_DOWNLOAD_TIMEOUT_MS, + ); + const mime = assertSupportedImage(bytes, "Downloaded output"); + return writeUniqueFile(withImageExtension(destPath, mime), bytes); +} + +async function resolveValidatedLocalImagePath( + filePath: string, + options: LocalImageReadOptions = {}, +): Promise { + const rootPath = options.rootDir ? path.resolve(options.rootDir) : undefined; + const candidatePath = rootPath + ? path.isAbsolute(filePath) + ? path.resolve(filePath) + : path.resolve(rootPath, filePath) + : path.resolve(filePath); + + if (rootPath && !isSubpath(candidatePath, rootPath)) { + throw new Error(`Image file must stay within configured input root: ${rootPath}`); + } + + const info = await lstat(candidatePath); + if (!info.isFile()) { + throw new Error(`Image path is not a regular file: ${filePath}`); + } + if (info.isSymbolicLink()) { + throw new Error(`Image path must not be a symbolic link: ${filePath}`); + } + + const maxBytes = options.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES; + if (info.size > maxBytes) { + throw new Error(`Image exceeds maximum size of ${maxBytes} bytes: ${filePath}`); + } + + const resolvedPath = await realpath(candidatePath); + if (options.rootDir) { + const root = await realpath(options.rootDir); + if (!isSubpath(resolvedPath, root)) { + throw new Error(`Image file must stay within configured input root: ${root}`); + } + } + + return resolvedPath; +} + +async function readValidatedLocalImage( + filePath: string, + options: LocalImageReadOptions = {}, +): Promise { + const resolvedPath = await resolveValidatedLocalImagePath(filePath, options); + const noFollowFlag = fsConstants.O_NOFOLLOW ?? 0; + const handle = await open(resolvedPath, fsConstants.O_RDONLY | noFollowFlag); + + try { + const info = await handle.stat(); + if (!info.isFile()) { + throw new Error(`Image path is not a regular file: ${filePath}`); + } + + const maxBytes = options.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES; + if (info.size > maxBytes) { + throw new Error(`Image exceeds maximum size of ${maxBytes} bytes: ${filePath}`); + } + + const buffer = Buffer.from(await handle.readFile()); + if (buffer.byteLength > maxBytes) { + throw new Error(`Image exceeds maximum size of ${maxBytes} bytes: ${filePath}`); + } + return buffer; + } finally { + await handle.close(); + } +} + /** * Read a local file and encode it as base64 for API submission. */ -export async function convertFileToBase64(filePath: string): Promise { - const file = Bun.file(filePath); - const buffer = await file.arrayBuffer(); - return Buffer.from(buffer).toString("base64"); +export async function convertFileToBase64( + filePath: string, + options: LocalImageReadOptions = {}, +): Promise { + const buffer = await readValidatedLocalImage(filePath, options); + assertSupportedImage(buffer, `Image file '${filePath}'`); + return buffer.toString("base64"); +} + +export async function convertFileToDataUri( + filePath: string, + options: LocalImageReadOptions = {}, +): Promise { + const buffer = await readValidatedLocalImage(filePath, options); + const mime = assertSupportedImage(buffer, `Image file '${filePath}'`); + return `data:${mime};base64,${buffer.toString("base64")}`; } /** @@ -73,25 +691,51 @@ export async function saveImagesFromOutputs( outputs: string[], outputDir: string, taskId: string, -): Promise<{ savedPaths: string[]; failed: { index: number; reason: string }[] }> { - await ensureOutputDir(outputDir); + options: SaveImagesOptions = {}, +): Promise<{ + savedPaths: string[]; + savedByIndex: Array; + failed: { index: number; reason: string }[]; +}> { + const resolvedOutputDir = resolveOutputDir(outputDir, options.outputRoot); + let rootRealPath: string | undefined; + if (options.outputRoot) { + ({ rootRealPath } = await validateOutputAncestorWithinRoot( + resolvedOutputDir, + options.outputRoot, + )); + } + + await ensureOutputDir(resolvedOutputDir); + if (rootRealPath) { + const outputRealPath = await realpath(resolvedOutputDir); + if (!isSubpath(outputRealPath, rootRealPath)) { + throw new Error(`Output directory must stay within configured output root: ${rootRealPath}`); + } + } + const safeTaskId = sanitizeFileComponent(taskId); + const results = await Promise.allSettled( outputs.map((item, i) => { - const filename = `${taskId}_${i + 1}.png`; - const destPath = path.join(outputDir, filename); + const filename = `${safeTaskId}_${i + 1}.png`; + const destPath = path.join(resolvedOutputDir, filename); return ( - isUrl(item) ? downloadImageFromUrl(item, destPath) : saveBase64Image(item, destPath) - ).then(() => destPath); + isUrl(item) + ? downloadImageFromUrl(item, destPath, options) + : saveBase64Image(item, destPath, options.maxBytes) + ).then((savedPath) => savedPath); }), ); const savedPaths: string[] = []; + const savedByIndex: Array = Array.from({ length: outputs.length }); const failed: { index: number; reason: string }[] = []; results.forEach((r, i) => { if (r.status === "fulfilled") { savedPaths.push(r.value); + savedByIndex[i] = r.value; } else { failed.push({ index: i, reason: r.reason?.message ?? String(r.reason) }); } }); - return { savedPaths, failed }; + return { savedPaths, savedByIndex, failed }; } diff --git a/src/utils/logging.ts b/src/utils/logging.ts new file mode 100644 index 0000000..df57640 --- /dev/null +++ b/src/utils/logging.ts @@ -0,0 +1,42 @@ +const DEBUG_ENV_PATTERN = /^(1|true|yes|on)$/i; + +export function isDebugEnabled(): boolean { + return DEBUG_ENV_PATTERN.test(process.env.WAVESPEED_DEBUG ?? ""); +} + +export function debugLog(message: string): void { + if (isDebugEnabled()) { + console.error(message); + } +} + +export function truncateForLog(value: unknown, maxLength = 500): string { + const text = typeof value === "string" ? value : String(value ?? ""); + if (text.length <= maxLength) { + return text; + } + return `${text.slice(0, maxLength)}…`; +} + +export function redactUrl(value: string): string { + try { + const url = new URL(value); + if (url.username) { + url.username = ""; + } + if (url.password) { + url.password = ""; + } + if (url.search) { + url.search = "?redacted"; + } + return url.toString(); + } catch { + const withoutCredentials = value.replace(/(\/\/)[^/?#\s@]+@/g, "$1@"); + const queryIndex = withoutCredentials.indexOf("?"); + if (queryIndex === -1) { + return withoutCredentials; + } + return `${withoutCredentials.slice(0, queryIndex)}?redacted`; + } +} diff --git a/src/utils/polling.ts b/src/utils/polling.ts index 1fb58c4..237defe 100644 --- a/src/utils/polling.ts +++ b/src/utils/polling.ts @@ -1,6 +1,7 @@ import { getResult } from "../api/client.ts"; import type { TaskData } from "../api/types.ts"; import type { ResolvedModel } from "../config/types.ts"; +import { debugLog } from "./logging.ts"; const DEFAULT_POLL_INTERVAL_MS = 2000; const MAX_POLL_DURATION_MS = 10 * 60 * 1000; // 10 minutes @@ -17,7 +18,7 @@ export async function pollUntilDone( const start = Date.now(); let iteration = 0; - console.error(`[DEBUG] Starting polling for request ${requestId} (max ${maxDurationMs}ms)`); + debugLog(`[DEBUG] Starting polling for request ${requestId} (max ${maxDurationMs}ms)`); // eslint-disable-next-line no-constant-condition while (true) { @@ -26,24 +27,24 @@ export async function pollUntilDone( const elapsed = now - start; if (elapsed > maxDurationMs) { - console.error(`[DEBUG] Polling timed out after ${elapsed}ms for request ${requestId}`); + debugLog(`[DEBUG] Polling timed out after ${elapsed}ms for request ${requestId}`); throw new Error(`Polling timed out after ${maxDurationMs}ms for request ${requestId}`); } - console.error( + debugLog( `[DEBUG] Polling iteration ${iteration} for request ${requestId} (elapsed: ${elapsed}ms)`, ); const data = await getResult(model, requestId); if (data.status === "completed" || data.status === "failed" || data.status === "succeeded") { - console.error( + debugLog( `[DEBUG] Polling completed for request ${requestId}: status=${data.status} (${iteration} iterations, ${elapsed}ms)`, ); return data; } - console.error( + debugLog( `[DEBUG] Request ${requestId} still processing (status: ${data.status}), waiting ${intervalMs}ms...`, ); await new Promise((resolve) => setTimeout(resolve, intervalMs)); diff --git a/src/utils/validation.ts b/src/utils/validation.ts index e224779..f01c8be 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -1,4 +1,10 @@ -import { convertFileToBase64, fileExists, isUrl } from "./images.ts"; +import { convertFileToDataUri, decodeBase64Image, isDataUriImage, isUrl } from "./images.ts"; + +export interface ParseImagesListOptions { + allowLocalFiles?: boolean; + localFileRoot?: string; + maxFileBytes?: number; +} /** * Validate that a prompt-like input contains non-empty text. @@ -32,21 +38,64 @@ export function parseSize(input: unknown, defaultSize = "2048*2048"): string { return `${w}*${h}`; } +function normalizeImageItems(arg: unknown): string[] { + if (Array.isArray(arg)) { + return arg.map((item) => String(item ?? "").trim()).filter(Boolean); + } + + const s = String(arg ?? "").trim(); + if (!s) return []; + + if (/data:image\//i.test(s)) { + const dataUriPattern = /data:image\/[A-Za-z0-9.+-]+;base64,[A-Za-z0-9+/=_-]+/gi; + const items: string[] = []; + let lastIndex = 0; + + for (const match of s.matchAll(dataUriPattern)) { + const index = match.index ?? 0; + items.push( + ...s + .slice(lastIndex, index) + .split(",") + .map((x) => x.trim()) + .filter(Boolean), + ); + items.push(match[0]); + lastIndex = index + match[0].length; + } + + items.push( + ...s + .slice(lastIndex) + .split(",") + .map((x) => x.trim()) + .filter(Boolean), + ); + + if (items.some((item) => isDataUriImage(item))) { + return items; + } + } + + return s + .split(",") + .map((x) => x.trim()) + .filter(Boolean); +} + /** - * Parse a comma-separated image list into URLs or base64-encoded file payloads. + * Parse and validate a comma-separated image list into URLs, data URIs, or + * base64-encoded local image payloads. */ export async function parseImagesList( arg: unknown, required: boolean, max = 10, + options: ParseImagesListOptions = {}, ): Promise { - const s = String(arg ?? "").trim(); - if (!s && required) throw new Error("Images are required"); - if (!s) return []; - const items = s - .split(",") - .map((x) => x.trim()) - .filter(Boolean); + const items = normalizeImageItems(arg); + if (!items.length && required) throw new Error("Images are required"); + if (!items.length) return []; if (items.length > max) { throw new Error(`At most ${max} images are allowed`); } @@ -55,21 +104,43 @@ export async function parseImagesList( for (const item of items) { if (isUrl(item)) { - // Validate URL - try { - new URL(item); - processedItems.push(item); - } catch { - throw new Error(`Invalid image URL: ${item}`); - } - } else { - // Treat as file path - if (!(await fileExists(item))) { + const url = new URL(item); + processedItems.push(url.toString()); + continue; + } + + if (isDataUriImage(item)) { + decodeBase64Image(item, options.maxFileBytes); + processedItems.push(item); + continue; + } + + try { + decodeBase64Image(item, options.maxFileBytes); + processedItems.push(item); + continue; + } catch { + // Not raw image base64; fall through to optional local file handling. + } + + if (options.allowLocalFiles === false) { + throw new Error( + "Local image file paths are disabled in this context. Use image URLs or data URI/base64 image data.", + ); + } + + try { + const dataUri = await convertFileToDataUri(item, { + rootDir: options.localFileRoot, + maxBytes: options.maxFileBytes, + }); + processedItems.push(dataUri); + } catch (err) { + const message = (err as Error).message; + if (message.includes("ENOENT") || message.includes("no such file")) { throw new Error(`Image file not found: ${item}`); } - // Convert to base64 with data URI - const base64 = await convertFileToBase64(item); - processedItems.push(`data:image/jpeg;base64,${base64}`); + throw err; } } diff --git a/tests/api/client.test.ts b/tests/api/client.test.ts index 8bb447e..9c37014 100644 --- a/tests/api/client.test.ts +++ b/tests/api/client.test.ts @@ -316,11 +316,21 @@ describe("API Client", () => { path: "/api/v3/vendor/background-remover/edit", }); }); + + it("rejects unsafe model path segments", () => { + expect(() => + buildSubmitTarget({ ...testModel, modelName: "vendor/../secret" }, "generate"), + ).toThrow("Invalid model id segment"); + expect(() => + buildSubmitTarget({ ...testModel, modelName: "vendor/model?x=1" }, "generate"), + ).toThrow("Invalid model id segment"); + }); }); describe("Static endpoints", () => { it("should keep the non-submit endpoint URLs", () => { expect(endpoints.result("test-123")).toBe("/api/v3/predictions/test-123/result"); + expect(() => endpoints.result("../secret")).toThrow("Invalid prediction request id"); expect(endpoints.models).toBe("/api/v3/models"); }); }); diff --git a/tests/commands/cli.test.ts b/tests/commands/cli.test.ts index 367b574..1726a9c 100644 --- a/tests/commands/cli.test.ts +++ b/tests/commands/cli.test.ts @@ -87,7 +87,12 @@ describe("CLI Integration Tests", () => { ): Promise<{ stdout: string; stderr: string; exitCode: number }> => { const spawnedProcess = spawn([process.execPath, "run", cliEntryPath, ...args], { cwd: tempDir, - env: { ...Bun.env, WAVESPEED_API_KEY: "test-cli-key" }, + env: { + ...Bun.env, + WAVESPEED_API_KEY: "test-cli-key", + WAVESPEED_ALLOW_CUSTOM_API_BASE_URL: "1", + WAVESPEED_ALLOW_INSECURE_API_BASE_URL: "1", + }, stdout: "pipe", stderr: "pipe", }); diff --git a/tests/config/models.test.ts b/tests/config/models.test.ts index bea88e8..a39869c 100644 --- a/tests/config/models.test.ts +++ b/tests/config/models.test.ts @@ -292,6 +292,108 @@ describe("config/models.resolveModel", () => { } }); + it("blocks sending WAVESPEED_API_KEY to custom non-Wavespeed hosts by default", () => { + process.env.WAVESPEED_API_KEY = "secret"; + + const config = makeConfig({ + models: { + gateway: { + provider: "wavespeed", + apiKeyEnv: "WAVESPEED_API_KEY", + apiBaseUrl: "https://example.com", + modelName: "vendor/model", + }, + }, + defaults: { + globalModel: "gateway", + }, + }); + + expect(() => resolveModel("generate", undefined, config)).toThrow(ConfigError); + }); + + it("allows trusted custom API base URLs when explicitly opted in", () => { + process.env.WAVESPEED_API_KEY = "secret"; + process.env.WAVESPEED_ALLOW_CUSTOM_API_BASE_URL = "1"; + + const model = resolveModel( + "generate", + undefined, + makeConfig({ + models: { + gateway: { + provider: "wavespeed", + apiKeyEnv: "WAVESPEED_API_KEY", + apiBaseUrl: "https://example.com/", + modelName: "vendor/model", + }, + }, + defaults: { + globalModel: "gateway", + }, + }), + ); + + expect(model.apiBaseUrl).toBe("https://example.com"); + }); + + it("blocks insecure localhost API base URLs by default", () => { + process.env.C_KEY = "secret"; + + const config = makeConfig({ + models: { + local: { + provider: "custom", + apiKeyEnv: "C_KEY", + apiBaseUrl: "http://127.0.0.1:3000", + }, + }, + defaults: { + globalModel: "local", + }, + }); + + expect(() => resolveModel("generate", undefined, config)).toThrow(ConfigError); + }); + + it("blocks IPv4-mapped IPv6 private API base URLs by default", () => { + process.env.C_KEY = "secret"; + + const config = makeConfig({ + models: { + local: { + provider: "custom", + apiKeyEnv: "C_KEY", + apiBaseUrl: "https://[::ffff:127.0.0.1]", + }, + }, + defaults: { + globalModel: "local", + }, + }); + + expect(() => resolveModel("generate", undefined, config)).toThrow(ConfigError); + }); + + it("blocks trailing-dot localhost API base URLs by default", () => { + process.env.C_KEY = "secret"; + + const config = makeConfig({ + models: { + local: { + provider: "custom", + apiKeyEnv: "C_KEY", + apiBaseUrl: "https://foo.localhost.", + }, + }, + defaults: { + globalModel: "local", + }, + }); + + expect(() => resolveModel("generate", undefined, config)).toThrow(ConfigError); + }); + it("infers ai-remover routing for configured aliases that point at remover models", () => { process.env.WAVESPEED_API_KEY = "test-key"; diff --git a/tests/core/output-formatter.test.ts b/tests/core/output-formatter.test.ts new file mode 100644 index 0000000..7c66ff5 --- /dev/null +++ b/tests/core/output-formatter.test.ts @@ -0,0 +1,49 @@ +import { afterAll, beforeAll, describe, expect, it } from "bun:test"; +import { mkdtemp, rm } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { formatForMCP } from "../../src/core/output-formatter.ts"; +import type { OperationResult } from "../../src/core/types.ts"; + +const PNG_BASE64 = Buffer.from( + new Uint8Array([ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, + 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x01, 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xe2, 0x21, 0xbc, 0x33, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, + ]), +).toString("base64"); + +describe("Output formatter", () => { + let tempDir = ""; + + beforeAll(async () => { + tempDir = await mkdtemp(path.join(os.tmpdir(), "wavespeed-output-formatter-")); + }); + + afterAll(async () => { + if (tempDir) { + await rm(tempDir, { recursive: true, force: true }); + } + }); + + it("preserves MCP image indexes when one path save fails", async () => { + const result: OperationResult = { + success: true, + taskId: "task-with-gap", + status: "completed", + outputs: [PNG_BASE64, "not-an-image", PNG_BASE64], + }; + + const formatted = await formatForMCP(result, "paths", tempDir); + + expect(formatted.images).toHaveLength(3); + expect(formatted.images[0].index).toBe(0); + expect(formatted.images[0].path).toBeDefined(); + expect(formatted.images[1].index).toBe(1); + expect(formatted.images[1].path).toBeUndefined(); + expect(formatted.images[2].index).toBe(2); + expect(formatted.images[2].path).toBeDefined(); + }); +}); diff --git a/tests/utils/images.test.ts b/tests/utils/images.test.ts index deae3b3..a476489 100644 --- a/tests/utils/images.test.ts +++ b/tests/utils/images.test.ts @@ -1,5 +1,6 @@ import { afterAll, beforeAll, describe, expect, it } from "bun:test"; -import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises"; +import { createServer, type Server } from "node:http"; import path from "node:path"; import { convertFileToBase64, @@ -80,6 +81,21 @@ describe("Image Utils", () => { Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), ); }); + + it("should resolve relative paths under a configured input root", async () => { + const base64 = await convertFileToBase64("test.png", { rootDir: testDir }); + const decoded = Buffer.from(base64, "base64"); + + expect(decoded.subarray(0, 8)).toEqual( + Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), + ); + }); + + it("should reject root escapes before probing local image paths", async () => { + await expect(convertFileToBase64("../outside.png", { rootDir: testDir })).rejects.toThrow( + "Image file must stay within configured input root", + ); + }); }); describe("saveBase64Image", () => { @@ -111,34 +127,57 @@ describe("Image Utils", () => { const originalContent = await readFile(testImagePath); expect(savedContent).toEqual(originalContent); }); + + it("should preserve the detected image extension", async () => { + const jpegBase64 = Buffer.from([0xff, 0xd8, 0xff, 0xdb]).toString("base64"); + const savedPath = await saveBase64Image(jpegBase64, path.join(outputDir, "saved-jpeg.png")); + + expect(path.extname(savedPath)).toBe(".jpg"); + }); }); describe("saveImagesFromOutputs", () => { - // Mock fetch for URL testing - const originalFetch = globalThis.fetch; + let server: Server; + let serverUrl = ""; - beforeAll(() => { - globalThis.fetch = async (url: string | URL) => { - if (url.toString().includes("example.com")) { + beforeAll(async () => { + server = createServer(async (req, res) => { + const pathname = new URL(req.url ?? "/", "http://127.0.0.1").pathname; + if (pathname === "/valid.png" || pathname === "/image1.png" || pathname === "/image2.png") { const pngData = await readFile(testImagePath); - return new Response(pngData, { - status: 200, - headers: { "content-type": "image/png" }, - }); + res.writeHead(200, { "content-type": "image/png" }); + res.end(pngData); + return; } - return new Response("Not found", { status: 404 }); - }; + + res.writeHead(404, { "content-type": "application/json" }); + res.end(JSON.stringify({ error: "not found" })); + }); + + await new Promise((resolve) => { + server.listen(0, "127.0.0.1", () => resolve()); + }); + + const address = server.address(); + if (!address || typeof address === "string") { + throw new Error("Failed to determine test server address"); + } + serverUrl = `http://127.0.0.1:${address.port}`; }); - afterAll(() => { - globalThis.fetch = originalFetch; + afterAll(async () => { + await new Promise((resolve, reject) => { + server.close((err) => (err ? reject(err) : resolve())); + }); }); it("should save images from URLs", async () => { - const outputs = ["https://example.com/image1.png", "https://example.com/image2.png"]; + const outputs = [`${serverUrl}/image1.png`, `${serverUrl}/image2.png`]; const taskId = "test-task-123"; - const result = await saveImagesFromOutputs(outputs, outputDir, taskId); + const result = await saveImagesFromOutputs(outputs, outputDir, taskId, { + allowPrivateNetwork: true, + }); expect(result.savedPaths).toHaveLength(2); expect(result.failed).toHaveLength(0); @@ -166,18 +205,78 @@ describe("Image Utils", () => { it("should handle mixed success and failure", async () => { const base64 = await convertFileToBase64(testImagePath); - const outputs = [ - "https://example.com/valid.png", - "https://invalid-domain.test/fail.png", - base64, - ]; + const outputs = [`${serverUrl}/valid.png`, `${serverUrl}/missing.png`, base64]; const taskId = "test-mixed-789"; - const result = await saveImagesFromOutputs(outputs, outputDir, taskId); + const result = await saveImagesFromOutputs(outputs, outputDir, taskId, { + allowPrivateNetwork: true, + }); expect(result.savedPaths).toHaveLength(2); // URL + base64 success + expect(result.savedByIndex[0]).toBeDefined(); + expect(result.savedByIndex[1]).toBeUndefined(); + expect(result.savedByIndex[2]).toBeDefined(); expect(result.failed).toHaveLength(1); // Invalid URL failure expect(result.failed[0].index).toBe(1); }); + + it("should sanitize task ids before building output paths", async () => { + const base64 = await convertFileToBase64(testImagePath); + const result = await saveImagesFromOutputs([base64], outputDir, "../evil/task"); + + expect(result.savedPaths).toHaveLength(1); + expect(path.dirname(result.savedPaths[0])).toBe(path.resolve(outputDir)); + expect(path.basename(result.savedPaths[0])).not.toContain(".."); + }); + + it("should reject output directories outside a configured root", async () => { + const base64 = await convertFileToBase64(testImagePath); + const outsideDir = path.resolve(outputDir, "..", "outside"); + await expect( + saveImagesFromOutputs([base64], outsideDir, "task", { outputRoot: outputDir }), + ).rejects.toThrow("Output directory must stay within configured output root"); + }); + + it("should reject symlinked output ancestors before creating directories", async () => { + const base64 = await convertFileToBase64(testImagePath); + const outsideTarget = path.join(testDir, "outside-target"); + const symlinkPath = path.join(outputDir, "linked-outside"); + const escapedNestedDir = path.join(outsideTarget, "nested"); + + await mkdir(outsideTarget, { recursive: true }); + await rm(symlinkPath, { force: true, recursive: true }); + await symlink(outsideTarget, symlinkPath, "dir"); + + await expect( + saveImagesFromOutputs([base64], "linked-outside/nested", "task", { + outputRoot: outputDir, + }), + ).rejects.toThrow("Output directory must stay within configured output root"); + expect(await fileExists(escapedNestedDir)).toBe(false); + }); + + it("should reject private network download URLs", async () => { + const result = await saveImagesFromOutputs( + ["http://127.0.0.1/private.png"], + outputDir, + "private-url", + ); + + expect(result.savedPaths).toHaveLength(0); + expect(result.failed).toHaveLength(1); + expect(result.failed[0].reason).toContain("localhost/private network"); + }); + + it("should reject IPv4-mapped IPv6 private download URLs", async () => { + const result = await saveImagesFromOutputs( + ["http://[::ffff:127.0.0.1]/private.png"], + outputDir, + "private-ipv6-url", + ); + + expect(result.savedPaths).toHaveLength(0); + expect(result.failed).toHaveLength(1); + expect(result.failed[0].reason).toContain("localhost/private network"); + }); }); }); diff --git a/tests/utils/validation.test.ts b/tests/utils/validation.test.ts index 27198b2..70a16f9 100644 --- a/tests/utils/validation.test.ts +++ b/tests/utils/validation.test.ts @@ -11,6 +11,7 @@ import { describe("Validation Utils", () => { const testDir = path.join(import.meta.dir, "../fixtures"); const testImagePath = path.join(testDir, "test-validation.png"); + let testImageDataUri = ""; beforeAll(async () => { await mkdir(testDir, { recursive: true }); @@ -23,6 +24,7 @@ describe("Validation Utils", () => { 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, ]); await writeFile(testImagePath, pngData); + testImageDataUri = `data:image/png;base64,${Buffer.from(pngData).toString("base64")}`; }); afterAll(async () => { @@ -95,7 +97,7 @@ describe("Validation Utils", () => { it("should handle local file paths", async () => { const result = await parseImagesList(testImagePath, true, 10); expect(result).toHaveLength(1); - expect(result[0]).toStartWith("data:image/jpeg;base64,"); + expect(result[0]).toStartWith("data:image/png;base64,"); }); it("should handle mixed URLs and files", async () => { @@ -103,7 +105,7 @@ describe("Validation Utils", () => { const result = await parseImagesList(mixed, true, 10); expect(result).toHaveLength(2); expect(result[0]).toBe("https://example.com/image.jpg"); - expect(result[1]).toStartWith("data:image/jpeg;base64,"); + expect(result[1]).toStartWith("data:image/png;base64,"); }); it("should return empty array when not required and no input", async () => { @@ -121,6 +123,37 @@ describe("Validation Utils", () => { ); }); + it("should accept image data URIs", async () => { + const dataUri = await parseImagesList(testImageDataUri, true, 10); + expect(dataUri).toHaveLength(1); + expect(dataUri[0]).toStartWith("data:image/png;base64,"); + }); + + it("should parse multiple comma-separated image data URIs", async () => { + const dataUris = await parseImagesList(`${testImageDataUri},${testImageDataUri}`, true, 10); + expect(dataUris).toHaveLength(2); + expect(dataUris[0]).toStartWith("data:image/png;base64,"); + expect(dataUris[1]).toStartWith("data:image/png;base64,"); + }); + + it("should parse mixed-case image data URIs", async () => { + const mixedCaseDataUri = testImageDataUri.replace( + "data:image/png;base64,", + "DATA:IMAGE/PNG;BASE64,", + ); + const dataUris = await parseImagesList(`${mixedCaseDataUri},${mixedCaseDataUri}`, true, 10); + + expect(dataUris).toHaveLength(2); + expect(dataUris[0]).toStartWith("DATA:IMAGE/PNG;BASE64,"); + expect(dataUris[1]).toStartWith("DATA:IMAGE/PNG;BASE64,"); + }); + + it("should disable local files when requested", async () => { + await expect( + parseImagesList(testImagePath, true, 10, { allowLocalFiles: false }), + ).rejects.toThrow("Local image file paths are disabled"); + }); + it("should throw when invalid URL provided", async () => { await expect(parseImagesList("not-a-url-or-file", true, 10)).rejects.toThrow( "Image file not found",