From bedc5d06281417b082016367084f2552d231df2f Mon Sep 17 00:00:00 2001 From: nearxos Date: Sun, 24 May 2026 15:47:21 +0300 Subject: [PATCH] Update .gitignore and README.md for CODESYS MCP project. Added entries to .gitignore for VSIX files and VS Code settings. Expanded README to include architecture, components, deployment instructions, and details about the Cursor extension. --- .gitignore | 4 + .vscode/extensions.json | 5 + .vscode/settings.json.example | 9 + README.md | 91 +- deploy/cursor/mcp.json.example | 15 + deploy/windows/start-agent.bat.example | 17 + package.json | 11 + python_scripting/README.md | 19 + .../codesys-mcp-bridge-operations.md | 115 + python_scripting/deploy_winrm_agent.py | 142 + python_scripting/how-to-use-codesys-mcp.md | 156 + python_scripting/mcp_codesys_bridge/README.md | 51 + .../mcp_codesys_bridge/requirements.txt | 2 + python_scripting/mcp_codesys_bridge/server.py | 293 + python_scripting/setup-in-codesys-ide.md | 61 + python_scripting/what-you-can-do.md | 46 + .../windows_codesys_agent/README.md | 57 + .../windows_codesys_agent/agent.py | 301 + .../windows_codesys_agent/requirements.txt | 2 + .../windows_codesys_agent/scripts/_common.py | 78 + .../scripts/build_project.py | 46 + .../scripts/create_project.py | 55 + .../scripts/download_to_device.py | 59 + .../scripts/manage_device.py | 318 + .../scripts/read_device_io.py | 303 + .../scripts/read_project_inventory.py | 188 + .../scripts/stage_existing_project.py | 116 + .../scripts/sync_project.py | 314 + .../scripts/write_io_mapping.py | 222 + .../scripts/write_pou.py | 208 + vscode_codesys_companion/.eslintrc.json | 23 + vscode_codesys_companion/.vscodeignore | 6 + vscode_codesys_companion/README.md | 84 + vscode_codesys_companion/docs/smoke-test.md | 56 + .../language-configuration.json | 47 + vscode_codesys_companion/media/codesys.svg | 4 + vscode_codesys_companion/package-lock.json | 6192 +++++++++++++++++ vscode_codesys_companion/package.json | 240 + .../snippets/codesys-st.json | 46 + .../src/codesys/payloads.ts | 10 + .../src/codesys/service.ts | 243 + vscode_codesys_companion/src/commands.ts | 296 + vscode_codesys_companion/src/config.ts | 44 + vscode_codesys_companion/src/extension.ts | 51 + vscode_codesys_companion/src/logging.ts | 29 + vscode_codesys_companion/src/mcp/client.ts | 190 + vscode_codesys_companion/src/state.ts | 15 + vscode_codesys_companion/src/test/runTest.ts | 19 + .../src/test/suite/extension.test.ts | 7 + .../src/test/suite/index.ts | 22 + .../src/test/suite/payloads.test.ts | 12 + vscode_codesys_companion/src/types.ts | 27 + vscode_codesys_companion/src/views.ts | 91 + .../syntaxes/codesys-st.tmLanguage.json | 36 + vscode_codesys_companion/tsconfig.json | 29 + 55 files changed, 11120 insertions(+), 3 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json.example create mode 100644 deploy/cursor/mcp.json.example create mode 100644 deploy/windows/start-agent.bat.example create mode 100644 package.json create mode 100644 python_scripting/README.md create mode 100644 python_scripting/codesys-mcp-bridge-operations.md create mode 100644 python_scripting/deploy_winrm_agent.py create mode 100644 python_scripting/how-to-use-codesys-mcp.md create mode 100644 python_scripting/mcp_codesys_bridge/README.md create mode 100644 python_scripting/mcp_codesys_bridge/requirements.txt create mode 100644 python_scripting/mcp_codesys_bridge/server.py create mode 100644 python_scripting/setup-in-codesys-ide.md create mode 100644 python_scripting/what-you-can-do.md create mode 100644 python_scripting/windows_codesys_agent/README.md create mode 100644 python_scripting/windows_codesys_agent/agent.py create mode 100644 python_scripting/windows_codesys_agent/requirements.txt create mode 100644 python_scripting/windows_codesys_agent/scripts/_common.py create mode 100644 python_scripting/windows_codesys_agent/scripts/build_project.py create mode 100644 python_scripting/windows_codesys_agent/scripts/create_project.py create mode 100644 python_scripting/windows_codesys_agent/scripts/download_to_device.py create mode 100644 python_scripting/windows_codesys_agent/scripts/manage_device.py create mode 100644 python_scripting/windows_codesys_agent/scripts/read_device_io.py create mode 100644 python_scripting/windows_codesys_agent/scripts/read_project_inventory.py create mode 100644 python_scripting/windows_codesys_agent/scripts/stage_existing_project.py create mode 100644 python_scripting/windows_codesys_agent/scripts/sync_project.py create mode 100644 python_scripting/windows_codesys_agent/scripts/write_io_mapping.py create mode 100644 python_scripting/windows_codesys_agent/scripts/write_pou.py create mode 100644 vscode_codesys_companion/.eslintrc.json create mode 100644 vscode_codesys_companion/.vscodeignore create mode 100644 vscode_codesys_companion/README.md create mode 100644 vscode_codesys_companion/docs/smoke-test.md create mode 100644 vscode_codesys_companion/language-configuration.json create mode 100644 vscode_codesys_companion/media/codesys.svg create mode 100644 vscode_codesys_companion/package-lock.json create mode 100644 vscode_codesys_companion/package.json create mode 100644 vscode_codesys_companion/snippets/codesys-st.json create mode 100644 vscode_codesys_companion/src/codesys/payloads.ts create mode 100644 vscode_codesys_companion/src/codesys/service.ts create mode 100644 vscode_codesys_companion/src/commands.ts create mode 100644 vscode_codesys_companion/src/config.ts create mode 100644 vscode_codesys_companion/src/extension.ts create mode 100644 vscode_codesys_companion/src/logging.ts create mode 100644 vscode_codesys_companion/src/mcp/client.ts create mode 100644 vscode_codesys_companion/src/state.ts create mode 100644 vscode_codesys_companion/src/test/runTest.ts create mode 100644 vscode_codesys_companion/src/test/suite/extension.test.ts create mode 100644 vscode_codesys_companion/src/test/suite/index.ts create mode 100644 vscode_codesys_companion/src/test/suite/payloads.test.ts create mode 100644 vscode_codesys_companion/src/types.ts create mode 100644 vscode_codesys_companion/src/views.ts create mode 100644 vscode_codesys_companion/syntaxes/codesys-st.tmLanguage.json create mode 100644 vscode_codesys_companion/tsconfig.json diff --git a/.gitignore b/.gitignore index 704c51a..69d8b37 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ dist/ build/ .venv/ venv/ +*.vsix +.vscode/settings.json +vscode_codesys_companion/node_modules/ +vscode_codesys_companion/out/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..db12e6c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "nearxos.codesys-mcp-companion" + ] +} diff --git a/.vscode/settings.json.example b/.vscode/settings.json.example new file mode 100644 index 0000000..b58f212 --- /dev/null +++ b/.vscode/settings.json.example @@ -0,0 +1,9 @@ +{ + "codesys.pythonPath": "/Users/nearxos/Project/Codesys MCP/.venv/bin/python", + "codesys.bridgeScriptPath": "/Users/nearxos/Project/Codesys MCP/python_scripting/mcp_codesys_bridge/server.py", + "codesys.agentUrl": "http://10.211.55.3:8787", + "codesys.agentToken": "REPLACE_WITH_TOKEN", + "codesys.requestTimeoutSec": 60, + "codesys.defaultDryRunMutations": true, + "codesys.allowDownloadCommand": false +} diff --git a/README.md b/README.md index ba26229..58719dd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,92 @@ # Codesys MCP -MCP server for CODESYS integration. +MCP bridge for working on CODESYS projects from Cursor, backed by a Windows agent in Parallels. -## Status +## Architecture -Initial project setup. +``` +Cursor (Mac) → MCP bridge (FastMCP) → Windows agent (FastAPI :8787) → CODESYS ScriptEngine +``` + +## Components + +| Path | Role | +|------|------| +| `python_scripting/mcp_codesys_bridge/` | MCP server for Cursor | +| `python_scripting/windows_codesys_agent/` | Windows job API + ScriptEngine scripts | +| `vscode_codesys_companion/` | Cursor/VS Code extension (UI, ST syntax, commands) | +| `deploy/windows/start-agent.bat.example` | Windows agent startup template | +| `deploy/cursor/mcp.json.example` | Cursor MCP config template | + +## Deployment (Parallels) + +### Windows VM + +1. Copy `python_scripting/windows_codesys_agent/` to `C:\codesys-agent\` +2. Install Python 3.12 ARM64 and create venv: `python -m venv C:\codesys-agent\.venv` +3. `pip install -r requirements.txt` +4. Copy `deploy/windows/start-agent.bat.example` → `C:\codesys-agent\start-agent.bat` and set `CODESYS_AGENT_TOKEN` +5. Run `C:\codesys-agent\start-agent.bat` (listens on `0.0.0.0:8787`) + +Current VM: **Windows 11** at `10.211.55.3` (Parallels shared network). + +### Mac (Cursor) + +1. `python3 -m venv .venv && .venv/bin/pip install -r python_scripting/mcp_codesys_bridge/requirements.txt` +2. Add MCP server config to `~/.cursor/mcp.json` (see `deploy/cursor/mcp.json.example`) +3. Restart Cursor / reload MCP servers + +## Cursor extension (CODESYS MCP Companion) + +The companion extension adds a **CODESYS sidebar**, command palette actions, ST syntax highlighting, and job polling UI — alongside the MCP tools used by the AI in chat. + +### Install + +```bash +cd vscode_codesys_companion +npm install && npm run compile && npm run package +cursor --install-extension codesys-mcp-companion-0.1.0.vsix +``` + +Or from Cursor: **Extensions → … → Install from VSIX** and select the `.vsix` file. + +### Configure + +Copy `.vscode/settings.json.example` to `.vscode/settings.json` (or set **Settings → CODESYS MCP Companion**): + +- `codesys.agentUrl` → `http://10.211.55.3:8787` +- `codesys.agentToken` → same token as the Windows agent +- `codesys.pythonPath` → project `.venv/bin/python` +- `codesys.bridgeScriptPath` → path to `mcp_codesys_bridge/server.py` + +Reload Cursor, then run **CODESYS: Connect MCP Bridge** and **CODESYS: Health Check**. + +### Extension vs MCP server + +| Feature | Cursor MCP (`~/.cursor/mcp.json`) | Companion extension | +|---------|-----------------------------------|---------------------| +| AI chat tools | Yes | No | +| Sidebar (Projects/Jobs/Actions) | No | Yes | +| ST syntax + snippets | No | Yes | +| Command palette workflows | No | Yes | +| Sync current `.st` file | No | Yes | + +Both can run together; the extension spawns its own bridge process when you connect. + +## Smoke tests + +```bash +curl http://10.211.55.3:8787/health +``` + +Use MCP tools `codesys_health`, `create_project` (dry_run), `read_project_inventory`. + +**Note:** Project paths on Parallels shared folders (`C:\Mac\Home\...` / OneDrive) may fail when the agent runs as SYSTEM. Use native Windows paths like `C:\Temp\codesys-tests\` for reliable ScriptEngine access. + +## Docs + +See `python_scripting/how-to-use-codesys-mcp.md` and `python_scripting/codesys-mcp-bridge-operations.md`. + +## Origin + +Ported from [codesys-mcp-agent](https://git.paraskeva.net/nearxos/codesys-mcp-agent). diff --git a/deploy/cursor/mcp.json.example b/deploy/cursor/mcp.json.example new file mode 100644 index 0000000..afb72db --- /dev/null +++ b/deploy/cursor/mcp.json.example @@ -0,0 +1,15 @@ +{ + "mcpServers": { + "codesys-bridge": { + "command": "/Users/nearxos/Project/Codesys MCP/.venv/bin/python", + "args": [ + "/Users/nearxos/Project/Codesys MCP/python_scripting/mcp_codesys_bridge/server.py" + ], + "env": { + "CODESYS_AGENT_URL": "http://10.211.55.3:8787", + "CODESYS_AGENT_TOKEN": "REPLACE_WITH_TOKEN", + "CODESYS_AGENT_REQUEST_TIMEOUT_SEC": "60" + } + } + } +} diff --git a/deploy/windows/start-agent.bat.example b/deploy/windows/start-agent.bat.example new file mode 100644 index 0000000..6622c08 --- /dev/null +++ b/deploy/windows/start-agent.bat.example @@ -0,0 +1,17 @@ +@echo off +setlocal +cd /d C:\codesys-agent + +rem Copy to start-agent.bat and set your token locally (do not commit). +set CODESYS_AGENT_TOKEN=REPLACE_WITH_TOKEN +set CODESYS_AGENT_SCRIPTS_DIR=C:\codesys-agent\scripts +set CODESYS_AGENT_RUNTIME_DIR=C:\codesys-agent\runtime +set CODESYS_AGENT_PYTHON_BIN=C:\Program Files\Python312-arm64\python.exe +set CODESYS_AGENT_ALLOW_DOWNLOAD=false +set CODESYS_EXE_PATH=C:\Program Files\CODESYS 3.5.22.10\CODESYS\Common\CODESYS.exe +set CODESYS_PROFILE=CODESYS V3.5 SP22 Patch 1 +set CODESYS_NO_UI=true + +if not exist C:\codesys-agent\runtime mkdir C:\codesys-agent\runtime + +C:\codesys-agent\.venv\Scripts\uvicorn.exe agent:app --host 0.0.0.0 --port 8787 diff --git a/package.json b/package.json new file mode 100644 index 0000000..0297cf6 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "codesys-mcp-workspace", + "private": true, + "description": "CODESYS MCP workspace root", + "scripts": { + "compile:extension": "npm run compile --prefix vscode_codesys_companion", + "watch:extension": "npm run watch --prefix vscode_codesys_companion", + "package:extension": "npm run package --prefix vscode_codesys_companion", + "check:extension": "npm run check --prefix vscode_codesys_companion" + } +} diff --git a/python_scripting/README.md b/python_scripting/README.md new file mode 100644 index 0000000..bde2afd --- /dev/null +++ b/python_scripting/README.md @@ -0,0 +1,19 @@ +# Python Scripting in CODESYS + +This folder contains practical documentation for using Python scripting with the CODESYS IDE. + +## Files + +- `setup-in-codesys-ide.md` - step-by-step setup and verification in the IDE +- `what-you-can-do.md` - real use cases, limits, and automation ideas +- `mcp_codesys_bridge/` - Linux MCP server for Cursor +- `windows_codesys_agent/` - Windows VM service that executes CODESYS actions +- `codesys-mcp-bridge-operations.md` - deployment, security, and smoke-test guide +- `how-to-use-codesys-mcp.md` - day-to-day usage, workflow, and FAQ + +## Quick Start + +1. Open `setup-in-codesys-ide.md` and complete the environment setup. +2. Run the small validation script from the guide. +3. Use `what-you-can-do.md` to choose your first automation workflow. +4. Follow `codesys-mcp-bridge-operations.md` to deploy MCP + VM agent bridge. diff --git a/python_scripting/codesys-mcp-bridge-operations.md b/python_scripting/codesys-mcp-bridge-operations.md new file mode 100644 index 0000000..d541d3a --- /dev/null +++ b/python_scripting/codesys-mcp-bridge-operations.md @@ -0,0 +1,115 @@ +# CODESYS MCP Bridge Operations Guide + +This guide covers deployment, security, and smoke tests for the recommended architecture. + +## 1) Components + +- Linux: `mcp_codesys_bridge/server.py` (MCP server for Cursor) +- Windows VM: `windows_codesys_agent/agent.py` (job API) +- Windows VM scripts: `windows_codesys_agent/scripts/*.py` + +ScriptEngine actions (`create_project`, `read_project_inventory`, `build_project`, `download_to_device`) are executed by launching `CODESYS.exe --runscript --noUI` from the agent. + +## 2) Deployment Steps + +1. Copy `windows_codesys_agent` to the Windows VM. +2. Install dependencies and start the agent on port `8787`. +3. Open firewall only to trusted Linux host IP. +4. Configure Linux MCP server with `CODESYS_AGENT_URL` and `CODESYS_AGENT_TOKEN`. +5. Register MCP server in Cursor settings and restart Cursor. + +## 3) Security Checklist + +- Use a long random `CODESYS_AGENT_TOKEN`. +- Restrict inbound access by firewall rules. +- Keep `CODESYS_AGENT_ALLOW_DOWNLOAD=false` until testing completes. +- Use dry-run for all mutating calls first. +- Rotate token after initial setup validation. + +## 4) Smoke Tests + +## 4.1 Health check + +Call MCP tool: + +- `codesys_health` + +Expected: + +- `ok: true` +- correct `scripts_dir` + +## 4.2 Submit dry-run create project + +Call MCP tool: + +- `create_project` + - `project_name`: `SmokeTest` + - `output_dir`: `C:\\Temp\\codesys-tests` + - `dry_run`: `true` + +Expected: + +- returns `job_id` +- job transitions `queued -> running -> succeeded` + +## 4.3 Poll job result + +Call MCP tool: + +- `codesys_get_job` with returned `job_id` + +Expected: + +- final `status: succeeded` +- `result.dry_run: true` + +## 4.4 Run one safe write artifact action + +Call MCP tool: + +- `write_pou` with test content and `dry_run=false` + +Expected: + +- `artifact_file` created under `_mcp_generated` near project path. + +## 4.5 Read existing project + +If your existing project is scattered in an old location, stage it first: + +Call MCP tool: + +- `stage_existing_project` + - `source_path`: `C:\\Users\\nearxos\\OneDrive - individual\\Codesys_Home_Automation` + - `projects_root_dir`: `C:\\Users\\nearxos\\OneDrive - individual\\Codesys_Projects` + - `project_folder_name`: `Home_Automation` (optional) + - `move`: `false` (copy by default) + - `overwrite`: `false` + +Expected: + +- returns `job_id` +- result contains `project_folder` and resolved staged `project_path` + +Then read inventory: + +Call MCP tool: + +- `read_project_inventory` + - `project_path`: `C:\\path\\to\\project.project` (or `.projectarchive`, or a folder with one project file) + - `max_depth`: `3` + +Expected: + +- `node_count > 0` +- tree entries in `nodes[]` + +## 5) Enabling download to device + +Only after successful smoke tests: + +1. Set `CODESYS_AGENT_ALLOW_DOWNLOAD=true` on VM. +2. Restart agent service. +3. Run download tool in dry-run first, then actual run. +4. Verify target routing, credentials, and safety state before start. diff --git a/python_scripting/deploy_winrm_agent.py b/python_scripting/deploy_winrm_agent.py new file mode 100644 index 0000000..ecda39d --- /dev/null +++ b/python_scripting/deploy_winrm_agent.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import base64 +import pathlib +import sys +from typing import Iterable + +import winrm + + +def run_ps(session: winrm.Session, script: str) -> str: + result = session.run_ps(script) + stdout = result.std_out.decode("utf-8", errors="ignore").strip() + stderr = result.std_err.decode("utf-8", errors="ignore").strip() + if result.status_code != 0: + raise RuntimeError(f"PowerShell failed ({result.status_code}): {stderr or stdout}") + return stdout + + +def upload_file(session: winrm.Session, local_path: pathlib.Path, remote_path: str) -> None: + data_b64 = base64.b64encode(local_path.read_bytes()).decode("ascii") + temp_b64_path = remote_path + ".b64tmp" + # WinRM command payloads can be short; use conservative chunking. + chunk_size = 500 + + run_ps( + session, + ( + f"$p='{remote_path}';" + "$d=Split-Path -Parent $p;" + "New-Item -ItemType Directory -Force -Path $d | Out-Null;" + f"[System.IO.File]::WriteAllText('{temp_b64_path}','');" + "Write-Output 'ready'" + ), + ) + + for i in range(0, len(data_b64), chunk_size): + chunk = data_b64[i : i + chunk_size] + run_ps( + session, + f"Add-Content -Path '{temp_b64_path}' -Value '{chunk}' -NoNewline", + ) + + ps_finalize = ( + f"$b64=[System.IO.File]::ReadAllText('{temp_b64_path}');" + f"[System.IO.File]::WriteAllBytes('{remote_path}',[Convert]::FromBase64String($b64));" + f"Remove-Item -Path '{temp_b64_path}' -Force;" + f"Write-Output ('uploaded:' + '{remote_path}')" + ) + print(run_ps(session, ps_finalize)) + + +def iter_agent_files(root: pathlib.Path) -> Iterable[pathlib.Path]: + for p in sorted(root.rglob("*")): + if p.is_file(): + yield p + + +def main() -> int: + parser = argparse.ArgumentParser(description="Deploy Windows CODESYS agent via WinRM.") + parser.add_argument("--host", required=True) + parser.add_argument("--username", required=True) + parser.add_argument("--password", required=True) + parser.add_argument("--token", required=True) + parser.add_argument("--install-path", default=r"C:\codesys-agent") + parser.add_argument("--codesys-exe-path", required=True) + parser.add_argument("--codesys-profile", required=True) + args = parser.parse_args() + + session = winrm.Session( + target=f"http://{args.host}:5985/wsman", + auth=(args.username, args.password), + transport="ntlm", + server_cert_validation="ignore", + ) + + print("Connected, checking identity...") + whoami = run_ps(session, "whoami") + print(f"Remote identity: {whoami}") + + local_root = pathlib.Path(__file__).resolve().parent / "windows_codesys_agent" + if not local_root.is_dir(): + raise RuntimeError(f"Local folder not found: {local_root}") + + remote_root = args.install_path.rstrip("\\") + r"\windows_codesys_agent" + for f in iter_agent_files(local_root): + rel = f.relative_to(local_root).as_posix().replace("/", "\\") + remote_path = remote_root + "\\" + rel + upload_file(session, f, remote_path) + + scripts_dir = remote_root + r"\scripts" + runtime_dir = args.install_path.rstrip("\\") + r"\runtime" + run_script_path = remote_root + r"\run-agent.ps1" + run_script = ( + f"$env:CODESYS_AGENT_TOKEN = '{args.token}'\n" + f"$env:CODESYS_AGENT_SCRIPTS_DIR = '{scripts_dir}'\n" + f"$env:CODESYS_AGENT_RUNTIME_DIR = '{runtime_dir}'\n" + "$env:CODESYS_AGENT_ALLOW_DOWNLOAD = 'false'\n" + "$env:CODESYS_AGENT_TIMEOUT_SEC = '180'\n" + f"$env:CODESYS_EXE_PATH = '{args.codesys_exe_path}'\n" + f"$env:CODESYS_PROFILE = '{args.codesys_profile}'\n" + "$env:CODESYS_NO_UI = 'true'\n" + "$env:CODESYS_SCRIPT_TIMEOUT_SEC = '600'\n" + "Set-Location -Path $PSScriptRoot\n" + ".\\.venv\\Scripts\\python.exe -m uvicorn agent:app --host 0.0.0.0 --port 8787\n" + ) + run_b64 = base64.b64encode(run_script.encode("utf-8")).decode("ascii") + run_ps( + session, + ( + f"$p='{run_script_path}';" + f"[System.IO.File]::WriteAllBytes($p,[Convert]::FromBase64String('{run_b64}'));" + "Write-Output ('uploaded:' + $p)" + ), + ) + + setup_ps = ( + f"Set-Location -Path '{remote_root}';" + "if (Get-Command py -ErrorAction SilentlyContinue) { py -3 -m venv .venv } " + "elseif (Get-Command python -ErrorAction SilentlyContinue) { python -m venv .venv } " + "else { throw 'Python is not installed on the Windows VM.' };" + ".\\.venv\\Scripts\\python.exe -m pip install -r requirements.txt;" + "Get-Process -Name python -ErrorAction SilentlyContinue | " + "Where-Object { $_.Path -like '*windows_codesys_agent*' } | Stop-Process -Force -ErrorAction SilentlyContinue;" + "Start-Process -FilePath powershell.exe " + f"-ArgumentList '-ExecutionPolicy Bypass -File \"{run_script_path}\"' " + "-WindowStyle Hidden;" + "Write-Output 'agent-started'" + ) + print(run_ps(session, setup_ps)) + print(f"Deployment complete. Agent expected at: http://{args.host}:8787/health") + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as exc: + print(f"ERROR: {exc}", file=sys.stderr) + raise diff --git a/python_scripting/how-to-use-codesys-mcp.md b/python_scripting/how-to-use-codesys-mcp.md new file mode 100644 index 0000000..02b35d9 --- /dev/null +++ b/python_scripting/how-to-use-codesys-mcp.md @@ -0,0 +1,156 @@ +# How To Use CODESYS MCP Bridge + +This guide explains day-to-day usage of your CODESYS MCP bridge from Cursor. + +## 1) Confirm prerequisites + +- Windows VM agent is running on port `8787` +- Linux host can reach VM IP +- Same token is configured on both sides: + - VM: `CODESYS_AGENT_TOKEN` + - Cursor MCP config: `CODESYS_AGENT_TOKEN` +- **CODESYS IDE must be closed** for write operations (read operations work even with IDE open) + +Quick connectivity check from Linux: + +```bash +curl http://10.77.30.50:8787/health +``` + +## 2) Cursor MCP config (working example) + +Update `~/.cursor/mcp.json`: + +```json +{ + "mcpServers": { + "codesys-bridge": { + "command": "/home/nearxos/Projects/.venvs/winrm/bin/python", + "args": [ + "/home/nearxos/Projects/gitea/codesys-mcp-agent/python_scripting/mcp_codesys_bridge/server.py" + ], + "env": { + "CODESYS_AGENT_URL": "http://10.77.30.50:8787", + "CODESYS_AGENT_TOKEN": "", + "CODESYS_AGENT_REQUEST_TIMEOUT_SEC": "30" + } + } + } +} +``` + +Reload MCP servers or restart Cursor after editing. + +## 3) Available MCP tools + +### Reading (works even with IDE open) + +| Tool | Purpose | +|------|---------| +| `codesys_health` | Check VM agent status | +| `codesys_get_job` | Poll job status/result by ID | +| `read_project_inventory` | Read project tree + textual sources (POUs, GVLs, NVLs, structs) | +| `read_device_io` | Read device tree, I/O channels, variable mappings | + +### Writing (requires CODESYS IDE to be closed) + +| Tool | Purpose | +|------|---------| +| `write_pou` | Update single or batch textual objects (declaration + implementation) | +| `write_io_mapping` | Map IEC variables to device I/O channels | +| `manage_device` | Add, remove, update, rename devices; set device parameters | +| `sync_project` | **Batch push** -- apply textual updates, IO mappings, and device params in one save | + +### Build & Deploy + +| Tool | Purpose | +|------|---------| +| `create_project` | Create new empty CODESYS project on VM | +| `build_project` | Compile a project | +| `download_to_device` | Deploy to PLC (requires `CODESYS_AGENT_ALLOW_DOWNLOAD=true`) | + +### Low-level + +| Tool | Purpose | +|------|---------| +| `codesys_submit_job` | Submit any action directly to the job queue | +| `stage_existing_project` | Copy/move project into standardized folder | + +## 4) Typical round-trip workflow + +### Import existing project into Cursor + +``` +1. read_project_inventory → get tree + textual sources → populate codesys_tree/ +2. read_device_io → get devices, I/O channels, variable mappings → populate codesys_tree/ +``` + +### Edit locally + +Edit `.st`, `.gvl`, `.nvl`, `.type.st` files under `codesys_tree/` in Cursor. + +### Push changes back to VM project + +``` +3. sync_project (dry_run=true) → preview what will change +4. sync_project (dry_run=false) → apply all textual + IO mapping changes in one save +5. build_project → compile +6. download_to_device → deploy to PLC +``` + +For individual changes, use `write_pou` or `write_io_mapping` directly. + +## 5) Safety-first usage + +- Start with `dry_run=true` for all mutating operations. +- Keep `CODESYS_AGENT_ALLOW_DOWNLOAD=false` while validating. +- Enable download only when target routing and credentials are verified. +- Close the CODESYS IDE on the VM before any write/push operations. + +## 6) Local project layout + +Every project in `Codesys_Projects/projects//` uses this structure: + +``` +/ +├── config/project.json # VM paths, profile, target device +├── codesys_tree/ # 1:1 mirror of CODESYS IDE tree +│ └── Device/ +│ ├── Plc Logic/Application/ +│ │ ├── POUs/PLC_PRG/PLC_PRG.program.st +│ │ ├── GVL/GVL.gvl +│ │ └── 3.Function Blocks/fb_boiler/fb_boiler.fb.st +│ └── EtherCAT_Master/EK1100/ +│ ├── K1/K1.io_mapping.txt +│ └── K2/K2.io_mapping.txt +└── docs/ +``` + +`codesys_tree/` is the canonical source layout. It mirrors the hierarchical +structure inside the CODESYS IDE so every folder and file maps 1:1 to a +CODESYS tree node. + +## FAQ + +### Do we need to keep CODESYS IDE open? + +- **No.** The agent launches `CODESYS.exe` with `--noUI --runscript` for all operations. +- **Read operations** (inventory, device I/O) work even if the IDE has the project open (uses readonly mode). +- **Write operations** (write_pou, sync_project, manage_device) require the IDE to have the project **closed**, because CODESYS project files are exclusively locked. +- A CODESYS installation and valid profile are still required on the VM. + +### Can we read existing CODESYS projects? + +- Yes. Use `read_project_inventory` for sources and `read_device_io` for device/IO info. +- Sources are written into `codesys_tree/` mirroring the exact CODESYS IDE hierarchy. + +### Can we modify device I/O mappings? + +- Yes. Use `write_io_mapping` with device_path + param_id + variable. +- Or use `sync_project` with `io_mappings` for batch updates. +- Or import/export CSV via `read_device_io(export_csv_path=...)` and `write_io_mapping(import_csv_path=...)`. + +### Can we add/remove devices? + +- Yes. Use `manage_device` with operations like `add`, `remove`, `update`, `rename`, `enable`/`disable`, `set_param`. +- Device type/id/version values come from `read_device_io` output or the CODESYS device catalog. diff --git a/python_scripting/mcp_codesys_bridge/README.md b/python_scripting/mcp_codesys_bridge/README.md new file mode 100644 index 0000000..814ba44 --- /dev/null +++ b/python_scripting/mcp_codesys_bridge/README.md @@ -0,0 +1,51 @@ +# CODESYS MCP Bridge (Linux) + +This MCP server runs on Linux (where Cursor runs) and forwards tool calls to a Windows VM agent that has access to CODESYS. + +## Tools + +- `codesys_health` +- `codesys_submit_job` +- `codesys_get_job` +- `create_project` +- `read_project_inventory` +- `stage_existing_project` +- `write_pou` +- `build_project` +- `download_to_device` + +## Environment variables + +- `CODESYS_AGENT_URL` (default: `http://127.0.0.1:8787`) +- `CODESYS_AGENT_TOKEN` (required) +- `CODESYS_AGENT_REQUEST_TIMEOUT_SEC` (default: `30`) + +## Run locally + +```bash +cd mcp_codesys_bridge +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +export CODESYS_AGENT_URL="http://:8787" +export CODESYS_AGENT_TOKEN="" +python server.py +``` + +## Cursor MCP config example + +```json +{ + "mcpServers": { + "codesys-bridge": { + "command": "python", + "args": ["/absolute/path/to/mcp_codesys_bridge/server.py"], + "env": { + "CODESYS_AGENT_URL": "http://:8787", + "CODESYS_AGENT_TOKEN": "", + "CODESYS_AGENT_REQUEST_TIMEOUT_SEC": "30" + } + } + } +} +``` diff --git a/python_scripting/mcp_codesys_bridge/requirements.txt b/python_scripting/mcp_codesys_bridge/requirements.txt new file mode 100644 index 0000000..5154144 --- /dev/null +++ b/python_scripting/mcp_codesys_bridge/requirements.txt @@ -0,0 +1,2 @@ +mcp>=1.10.0 +httpx>=0.27.0 diff --git a/python_scripting/mcp_codesys_bridge/server.py b/python_scripting/mcp_codesys_bridge/server.py new file mode 100644 index 0000000..4c6790c --- /dev/null +++ b/python_scripting/mcp_codesys_bridge/server.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 +"""Cursor MCP bridge for a remote Windows CODESYS agent.""" + +from __future__ import annotations + +import json +import os +from typing import Any, Dict + +import httpx +from mcp.server.fastmcp import FastMCP + + +AGENT_URL = os.environ.get("CODESYS_AGENT_URL", "http://127.0.0.1:8787").rstrip("/") +AGENT_TOKEN = os.environ.get("CODESYS_AGENT_TOKEN", "") +REQUEST_TIMEOUT_SEC = float(os.environ.get("CODESYS_AGENT_REQUEST_TIMEOUT_SEC", "30")) + + +if not AGENT_TOKEN: + raise RuntimeError("CODESYS_AGENT_TOKEN is required.") + + +mcp = FastMCP("codesys-bridge") + + +def _headers() -> Dict[str, str]: + return {"x-api-token": AGENT_TOKEN, "content-type": "application/json"} + + +def _client() -> httpx.Client: + return httpx.Client(timeout=REQUEST_TIMEOUT_SEC) + + +def _post(path: str, payload: Dict[str, Any]) -> Dict[str, Any]: + with _client() as client: + response = client.post( + f"{AGENT_URL}{path}", + headers=_headers(), + content=json.dumps(payload), + ) + response.raise_for_status() + return response.json() + + +def _get(path: str) -> Dict[str, Any]: + with _client() as client: + response = client.get(f"{AGENT_URL}{path}", headers=_headers()) + response.raise_for_status() + return response.json() + + +@mcp.tool() +def codesys_health() -> Dict[str, Any]: + """Check Windows VM CODESYS agent health.""" + with _client() as client: + response = client.get(f"{AGENT_URL}/health") + response.raise_for_status() + return response.json() + + +@mcp.tool() +def codesys_submit_job(action: str, payload: Dict[str, Any], dry_run: bool = False) -> Dict[str, Any]: + """Submit an asynchronous CODESYS action.""" + return _post( + "/jobs", + { + "action": action, + "payload": payload, + "dry_run": dry_run, + }, + ) + + +@mcp.tool() +def codesys_get_job(job_id: str) -> Dict[str, Any]: + """Get CODESYS job status/result by ID.""" + return _get(f"/jobs/{job_id}") + + +@mcp.tool() +def create_project(project_name: str, output_dir: str, template: str = "standard", dry_run: bool = False) -> Dict[str, Any]: + """Create a new CODESYS project in the Windows VM.""" + return codesys_submit_job( + action="create_project", + payload={ + "project_name": project_name, + "output_dir": output_dir, + "template": template, + }, + dry_run=dry_run, + ) + + +@mcp.tool() +def read_project_inventory(project_path: str, max_depth: int = 3, dry_run: bool = False) -> Dict[str, Any]: + """Read an existing project tree/inventory.""" + return codesys_submit_job( + action="read_project_inventory", + payload={"project_path": project_path, "max_depth": max_depth}, + dry_run=dry_run, + ) + + +@mcp.tool() +def stage_existing_project( + source_path: str, + projects_root_dir: str, + project_folder_name: str = "", + move: bool = False, + overwrite: bool = False, + dry_run: bool = False, +) -> Dict[str, Any]: + """Copy/move an existing project into a standardized projects root folder.""" + return codesys_submit_job( + action="stage_existing_project", + payload={ + "source_path": source_path, + "projects_root_dir": projects_root_dir, + "project_folder_name": project_folder_name, + "move": move, + "overwrite": overwrite, + }, + dry_run=dry_run, + ) + + +@mcp.tool() +def write_pou( + project_path: str, + object_path: str = "", + object_name: str = "", + declaration: str = "", + implementation: str = "", + updates: list = [], + dry_run: bool = False, +) -> Dict[str, Any]: + """Write/update textual objects (POUs, FBs, GVLs, NVLs, structs, programs). + + For a single object: provide object_path (e.g. "Device/Plc Logic/Application/3.Function Blocks/fb_boiler") + or object_name (unique name), plus declaration and/or implementation text. + For batch: provide updates list of dicts with object_path/object_name + declaration/implementation. + """ + payload: Dict[str, Any] = {"project_path": project_path, "dry_run": dry_run} + if updates: + payload["updates"] = updates + else: + if object_path: + payload["object_path"] = object_path + if object_name: + payload["object_name"] = object_name + if declaration: + payload["declaration"] = declaration + if implementation: + payload["implementation"] = implementation + return codesys_submit_job(action="write_pou", payload=payload, dry_run=dry_run) + + +@mcp.tool() +def read_device_io( + project_path: str, + channels_only: bool = True, + max_depth: int = 10, + export_csv_path: str = "", + dry_run: bool = False, +) -> Dict[str, Any]: + """Read device tree with I/O channels, parameters, and variable mappings. + + Returns device identification (type/model), I/O channel details (input/output, + bit size, IEC type), and current variable mappings for each device in the project. + Set channels_only=False to include non-IO parameters too. + Optionally export all IO mappings as CSV on the VM. + """ + payload: Dict[str, Any] = { + "project_path": project_path, + "channels_only": channels_only, + "max_depth": max_depth, + } + if export_csv_path: + payload["export_csv_path"] = export_csv_path + return codesys_submit_job(action="read_device_io", payload=payload, dry_run=dry_run) + + +@mcp.tool() +def write_io_mapping( + project_path: str, + mappings: list = [], + import_csv_path: str = "", + dry_run: bool = True, +) -> Dict[str, Any]: + """Map IEC variables to device I/O channels. + + Each mapping dict needs: device_path (slash-separated, e.g. "Device/EtherCAT_Master/EK1100/K1"), + param_id or param_name (from read_device_io output), and variable (IEC variable expression). + Alternatively provide import_csv_path to bulk-import mappings from a CSV file on the VM. + dry_run=True by default for safety -- set False to actually write. + """ + payload: Dict[str, Any] = { + "project_path": project_path, + "dry_run": dry_run, + } + if mappings: + payload["mappings"] = mappings + if import_csv_path: + payload["import_csv_path"] = import_csv_path + return codesys_submit_job(action="write_io_mapping", payload=payload, dry_run=dry_run) + + +@mcp.tool() +def manage_device( + project_path: str, + operations: list = [], + dry_run: bool = True, +) -> Dict[str, Any]: + """Add, remove, update, rename, enable/disable devices, or set device parameters. + + Each operation dict needs action_type plus action-specific fields: + - add: parent_path, name, device_type, device_id, device_version [, method="add"|"plug"|"insert"] + - remove: device_path + - update: device_path, device_type, device_id, device_version + - rename: device_path, new_name + - enable/disable: device_path, enabled (bool) + - set_param: device_path, param_id or param_name, value + + Use device_type/device_id/device_version values from read_device_io output. + dry_run=True by default for safety. + """ + return codesys_submit_job( + action="manage_device", + payload={ + "project_path": project_path, + "operations": operations, + "dry_run": dry_run, + }, + dry_run=dry_run, + ) + + +@mcp.tool() +def sync_project( + project_path: str, + textual_updates: list = [], + io_mappings: list = [], + device_params: list = [], + dry_run: bool = True, +) -> Dict[str, Any]: + """Batch-push local changes into a CODESYS project in a single open/save cycle. + + textual_updates: list of {object_path, declaration?, implementation?} + io_mappings: list of {device_path, param_id, variable} + device_params: list of {device_path, param_id or param_name, value} + + This is the primary "push" tool -- reads all changes from Cursor and applies them at once. + dry_run=True by default for safety. + """ + return codesys_submit_job( + action="sync_project", + payload={ + "project_path": project_path, + "textual_updates": textual_updates, + "io_mappings": io_mappings, + "device_params": device_params, + "dry_run": dry_run, + }, + dry_run=dry_run, + ) + + +@mcp.tool() +def build_project(project_path: str, dry_run: bool = False) -> Dict[str, Any]: + """Build/compile a CODESYS project.""" + return codesys_submit_job( + action="build_project", + payload={"project_path": project_path}, + dry_run=dry_run, + ) + + +@mcp.tool() +def download_to_device(project_path: str, target_name: str, start_application: bool = False, dry_run: bool = False) -> Dict[str, Any]: + """Download project to configured target device.""" + return codesys_submit_job( + action="download_to_device", + payload={ + "project_path": project_path, + "target_name": target_name, + "start_application": start_application, + }, + dry_run=dry_run, + ) + + +if __name__ == "__main__": + mcp.run() diff --git a/python_scripting/setup-in-codesys-ide.md b/python_scripting/setup-in-codesys-ide.md new file mode 100644 index 0000000..11dc8a2 --- /dev/null +++ b/python_scripting/setup-in-codesys-ide.md @@ -0,0 +1,61 @@ +# Configure Python Scripting in CODESYS IDE + +This guide helps you enable and validate Python scripting in the CODESYS Development System. + +## 1) Prerequisites + +- CODESYS Development System installed +- A local Python installation (recommended: 64-bit) +- Permissions to install CODESYS packages/add-ons + +> Note: Python version compatibility can vary by CODESYS release. Check your CODESYS release notes if the scripting package requests a specific Python version. + +## 2) Enable scripting support in CODESYS + +1. Start CODESYS IDE. +2. Open the CODESYS package/add-on manager. +3. Verify that scripting support is installed (usually shown as `Scripting`, `Script Engine`, or similar CODESYS scripting component). +4. If missing, install the scripting package from CODESYS Store/Installer and restart CODESYS. + +## 3) Verify Python environment in IDE + +1. Open the scripting window/console in CODESYS (menu name depends on version/profile). +2. Confirm that the script console starts without errors. +3. If CODESYS asks for a Python path/interpreter, set your local Python executable. +4. Save settings and restart IDE once. + +## 4) Run a first test script + +Use this minimal script in the CODESYS script console/editor: + +```python +import sys +print("Python OK") +print(sys.version) +``` + +Expected result: no import/runtime errors and printed Python version output. + +## 5) Connect script to an existing project + +1. Open your CODESYS project. +2. From scripting console/editor, access the active project object (CODESYS scripting API). +3. Start with read-only actions first: + - list devices + - list POUs + - print project metadata +4. Then move to controlled write actions (for example, auto-creating folders/objects). + +## 6) Common setup issues + +- **Scripting menu missing**: scripting package not installed or wrong CODESYS profile. +- **Interpreter errors**: incompatible Python version or wrong executable path. +- **Permission errors**: run IDE with appropriate permissions for package/plugin install. +- **API object errors**: script written for another CODESYS version/API revision. + +## 7) Recommended workflow + +- Keep scripts in version control near your CODESYS project. +- Start with non-destructive scripts (reporting/checks) before auto-edit scripts. +- Add log output for every script action. +- Test scripts on a copy of the project before applying to production projects. diff --git a/python_scripting/what-you-can-do.md b/python_scripting/what-you-can-do.md new file mode 100644 index 0000000..9f8b3d5 --- /dev/null +++ b/python_scripting/what-you-can-do.md @@ -0,0 +1,46 @@ +# What You Can Do with Python in CODESYS + +Python in CODESYS is mainly for **engineering automation** (IDE/project automation), not for PLC runtime logic execution. + +## Typical use cases + +- **Project scaffolding** + - Create project structures, folders, and naming templates automatically. +- **Bulk edits** + - Create or modify many POUs/objects consistently. +- **Consistency checks** + - Enforce naming conventions, library versions, and structure rules. +- **Build/export automation** + - Trigger exports and produce repeatable build artifacts. +- **Documentation generation** + - Extract project metadata and generate docs/reports. +- **Pre-release validation** + - Run scripted checks before handing over a project. + +## Practical examples + +- Generate standard folder trees and placeholder POUs for new machines. +- Scan all variables and report naming/style violations. +- Export project data for review and commit audit trails. +- Produce release notes from project metadata. + +## What Python in CODESYS is NOT + +- Not a replacement for IEC 61131-3 application logic running on the PLC. +- Not typically used for hard real-time control loops on the target runtime. + +## Good practices + +- Keep scripts idempotent where possible (safe to run multiple times). +- Add a `dry-run` mode for scripts that change project data. +- Write action logs (what changed, where, and when). +- Use one script per purpose (small, focused scripts are easier to maintain). +- Test against project copies before production use. + +## Suggested first automation tasks + +1. A read-only project inventory script (devices, POUs, libraries). +2. A naming-convention checker script. +3. A release/export helper script. + +These three give immediate value while keeping risk low. diff --git a/python_scripting/windows_codesys_agent/README.md b/python_scripting/windows_codesys_agent/README.md new file mode 100644 index 0000000..7771b8a --- /dev/null +++ b/python_scripting/windows_codesys_agent/README.md @@ -0,0 +1,57 @@ +# Windows CODESYS Agent + +This service runs inside the Windows VM that has CODESYS installed. + +It receives authenticated job requests from the Linux MCP bridge and executes action scripts locally. + +## Supported actions + +- `create_project` +- `read_project_inventory` +- `stage_existing_project` +- `write_pou` +- `build_project` +- `download_to_device` + +## Endpoints + +- `GET /health` +- `POST /jobs` +- `GET /jobs/{job_id}` + +## Environment variables + +- `CODESYS_AGENT_TOKEN` (required shared secret) +- `CODESYS_AGENT_SCRIPTS_DIR` (default: `C:\\codesys-agent\\scripts`) +- `CODESYS_AGENT_RUNTIME_DIR` (default: `C:\\codesys-agent\\runtime`) +- `CODESYS_AGENT_PYTHON_BIN` (default: `python`) +- `CODESYS_AGENT_TIMEOUT_SEC` (default: `180`) +- `CODESYS_AGENT_ALLOW_DOWNLOAD` (`true` or `false`, default `false`) +- `CODESYS_EXE_PATH` (required for ScriptEngine actions, e.g. `C:\\Program Files\\CODESYS 3.5.xx\\CODESYS\\Common\\CODESYS.exe`) +- `CODESYS_PROFILE` (required for ScriptEngine actions, e.g. `CODESYS V3.5 SP21`) +- `CODESYS_NO_UI` (`true`/`false`, default `true`, runs actions without opening visible IDE UI) +- `CODESYS_SCRIPT_TIMEOUT_SEC` (default `600`) + +## Run on Windows VM + +```powershell +cd windows_codesys_agent +py -m venv .venv +.venv\Scripts\Activate.ps1 +pip install -r requirements.txt +$env:CODESYS_AGENT_TOKEN = "" +$env:CODESYS_AGENT_SCRIPTS_DIR = "C:\codesys-agent\windows_codesys_agent\scripts" +$env:CODESYS_AGENT_RUNTIME_DIR = "C:\codesys-agent\runtime" +$env:CODESYS_AGENT_ALLOW_DOWNLOAD = "false" +$env:CODESYS_EXE_PATH = "C:\Program Files\CODESYS 3.5.21.0\CODESYS\Common\CODESYS.exe" +$env:CODESYS_PROFILE = "CODESYS V3.5 SP21" +$env:CODESYS_NO_UI = "true" +uvicorn agent:app --host 0.0.0.0 --port 8787 +``` + +## Security notes + +- Keep VM firewall limited to trusted source IPs. +- Use a long random token, rotate periodically. +- Keep `CODESYS_AGENT_ALLOW_DOWNLOAD=false` until you finish dry-run validation. +- For full automation, verify `CODESYS_EXE_PATH` and `CODESYS_PROFILE` are correct for your VM installation. diff --git a/python_scripting/windows_codesys_agent/agent.py b/python_scripting/windows_codesys_agent/agent.py new file mode 100644 index 0000000..b852c90 --- /dev/null +++ b/python_scripting/windows_codesys_agent/agent.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python3 +"""Windows-side CODESYS agent with async job processing.""" + +from __future__ import annotations + +import json +import os +import queue +import subprocess +import tempfile +import threading +import time +import uuid +from dataclasses import asdict, dataclass, field +from pathlib import Path +from typing import Any, Dict, Optional + +from fastapi import FastAPI, Header, HTTPException +from pydantic import BaseModel, Field + + +API_TOKEN = os.environ.get("CODESYS_AGENT_TOKEN", "") +SCRIPTS_DIR = Path( + os.environ.get("CODESYS_AGENT_SCRIPTS_DIR", r"C:\codesys-agent\scripts") +).resolve() +RUNTIME_DIR = Path( + os.environ.get("CODESYS_AGENT_RUNTIME_DIR", r"C:\codesys-agent\runtime") +).resolve() +PYTHON_BIN = os.environ.get("CODESYS_AGENT_PYTHON_BIN", "python") +DEFAULT_TIMEOUT_SEC = int(os.environ.get("CODESYS_AGENT_TIMEOUT_SEC", "180")) +ALLOW_DOWNLOAD = os.environ.get("CODESYS_AGENT_ALLOW_DOWNLOAD", "false").lower() == "true" +CODESYS_EXE_PATH = os.environ.get("CODESYS_EXE_PATH", "") +CODESYS_PROFILE = os.environ.get("CODESYS_PROFILE", "") +CODESYS_NO_UI = os.environ.get("CODESYS_NO_UI", "true").lower() == "true" +CODESYS_SCRIPT_TIMEOUT_SEC = int(os.environ.get("CODESYS_SCRIPT_TIMEOUT_SEC", "600")) + + +@dataclass +class Job: + id: str + action: str + payload: Dict[str, Any] + dry_run: bool = False + status: str = "queued" + created_at: float = field(default_factory=time.time) + started_at: Optional[float] = None + finished_at: Optional[float] = None + result: Optional[Dict[str, Any]] = None + error: Optional[str] = None + + +class SubmitJobRequest(BaseModel): + action: str = Field(..., description="Action name") + payload: Dict[str, Any] = Field(default_factory=dict) + dry_run: bool = False + + +class JobStore: + def __init__(self) -> None: + self._jobs: Dict[str, Job] = {} + self._lock = threading.Lock() + + def create(self, action: str, payload: Dict[str, Any], dry_run: bool) -> Job: + job = Job( + id=str(uuid.uuid4()), + action=action, + payload=payload, + dry_run=dry_run, + ) + with self._lock: + self._jobs[job.id] = job + return job + + def get(self, job_id: str) -> Job: + with self._lock: + job = self._jobs.get(job_id) + if not job: + raise KeyError(job_id) + return job + + def update(self, job: Job) -> None: + with self._lock: + self._jobs[job.id] = job + + +app = FastAPI(title="CODESYS VM Agent", version="0.2.0") +store = JobStore() +job_queue: "queue.Queue[str]" = queue.Queue() + + +def _check_auth(token: Optional[str]) -> None: + if not API_TOKEN: + raise HTTPException( + status_code=500, + detail="CODESYS_AGENT_TOKEN is not configured.", + ) + if token != API_TOKEN: + raise HTTPException(status_code=401, detail="Unauthorized") + + +def _action_to_script(action: str) -> str: + mapping = { + "create_project": "create_project.py", + "read_project_inventory": "read_project_inventory.py", + "read_device_io": "read_device_io.py", + "write_pou": "write_pou.py", + "write_io_mapping": "write_io_mapping.py", + "manage_device": "manage_device.py", + "sync_project": "sync_project.py", + "stage_existing_project": "stage_existing_project.py", + "build_project": "build_project.py", + "download_to_device": "download_to_device.py", + } + script = mapping.get(action) + if not script: + raise RuntimeError(f"Unsupported action: {action}") + if action == "download_to_device" and not ALLOW_DOWNLOAD: + raise RuntimeError("download_to_device is disabled by policy.") + return script + + +def _run_script_python(script_name: str, payload: Dict[str, Any]) -> Dict[str, Any]: + script_path = (SCRIPTS_DIR / script_name).resolve() + if not script_path.is_file(): + raise RuntimeError(f"Script not found: {script_path}") + + cmd = [PYTHON_BIN, str(script_path), json.dumps(payload)] + proc = subprocess.run( + cmd, + text=True, + capture_output=True, + timeout=DEFAULT_TIMEOUT_SEC, + check=False, + ) + return { + "command": cmd, + "exit_code": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + } + + +def _run_script_codesys(script_name: str, payload: Dict[str, Any]) -> Dict[str, Any]: + if not CODESYS_EXE_PATH or not CODESYS_PROFILE: + raise RuntimeError( + "CODESYS_EXE_PATH and CODESYS_PROFILE must be configured for ScriptEngine actions." + ) + script_path = (SCRIPTS_DIR / script_name).resolve() + if not script_path.is_file(): + raise RuntimeError(f"Script not found: {script_path}") + + RUNTIME_DIR.mkdir(parents=True, exist_ok=True) + payload_fd, payload_name = tempfile.mkstemp( + prefix="codesys_payload_", + suffix=".json", + dir=str(RUNTIME_DIR), + ) + result_fd, result_name = tempfile.mkstemp( + prefix="codesys_result_", + suffix=".json", + dir=str(RUNTIME_DIR), + ) + os.close(payload_fd) + os.close(result_fd) + payload_file = Path(payload_name) + result_file = Path(result_name) + payload_file.write_text(json.dumps(payload), encoding="utf-8") + + script_args = f'--scriptargs:"{payload_file}"' + cmd = ( + f'"{CODESYS_EXE_PATH}" ' + f'--profile="{CODESYS_PROFILE}" ' + f'--runscript="{script_path}" ' + f"{script_args}" + ) + if CODESYS_NO_UI: + cmd += " --noUI" + + proc_env = os.environ.copy() + proc_env["CODESYS_SCRIPT_RESULT_PATH"] = str(result_file) + + proc = subprocess.run( + cmd, + shell=True, + env=proc_env, + text=True, + capture_output=True, + timeout=CODESYS_SCRIPT_TIMEOUT_SEC, + check=False, + ) + parsed_result: Dict[str, Any] | None = None + if result_file.exists(): + try: + parsed_result = json.loads(result_file.read_text(encoding="utf-8")) + except json.JSONDecodeError: + parsed_result = {"raw_result_text": result_file.read_text(encoding="utf-8", errors="ignore")} + + return { + "command": cmd, + "exit_code": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + "script_result": parsed_result, + "payload_file": str(payload_file), + "result_file": str(result_file), + } + + +def _execute(job: Job) -> Dict[str, Any]: + if job.dry_run: + return { + "dry_run": True, + "action": job.action, + "payload": job.payload, + "message": "Dry-run accepted. No changes made.", + } + + script_name = _action_to_script(job.action) + # Project operations use CODESYS ScriptEngine (no manual IDE opening required). + codesys_actions = { + "create_project", + "read_project_inventory", + "read_device_io", + "write_pou", + "write_io_mapping", + "manage_device", + "sync_project", + "build_project", + "download_to_device", + } + if job.action in codesys_actions: + result = _run_script_codesys(script_name, job.payload) + else: + result = _run_script_python(script_name, job.payload) + if result["exit_code"] != 0: + raise RuntimeError( + f"{job.action} failed with exit_code={result['exit_code']} stderr={result['stderr']}" + ) + if result.get("script_result"): + return result["script_result"] + return result + + +def _worker() -> None: + while True: + job_id = job_queue.get() + try: + job = store.get(job_id) + job.status = "running" + job.started_at = time.time() + store.update(job) + + result = _execute(job) + job.status = "succeeded" + job.result = result + job.finished_at = time.time() + store.update(job) + except Exception as exc: # pylint: disable=broad-except + job = store.get(job_id) + job.status = "failed" + job.error = str(exc) + job.finished_at = time.time() + store.update(job) + finally: + job_queue.task_done() + + +threading.Thread(target=_worker, daemon=True).start() + + +@app.get("/health") +def health() -> Dict[str, Any]: + return { + "ok": True, + "service": "codesys-vm-agent", + "version": "0.2.0", + "scripts_dir": str(SCRIPTS_DIR), + "runtime_dir": str(RUNTIME_DIR), + "download_enabled": ALLOW_DOWNLOAD, + "codesys_exe_configured": bool(CODESYS_EXE_PATH), + "codesys_profile_configured": bool(CODESYS_PROFILE), + "codesys_no_ui": CODESYS_NO_UI, + } + + +@app.post("/jobs") +def submit_job(req: SubmitJobRequest, x_api_token: Optional[str] = Header(None)) -> Dict[str, Any]: + _check_auth(x_api_token) + job = store.create(req.action, req.payload, req.dry_run) + job_queue.put(job.id) + return {"job_id": job.id, "status": job.status} + + +@app.get("/jobs/{job_id}") +def get_job(job_id: str, x_api_token: Optional[str] = Header(None)) -> Dict[str, Any]: + _check_auth(x_api_token) + try: + job = store.get(job_id) + except KeyError as exc: + raise HTTPException(status_code=404, detail="Job not found") from exc + return asdict(job) diff --git a/python_scripting/windows_codesys_agent/requirements.txt b/python_scripting/windows_codesys_agent/requirements.txt new file mode 100644 index 0000000..40019c8 --- /dev/null +++ b/python_scripting/windows_codesys_agent/requirements.txt @@ -0,0 +1,2 @@ +fastapi>=0.115.0 +uvicorn>=0.30.0 diff --git a/python_scripting/windows_codesys_agent/scripts/_common.py b/python_scripting/windows_codesys_agent/scripts/_common.py new file mode 100644 index 0000000..96365dc --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/_common.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +"""Shared helpers for Windows CODESYS action scripts.""" + +from __future__ import print_function + +import json +import os +import sys + +try: + text_type = unicode # type: ignore[name-defined] + binary_type = str + PY2 = True +except NameError: + text_type = str + binary_type = bytes + PY2 = False + + +def _argv(index): + if len(sys.argv) > index: + return sys.argv[index] + return None + + +def load_payload(): + """Load payload from argv[1] (json string or json file path).""" + raw = _argv(1) + if not raw: + raise ValueError("Missing payload argument.") + if os.path.exists(raw): + with open(raw, "r") as f: + return json.loads(f.read()) + try: + return json.loads(raw) + except ValueError as exc: + raise ValueError("Invalid JSON payload: {0}".format(exc)) + + +def _normalize_text(value): + if isinstance(value, dict): + normalized = {} + for key, item in value.items(): + normalized[_normalize_text(key)] = _normalize_text(item) + return normalized + if isinstance(value, list): + return [_normalize_text(item) for item in value] + if isinstance(value, tuple): + return [_normalize_text(item) for item in value] + if isinstance(value, binary_type): + if PY2: + for encoding in ("utf-8", "cp1252", "latin-1"): + try: + return value.decode(encoding).encode("ascii", "replace") + except Exception: + pass + return value.decode("latin-1", "replace").encode("ascii", "replace") + for encoding in ("utf-8", "cp1252", "latin-1"): + try: + return value.decode(encoding).encode("ascii", "replace").decode("ascii") + except Exception: + pass + return value.decode("latin-1", "replace").encode("ascii", "replace").decode("ascii") + if isinstance(value, text_type): + if PY2: + return value.encode("ascii", "replace") + return value.encode("ascii", "replace").decode("ascii") + return value + + +def emit(data): + """Print JSON to stdout and optionally write to argv[2] path.""" + text = json.dumps(_normalize_text(data), indent=2, ensure_ascii=True) + print(text) + result_path = _argv(2) or os.environ.get("CODESYS_SCRIPT_RESULT_PATH") + if result_path: + with open(result_path, "w") as f: + f.write(text) diff --git a/python_scripting/windows_codesys_agent/scripts/build_project.py b/python_scripting/windows_codesys_agent/scripts/build_project.py new file mode 100644 index 0000000..d1567bb --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/build_project.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +"""Build/compile a CODESYS project via ScriptEngine.""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def main(): + payload = load_payload() + project_path = payload.get("project_path") + save_after_build = bool(payload.get("save_after_build", True)) + if not project_path: + raise ValueError("project_path is required.") + pp = os.path.abspath(os.path.expanduser(project_path)) + if not os.path.exists(pp): + raise RuntimeError("Project path does not exist: {0}".format(pp)) + if "projects" not in globals(): + raise RuntimeError("Run through CODESYS ScriptEngine (--runscript).") + + if projects.primary: + projects.primary.close() + proj = projects.open(pp, primary=True, allow_readonly=False) + app = proj.active_application + if app is None: + raise RuntimeError("No active application found in project.") + + app.build() + if save_after_build: + proj.save() + proj.close() + + emit( + { + "ok": True, + "action": "build_project", + "project_path": pp, + "save_after_build": save_after_build, + } + ) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/create_project.py b/python_scripting/windows_codesys_agent/scripts/create_project.py new file mode 100644 index 0000000..21c558c --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/create_project.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +"""Create a new CODESYS project via ScriptEngine.""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def main(): + payload = load_payload() + project_name = payload.get("project_name") + output_dir = payload.get("output_dir") + template = payload.get("template", "standard") + overwrite = bool(payload.get("overwrite", False)) + + if not project_name or not output_dir: + raise ValueError("project_name and output_dir are required.") + + project_path = os.path.join(output_dir, "{0}.project".format(project_name)) + if os.path.exists(project_path) and not overwrite: + raise RuntimeError("Project already exists: {0}".format(project_path)) + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + if "projects" not in globals(): + raise RuntimeError( + "CODESYS ScriptEngine globals not found. " + "Run this script through CODESYS.exe --runscript." + ) + + if projects.primary: + projects.primary.close() + + if os.path.exists(project_path) and overwrite: + os.remove(project_path) + + proj = projects.create(project_path, primary=True) + proj.save() + proj.close() + + emit( + { + "ok": True, + "action": "create_project", + "project_name": project_name, + "template": template, + "project_path": project_path, + } + ) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/download_to_device.py b/python_scripting/windows_codesys_agent/scripts/download_to_device.py new file mode 100644 index 0000000..99bb09a --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/download_to_device.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +"""Login/download/start the active application using ScriptEngine online API.""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def main(): + payload = load_payload() + project_path = payload.get("project_path") + target_name = payload.get("target_name", "") + start_application = bool(payload.get("start_application", False)) + force_download = bool(payload.get("force_download", True)) + + if not project_path: + raise ValueError("project_path is required.") + pp = os.path.abspath(os.path.expanduser(project_path)) + if not os.path.exists(pp): + raise RuntimeError("Project path does not exist: {0}".format(pp)) + if "projects" not in globals() or "online" not in globals(): + raise RuntimeError("Run through CODESYS ScriptEngine (--runscript).") + + if projects.primary: + projects.primary.close() + proj = projects.open(pp, primary=True, allow_readonly=False) + app = proj.active_application + if app is None: + raise RuntimeError("No active application found in project.") + + online_app = online.create_online_application(app) + # Try online change first; CODESYS handles download when needed. + login_mode = OnlineChangeOption.Try if force_download else OnlineChangeOption.Never + online_app.login(login_mode, True) + + started = False + if start_application and online_app.application_state != ApplicationState.run: + online_app.start() + started = True + + online_app.logout() + proj.close() + + emit( + { + "ok": True, + "action": "download_to_device", + "project_path": pp, + "target_name": target_name, + "start_application": start_application, + "started": started, + } + ) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/manage_device.py b/python_scripting/windows_codesys_agent/scripts/manage_device.py new file mode 100644 index 0000000..4720748 --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/manage_device.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python +"""Add, remove, or update devices in a CODESYS project via ScriptEngine.""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def _discover_project_file(path_value): + if os.path.isfile(path_value): + lower_path = path_value.lower() + if lower_path.endswith(".project") or lower_path.endswith(".projectarchive"): + return path_value + raise RuntimeError("project_path must be .project or .projectarchive: {0}".format(path_value)) + if os.path.isdir(path_value): + candidates = [] + for entry in os.listdir(path_value): + full_path = os.path.join(path_value, entry) + if os.path.isfile(full_path): + lower_name = entry.lower() + if lower_name.endswith(".project") or lower_name.endswith(".projectarchive"): + candidates.append(full_path) + if len(candidates) == 1: + return candidates[0] + if not candidates: + raise RuntimeError("No .project/.projectarchive in: {0}".format(path_value)) + raise RuntimeError("Multiple project files: {0}".format(", ".join(candidates))) + raise RuntimeError("Path does not exist: {0}".format(path_value)) + + +def _safe_str(val): + try: + return "{0}".format(val) + except Exception: + return "?" + + +def _name_of(obj): + try: + return obj.get_name(False) + except Exception: + try: + return obj.get_name() + except Exception: + return _safe_str(obj) + + +def _children_of(obj): + try: + return list(obj.get_children(False)) + except Exception: + try: + return list(obj.get_children()) + except Exception: + return [] + + +def _find_by_path(root, path_str): + parts = [p.strip() for p in path_str.split("/") if p.strip()] + current = root + for i, part in enumerate(parts): + found = None + for child in _children_of(current): + if _name_of(child) == part: + found = child + break + if found is None: + raise RuntimeError("Not found: '{0}' at segment {1} of '{2}'".format(part, i, path_str)) + current = found + return current + + +def _get_device_id(obj): + try: + did = obj.get_device_identification() + return {"type": int(did.type), "id": _safe_str(did.id), "version": _safe_str(did.version)} + except Exception: + return None + + +def _action_add(proj, op, warnings): + parent_path = op.get("parent_path", "") + name = op.get("name", "") + dev_type = op.get("device_type") + dev_id = op.get("device_id", "") + dev_version = op.get("device_version", "") + module_id = op.get("module_id") + + if not parent_path or not name or dev_type is None or not dev_id or not dev_version: + raise ValueError("add requires: parent_path, name, device_type, device_id, device_version") + + parent = _find_by_path(proj, parent_path) + args = [name, int(dev_type), dev_id, dev_version] + if module_id is not None: + args.append(module_id) + + method = op.get("method", "add") + if method == "plug": + parent.plug(*args) + elif method == "insert": + index = int(op.get("index", -1)) + parent.insert(name, index, int(dev_type), dev_id, dev_version) + else: + parent.add(*args) + + return { + "action": "add", + "parent_path": parent_path, + "name": name, + "device_type": dev_type, + "device_id": dev_id, + "device_version": dev_version, + "status": "added", + } + + +def _action_remove(proj, op, warnings): + device_path = op.get("device_path", "") + if not device_path: + raise ValueError("remove requires: device_path") + obj = _find_by_path(proj, device_path) + name = _name_of(obj) + dev_id = _get_device_id(obj) + obj.remove() + return { + "action": "remove", + "device_path": device_path, + "name": name, + "device_id": dev_id, + "status": "removed", + } + + +def _action_update(proj, op, warnings): + device_path = op.get("device_path", "") + dev_type = op.get("device_type") + dev_id = op.get("device_id", "") + dev_version = op.get("device_version", "") + module_id = op.get("module_id") + + if not device_path or dev_type is None or not dev_id or not dev_version: + raise ValueError("update requires: device_path, device_type, device_id, device_version") + + obj = _find_by_path(proj, device_path) + old_id = _get_device_id(obj) + + args = [int(dev_type), dev_id, dev_version] + if module_id is not None: + args.append(module_id) + obj.update(*args) + + return { + "action": "update", + "device_path": device_path, + "old_device_id": old_id, + "new_device_id": {"type": dev_type, "id": dev_id, "version": dev_version}, + "status": "updated", + } + + +def _action_rename(proj, op, warnings): + device_path = op.get("device_path", "") + new_name = op.get("new_name", "") + if not device_path or not new_name: + raise ValueError("rename requires: device_path, new_name") + obj = _find_by_path(proj, device_path) + old_name = _name_of(obj) + obj.rename(new_name) + return { + "action": "rename", + "device_path": device_path, + "old_name": old_name, + "new_name": new_name, + "status": "renamed", + } + + +def _action_enable(proj, op, warnings): + device_path = op.get("device_path", "") + enabled = op.get("enabled", True) + if not device_path: + raise ValueError("enable/disable requires: device_path") + obj = _find_by_path(proj, device_path) + if enabled: + obj.enable() + else: + obj.disable() + return { + "action": "enable" if enabled else "disable", + "device_path": device_path, + "status": "enabled" if enabled else "disabled", + } + + +def _action_set_param(proj, op, warnings): + """Set a device parameter value by id or name.""" + device_path = op.get("device_path", "") + param_id = op.get("param_id") + param_name = op.get("param_name", "") + value = op.get("value") + + if not device_path: + raise ValueError("set_param requires: device_path") + if value is None: + raise ValueError("set_param requires: value") + + obj = _find_by_path(proj, device_path) + params = obj.device_parameters + target = None + + for p in list(params): + if param_id is not None and int(p.id) == int(param_id): + target = p + break + if param_name and (_safe_str(getattr(p, "visible_name", "")) == param_name or _safe_str(getattr(p, "name", "")) == param_name): + target = p + break + + if target is None: + raise RuntimeError("Parameter not found: id={0} name={1} on {2}".format(param_id, param_name, device_path)) + + old_value = _safe_str(target.value) if hasattr(target, "value") else "?" + target.value = _safe_str(value) + + return { + "action": "set_param", + "device_path": device_path, + "param_id": int(target.id), + "param_name": _safe_str(getattr(target, "visible_name", "")), + "old_value": old_value, + "new_value": _safe_str(value), + "status": "set", + } + + +ACTIONS = { + "add": _action_add, + "remove": _action_remove, + "update": _action_update, + "rename": _action_rename, + "enable": _action_enable, + "disable": _action_enable, + "set_param": _action_set_param, +} + + +def main(): + payload = load_payload() + project_path = payload.get("project_path") + if not project_path: + raise ValueError("project_path is required.") + + operations = payload.get("operations", []) + if not operations: + single_action = payload.get("action_type", "") + if single_action: + operations = [payload] + else: + raise ValueError("Provide 'operations' list or a single operation with 'action_type'.") + + dry_run = bool(payload.get("dry_run", False)) + pp = os.path.abspath(os.path.expanduser(project_path)) + pp = _discover_project_file(pp) + + if "projects" not in globals(): + raise RuntimeError("Run through CODESYS ScriptEngine (--runscript).") + + if projects.primary: + projects.primary.close() + + proj = projects.open(pp, primary=True) + results = [] + warnings = [] + + for op in operations: + action_type = op.get("action_type", "") + handler = ACTIONS.get(action_type) + if not handler: + warnings.append("unknown action_type: {0}".format(action_type)) + continue + + try: + if dry_run: + results.append({ + "action": action_type, + "dry_run": True, + "status": "would_execute", + "details": op, + }) + else: + result = handler(proj, op, warnings) + results.append(result) + except Exception as exc: + warnings.append("{0}_failed: {1}".format(action_type, exc)) + + if not dry_run: + try: + proj.save() + except Exception as exc: + warnings.append("project_save_failed: {0}".format(exc)) + + proj.close() + + emit({ + "ok": True, + "action": "manage_device", + "project_path": pp, + "dry_run": dry_run, + "results": results, + "result_count": len(results), + "warnings": warnings, + }) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/read_device_io.py b/python_scripting/windows_codesys_agent/scripts/read_device_io.py new file mode 100644 index 0000000..f4826e1 --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/read_device_io.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python +"""Read device I/O channels, parameters, and variable mappings from a CODESYS project.""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def _discover_project_file(path_value): + if os.path.isfile(path_value): + lower_path = path_value.lower() + if lower_path.endswith(".project") or lower_path.endswith(".projectarchive"): + return path_value + raise RuntimeError( + "project_path must point to a .project or .projectarchive file. Got: {0}".format(path_value) + ) + if os.path.isdir(path_value): + candidates = [] + for entry in os.listdir(path_value): + full_path = os.path.join(path_value, entry) + if os.path.isfile(full_path): + lower_name = entry.lower() + if lower_name.endswith(".project") or lower_name.endswith(".projectarchive"): + candidates.append(full_path) + if len(candidates) == 1: + return candidates[0] + if not candidates: + raise RuntimeError("No .project/.projectarchive in directory: {0}".format(path_value)) + raise RuntimeError("Multiple project files found: {0}".format(", ".join(candidates))) + raise RuntimeError("Project path does not exist: {0}".format(path_value)) + + +def _safe_str(val): + try: + return "{0}".format(val) + except Exception: + return "?" + + +def _name_of(obj): + try: + return obj.get_name(False) + except Exception: + try: + return obj.get_name() + except Exception: + return _safe_str(obj) + + +def _children_of(obj): + try: + return list(obj.get_children(False)) + except Exception: + try: + return list(obj.get_children()) + except Exception: + return [] + + +def _get_device_id(obj): + try: + did = obj.get_device_identification() + return { + "type": int(did.type), + "id": _safe_str(did.id), + "version": _safe_str(did.version), + } + except Exception: + return None + + +def _channel_type_str(ct): + mapping = {0: "none", 1: "input", 2: "output", 3: "output_readonly"} + try: + return mapping.get(int(ct), _safe_str(ct)) + except Exception: + return _safe_str(ct) + + +def _read_io_mapping(param): + """Extract IO mapping info from a device parameter.""" + try: + iom = param.io_mapping + if iom is None: + return None + result = {} + try: + result["variable"] = _safe_str(iom.variable) if iom.variable else "" + except Exception: + result["variable"] = "" + try: + result["default_variable"] = _safe_str(iom.default_variable) if iom.default_variable else "" + except Exception: + result["default_variable"] = "" + try: + result["creates_variable"] = bool(iom.mapping_creates_variable) + except Exception: + pass + try: + result["maps_to_existing"] = bool(iom.maps_to_existing_variable) + except Exception: + pass + try: + result["auto_iec_address"] = bool(iom.automatic_iec_address) + except Exception: + pass + try: + addr = iom.manual_iec_address + result["iec_address"] = _safe_str(addr) if addr else "" + except Exception: + result["iec_address"] = "" + return result + except Exception: + return None + + +def _read_params(param_set, channels_only): + """Read device parameters, optionally filtering to I/O channels only.""" + params = [] + try: + param_list = list(param_set) + except Exception: + return params + + for param in param_list: + try: + ct = param.channel_type + ct_str = _channel_type_str(ct) + if channels_only and ct_str == "none": + continue + + entry = { + "name": _safe_str(getattr(param, "visible_name", "") or getattr(param, "name", "")), + "id": int(param.id) if hasattr(param, "id") else -1, + "channel_type": ct_str, + "bit_size": int(param.bit_size) if hasattr(param, "bit_size") else 0, + } + try: + entry["iec_type"] = _safe_str(param.iec_type) if param.iec_type else "" + except Exception: + entry["iec_type"] = "" + try: + entry["type_string"] = _safe_str(param.type_string) + except Exception: + entry["type_string"] = "" + try: + entry["section"] = _safe_str(param.section) if param.section else "" + except Exception: + entry["section"] = "" + + mapping = _read_io_mapping(param) + if mapping is not None: + entry["io_mapping"] = mapping + + if getattr(param, "has_sub_elements", False): + sub_entries = [] + try: + for sub in list(param): + sub_entry = { + "name": _safe_str(getattr(sub, "visible_name", "") or getattr(sub, "identifier", "")), + "bit_size": int(sub.bit_size) if hasattr(sub, "bit_size") else 0, + } + try: + sub_entry["iec_type"] = _safe_str(sub.iec_type) if hasattr(sub, "iec_type") and sub.iec_type else "" + except Exception: + pass + sub_mapping = _read_io_mapping(sub) + if sub_mapping is not None: + sub_entry["io_mapping"] = sub_mapping + sub_entries.append(sub_entry) + except Exception: + pass + if sub_entries: + entry["sub_elements"] = sub_entries + + params.append(entry) + except Exception: + continue + return params + + +def _walk_devices(node, path_parts, results, warnings, channels_only, max_depth, depth): + if depth > max_depth: + return + + name = _name_of(node) + is_dev = getattr(node, "is_device", False) + + if is_dev: + device_info = { + "name": name, + "path": "/".join(path_parts + [name]), + } + + dev_id = _get_device_id(node) + if dev_id: + device_info["device_id"] = dev_id + + try: + params = node.device_parameters + device_info["parameters"] = _read_params(params, channels_only) + device_info["parameter_count"] = len(device_info["parameters"]) + except Exception as exc: + device_info["parameters"] = [] + device_info["parameter_count"] = 0 + warnings.append("params_failed({0}): {1}".format(name, exc)) + + connectors_info = [] + try: + conn_set = node.connectors + for conn in list(conn_set): + ci = { + "connector_id": int(conn.connector_id), + "interface": _safe_str(conn.interface), + "role": "parent" if int(conn.connector_role) == 0 else "child", + } + try: + hp = conn.host_parameters + ci["host_parameters"] = _read_params(hp, channels_only) + except Exception: + pass + connectors_info.append(ci) + except Exception as exc: + warnings.append("connectors_failed({0}): {1}".format(name, exc)) + + if connectors_info: + device_info["connectors"] = connectors_info + + results.append(device_info) + + for child in _children_of(node): + try: + _walk_devices(child, path_parts + [name], results, warnings, channels_only, max_depth, depth + 1) + except Exception as exc: + warnings.append("walk_child_failed({0}): {1}".format(name, exc)) + + +def main(): + payload = load_payload() + project_path = payload.get("project_path") + if not project_path: + raise ValueError("project_path is required.") + + channels_only = bool(payload.get("channels_only", True)) + max_depth = int(payload.get("max_depth", 10)) + export_csv_path = payload.get("export_csv_path", "") + + pp = os.path.abspath(os.path.expanduser(project_path)) + pp = _discover_project_file(pp) + + if "projects" not in globals(): + raise RuntimeError("Run through CODESYS ScriptEngine (--runscript).") + + if projects.primary: + projects.primary.close() + + if pp.lower().endswith(".projectarchive"): + project_dir = os.path.dirname(pp) + proj = projects.open_archive(pp, project_dir, overwrite=False) + else: + proj = projects.open(pp, primary=True, allow_readonly=True) + + devices = [] + warnings = [] + + try: + top_nodes = _children_of(proj) + except Exception as exc: + top_nodes = [] + warnings.append("top_level_enumeration_failed: {0}".format(exc)) + + for top in top_nodes: + try: + _walk_devices(top, [], devices, warnings, channels_only, max_depth, 0) + except Exception as exc: + warnings.append("walk_failed: {0}".format(exc)) + + if export_csv_path: + try: + for top in top_nodes: + if getattr(top, "is_device", False): + top.export_io_mappings_as_csv(export_csv_path) + break + except Exception as exc: + warnings.append("csv_export_failed: {0}".format(exc)) + + proj.close() + + emit({ + "ok": True, + "action": "read_device_io", + "project_path": pp, + "devices": devices, + "device_count": len(devices), + "channels_only": channels_only, + "warnings": warnings, + }) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/read_project_inventory.py b/python_scripting/windows_codesys_agent/scripts/read_project_inventory.py new file mode 100644 index 0000000..4fcf2b0 --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/read_project_inventory.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +"""Read existing CODESYS project inventory via ScriptEngine.""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def _discover_project_file(path_value): + if os.path.isfile(path_value): + lower_path = path_value.lower() + if lower_path.endswith(".project") or lower_path.endswith(".projectarchive"): + return path_value + raise RuntimeError( + "project_path must point to a .project or .projectarchive file. Got: {0}".format(path_value) + ) + + if os.path.isdir(path_value): + candidates = [] + for entry in os.listdir(path_value): + full_path = os.path.join(path_value, entry) + if os.path.isfile(full_path): + lower_name = entry.lower() + if lower_name.endswith(".project") or lower_name.endswith(".projectarchive"): + candidates.append(full_path) + if len(candidates) == 1: + return candidates[0] + if not candidates: + raise RuntimeError( + "No .project or .projectarchive file found in directory: {0}".format(path_value) + ) + raise RuntimeError( + "Multiple project files found in directory. Specify project_path explicitly: {0}".format( + ", ".join(candidates) + ) + ) + + raise RuntimeError("Project path does not exist: {0}".format(path_value)) + + +def _name_of(obj): + try: + return obj.get_name(False) + except Exception: + try: + return obj.get_name() + except Exception: + return "{0}".format(obj) + + +def _children_of(obj): + try: + return list(obj.get_children(False)) + except Exception: + try: + return list(obj.get_children()) + except Exception: + return [] + + +def _walk(node, depth, max_depth, rows): + if depth > max_depth: + return + kind = "node" + for attr in ("is_device", "is_application", "is_folder"): + if getattr(node, attr, False): + kind = attr.replace("is_", "") + break + rows.append({"name": _name_of(node), "kind": kind, "depth": depth}) + for child in _children_of(node): + _walk(child, depth + 1, max_depth, rows) + + +def _text_from_doc(doc): + try: + return doc.text + except Exception: + try: + return doc.get_text(0, doc.length) + except Exception: + return None + + +def _collect_textual(node, path_names, collected): + name = _name_of(node) + decl = None + impl = None + try: + if getattr(node, "has_textual_declaration", False): + decl = _text_from_doc(node.textual_declaration) + except Exception: + pass + try: + if getattr(node, "has_textual_implementation", False): + impl = _text_from_doc(node.textual_implementation) + except Exception: + pass + if decl is not None or impl is not None: + collected.append( + { + "name": name, + "path": " / ".join(path_names + [name]), + "has_declaration": decl is not None, + "has_implementation": impl is not None, + "declaration": decl or "", + "implementation": impl or "", + } + ) + for child in _children_of(node): + _collect_textual(child, path_names + [name], collected) + + +def main(): + payload = load_payload() + project_path = payload.get("project_path") + max_depth = int(payload.get("max_depth", 3)) + include_textual_sources = bool(payload.get("include_textual_sources", True)) + if not project_path: + raise ValueError("project_path is required.") + pp = os.path.abspath(os.path.expanduser(project_path)) + pp = _discover_project_file(pp) + if "projects" not in globals(): + raise RuntimeError("Run through CODESYS ScriptEngine (--runscript).") + + if projects.primary: + projects.primary.close() + + if pp.lower().endswith(".projectarchive"): + project_dir = os.path.dirname(pp) + proj = projects.open_archive(pp, project_dir, overwrite=False) + else: + proj = projects.open(pp, primary=True, allow_readonly=True) + + rows = [] + warnings = [] + + try: + top_nodes = _children_of(proj) + except Exception as exc: + top_nodes = [] + warnings.append("top_level_enumeration_failed: {0}".format(exc)) + + for top in top_nodes: + try: + _walk(top, depth=0, max_depth=max_depth, rows=rows) + except Exception as exc: + warnings.append("walk_failed_for_node: {0}".format(exc)) + + textual_sources = [] + if include_textual_sources: + for top in top_nodes: + try: + _collect_textual(top, [], textual_sources) + except Exception as exc: + warnings.append("textual_collect_failed: {0}".format(exc)) + + active_app_name = None + try: + active_app = getattr(proj, "active_application", None) + except Exception as exc: + active_app = None + warnings.append("active_application_unavailable: {0}".format(exc)) + if active_app is not None: + try: + active_app_name = _name_of(active_app) + except Exception as exc: + warnings.append("active_application_name_failed: {0}".format(exc)) + + proj.close() + emit( + { + "ok": True, + "action": "read_project_inventory", + "project_path": pp, + "active_application": active_app_name, + "nodes": rows, + "node_count": len(rows), + "textual_sources": textual_sources, + "textual_source_count": len(textual_sources), + "warnings": warnings, + } + ) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/stage_existing_project.py b/python_scripting/windows_codesys_agent/scripts/stage_existing_project.py new file mode 100644 index 0000000..94e76b8 --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/stage_existing_project.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +"""Stage an existing CODESYS project into a standardized projects root folder.""" + +from __future__ import print_function + +import os +import re +import shutil + +from _common import emit, load_payload + + +def _is_project_file(name): + lower_name = name.lower() + return lower_name.endswith(".project") or lower_name.endswith(".projectarchive") + + +def _sanitize_folder_name(name): + cleaned = re.sub(r'[<>:"/\\|?*]+', "_", name or "") + cleaned = cleaned.strip().strip(".") + return cleaned or "codesys_project" + + +def _discover_project_file(source_path): + if os.path.isfile(source_path): + if _is_project_file(source_path): + return source_path + raise RuntimeError( + "source_path file must be .project or .projectarchive. Got: {0}".format(source_path) + ) + + if not os.path.isdir(source_path): + raise RuntimeError("source_path does not exist: {0}".format(source_path)) + + candidates = [] + for root, _, files in os.walk(source_path): + for file_name in files: + if _is_project_file(file_name): + candidates.append(os.path.join(root, file_name)) + + if len(candidates) == 1: + return candidates[0] + if not candidates: + raise RuntimeError("No .project or .projectarchive found under: {0}".format(source_path)) + raise RuntimeError( + "Multiple project files found under source_path. Use source_path as a specific file: {0}".format( + ", ".join(candidates) + ) + ) + + +def _copy_or_move(source_dir, target_dir, move_mode): + if move_mode: + shutil.move(source_dir, target_dir) + else: + shutil.copytree(source_dir, target_dir) + + +def main(): + payload = load_payload() + source_path = payload.get("source_path") + projects_root_dir = payload.get("projects_root_dir") + project_folder_name = payload.get("project_folder_name") + move_mode = bool(payload.get("move", False)) + overwrite = bool(payload.get("overwrite", False)) + + if not source_path or not projects_root_dir: + raise ValueError("source_path and projects_root_dir are required.") + + source_abs = os.path.abspath(os.path.expanduser(source_path)) + root_abs = os.path.abspath(os.path.expanduser(projects_root_dir)) + if not os.path.exists(source_abs): + raise RuntimeError("source_path does not exist: {0}".format(source_abs)) + + project_file_abs = _discover_project_file(source_abs) + source_dir_abs = os.path.dirname(project_file_abs) + inferred_folder_name = os.path.basename(source_dir_abs) + folder_name = _sanitize_folder_name(project_folder_name or inferred_folder_name) + + if not os.path.exists(root_abs): + os.makedirs(root_abs) + + target_dir_abs = os.path.join(root_abs, folder_name) + if os.path.exists(target_dir_abs): + if not overwrite: + raise RuntimeError("Target project folder already exists: {0}".format(target_dir_abs)) + if os.path.isdir(target_dir_abs): + shutil.rmtree(target_dir_abs) + else: + os.remove(target_dir_abs) + + _copy_or_move(source_dir_abs, target_dir_abs, move_mode) + + relative_project_path = os.path.relpath(project_file_abs, source_dir_abs) + staged_project_path = os.path.join(target_dir_abs, relative_project_path) + if not os.path.exists(staged_project_path): + raise RuntimeError( + "Staged project file not found after transfer: {0}".format(staged_project_path) + ) + + emit( + { + "ok": True, + "action": "stage_existing_project", + "source_path": source_abs, + "projects_root_dir": root_abs, + "project_folder": target_dir_abs, + "project_path": staged_project_path, + "moved": move_mode, + "overwritten": overwrite, + } + ) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/sync_project.py b/python_scripting/windows_codesys_agent/scripts/sync_project.py new file mode 100644 index 0000000..d15f62c --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/sync_project.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python +"""Batch-push local source changes into a CODESYS project. + +Receives a list of textual updates and IO mapping changes in a single payload, +applies them all in one project open/save cycle. +""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def _discover_project_file(path_value): + if os.path.isfile(path_value): + lower_path = path_value.lower() + if lower_path.endswith(".project") or lower_path.endswith(".projectarchive"): + return path_value + raise RuntimeError("project_path must be .project or .projectarchive: {0}".format(path_value)) + if os.path.isdir(path_value): + candidates = [] + for entry in os.listdir(path_value): + full_path = os.path.join(path_value, entry) + if os.path.isfile(full_path): + lower_name = entry.lower() + if lower_name.endswith(".project") or lower_name.endswith(".projectarchive"): + candidates.append(full_path) + if len(candidates) == 1: + return candidates[0] + if not candidates: + raise RuntimeError("No .project/.projectarchive in: {0}".format(path_value)) + raise RuntimeError("Multiple project files: {0}".format(", ".join(candidates))) + raise RuntimeError("Path does not exist: {0}".format(path_value)) + + +def _safe_str(val): + try: + return "{0}".format(val) + except Exception: + return "?" + + +def _name_of(obj): + try: + return obj.get_name(False) + except Exception: + try: + return obj.get_name() + except Exception: + return _safe_str(obj) + + +def _children_of(obj): + try: + return list(obj.get_children(False)) + except Exception: + try: + return list(obj.get_children()) + except Exception: + return [] + + +def _find_by_path(root, path_str): + parts = [p.strip() for p in path_str.split("/") if p.strip()] + current = root + for i, part in enumerate(parts): + found = None + for child in _children_of(current): + if _name_of(child) == part: + found = child + break + if found is None: + return None + current = found + return current + + +def _set_text(doc, new_text): + try: + doc.replace(new_text=new_text) + except TypeError: + length = doc.length + if length > 0: + doc.remove(offset=0, length=length) + if new_text: + doc.append(new_text) + + +def _apply_textual_updates(proj, updates, results, warnings): + for upd in updates: + obj_path = upd.get("object_path", "") + obj_name = upd.get("object_name", "") + decl = upd.get("declaration") + impl = upd.get("implementation") + + target_label = obj_path or obj_name + if not target_label: + warnings.append("skipped update: no object_path or object_name") + continue + + try: + obj = None + if obj_path: + obj = _find_by_path(proj, obj_path) + if obj is None and obj_name: + found = proj.find(obj_name, True) + if found: + obj = found[0] + + if obj is None: + warnings.append("not_found: {0}".format(target_label)) + continue + + name = _name_of(obj) + entry = {"name": name, "path": target_label, "declaration_updated": False, "implementation_updated": False} + + if decl is not None and getattr(obj, "has_textual_declaration", False): + _set_text(obj.textual_declaration, decl) + entry["declaration_updated"] = True + + if impl is not None and getattr(obj, "has_textual_implementation", False): + _set_text(obj.textual_implementation, impl) + entry["implementation_updated"] = True + + results.append(entry) + except Exception as exc: + warnings.append("textual_update_failed({0}): {1}".format(target_label, exc)) + + +def _apply_io_mappings(proj, mappings, results, warnings): + for m in mappings: + device_path = m.get("device_path", "") + param_id = m.get("param_id") + param_name = m.get("param_name") + variable = m.get("variable", "") + + if not device_path or not variable: + warnings.append("io_mapping skipped: missing device_path or variable") + continue + + try: + device = _find_by_path(proj, device_path) + if device is None: + warnings.append("device not found: {0}".format(device_path)) + continue + + target_param = None + try: + params = device.device_parameters + for p in list(params): + if param_id is not None and int(p.id) == int(param_id): + target_param = p + break + if param_name and _safe_str(getattr(p, "visible_name", "")) == param_name: + target_param = p + break + except Exception: + pass + + if target_param is None: + for conn in list(device.connectors): + try: + hp = conn.host_parameters + for p in list(hp): + if param_id is not None and int(p.id) == int(param_id): + target_param = p + break + if param_name and _safe_str(getattr(p, "visible_name", "")) == param_name: + target_param = p + break + except Exception: + pass + if target_param: + break + + if target_param is None: + warnings.append("param not found: id={0} name={1} on {2}".format(param_id, param_name, device_path)) + continue + + iom = target_param.io_mapping + if iom is None: + warnings.append("no io_mapping for param on {0}".format(device_path)) + continue + + old_var = _safe_str(iom.variable) if iom.variable else "" + iom.variable = variable + results.append({ + "type": "io_mapping", + "device_path": device_path, + "param_id": int(target_param.id), + "old_variable": old_var, + "new_variable": variable, + "status": "mapped", + }) + except Exception as exc: + warnings.append("io_mapping_failed({0}): {1}".format(device_path, exc)) + + +def _apply_device_params(proj, params, results, warnings): + for dp in params: + device_path = dp.get("device_path", "") + param_id = dp.get("param_id") + param_name = dp.get("param_name", "") + value = dp.get("value") + + if not device_path or value is None: + warnings.append("device_param skipped: missing device_path or value") + continue + + try: + device = _find_by_path(proj, device_path) + if device is None: + warnings.append("device not found: {0}".format(device_path)) + continue + + target = None + for p in list(device.device_parameters): + if param_id is not None and int(p.id) == int(param_id): + target = p + break + if param_name and _safe_str(getattr(p, "visible_name", "")) == param_name: + target = p + break + + if target is None: + warnings.append("param not found on {0}".format(device_path)) + continue + + old_val = _safe_str(target.value) if hasattr(target, "value") else "?" + target.value = _safe_str(value) + results.append({ + "type": "device_param", + "device_path": device_path, + "param_id": int(target.id), + "old_value": old_val, + "new_value": _safe_str(value), + "status": "set", + }) + except Exception as exc: + warnings.append("device_param_failed({0}): {1}".format(device_path, exc)) + + +def main(): + payload = load_payload() + project_path = payload.get("project_path") + if not project_path: + raise ValueError("project_path is required.") + + dry_run = bool(payload.get("dry_run", False)) + + textual_updates = payload.get("textual_updates", []) + io_mappings = payload.get("io_mappings", []) + device_params = payload.get("device_params", []) + + if not textual_updates and not io_mappings and not device_params: + raise ValueError("Nothing to sync. Provide textual_updates, io_mappings, or device_params.") + + pp = os.path.abspath(os.path.expanduser(project_path)) + pp = _discover_project_file(pp) + + if "projects" not in globals(): + raise RuntimeError("Run through CODESYS ScriptEngine (--runscript).") + + if projects.primary: + projects.primary.close() + + proj = projects.open(pp, primary=True) + results = [] + warnings = [] + + if dry_run: + emit({ + "ok": True, + "action": "sync_project", + "project_path": pp, + "dry_run": True, + "textual_update_count": len(textual_updates), + "io_mapping_count": len(io_mappings), + "device_param_count": len(device_params), + "message": "Dry-run: would apply {0} textual updates, {1} IO mappings, {2} device params.".format( + len(textual_updates), len(io_mappings), len(device_params)), + "warnings": [], + }) + proj.close() + return + + if textual_updates: + _apply_textual_updates(proj, textual_updates, results, warnings) + if io_mappings: + _apply_io_mappings(proj, io_mappings, results, warnings) + if device_params: + _apply_device_params(proj, device_params, results, warnings) + + try: + proj.save() + except Exception as exc: + warnings.append("project_save_failed: {0}".format(exc)) + + proj.close() + + emit({ + "ok": True, + "action": "sync_project", + "project_path": pp, + "dry_run": False, + "results": results, + "result_count": len(results), + "warnings": warnings, + }) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/write_io_mapping.py b/python_scripting/windows_codesys_agent/scripts/write_io_mapping.py new file mode 100644 index 0000000..2e31c4a --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/write_io_mapping.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python +"""Write I/O variable mappings in a CODESYS project via ScriptEngine.""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def _discover_project_file(path_value): + if os.path.isfile(path_value): + lower_path = path_value.lower() + if lower_path.endswith(".project") or lower_path.endswith(".projectarchive"): + return path_value + raise RuntimeError( + "project_path must point to a .project or .projectarchive file. Got: {0}".format(path_value) + ) + if os.path.isdir(path_value): + candidates = [] + for entry in os.listdir(path_value): + full_path = os.path.join(path_value, entry) + if os.path.isfile(full_path): + lower_name = entry.lower() + if lower_name.endswith(".project") or lower_name.endswith(".projectarchive"): + candidates.append(full_path) + if len(candidates) == 1: + return candidates[0] + if not candidates: + raise RuntimeError("No .project/.projectarchive in directory: {0}".format(path_value)) + raise RuntimeError("Multiple project files found: {0}".format(", ".join(candidates))) + raise RuntimeError("Project path does not exist: {0}".format(path_value)) + + +def _safe_str(val): + try: + return "{0}".format(val) + except Exception: + return "?" + + +def _name_of(obj): + try: + return obj.get_name(False) + except Exception: + try: + return obj.get_name() + except Exception: + return _safe_str(obj) + + +def _children_of(obj): + try: + return list(obj.get_children(False)) + except Exception: + try: + return list(obj.get_children()) + except Exception: + return [] + + +def _find_device_by_path(proj, device_path): + """Walk the tree to find a device node matching the given slash-separated path.""" + parts = [p.strip() for p in device_path.split("/") if p.strip()] + current_nodes = _children_of(proj) + target = None + + for i, part in enumerate(parts): + found = None + for node in current_nodes: + if _name_of(node) == part: + found = node + break + if found is None: + raise RuntimeError( + "Device path segment '{0}' not found at depth {1}. Path: {2}".format(part, i, device_path) + ) + if i == len(parts) - 1: + target = found + else: + current_nodes = _children_of(found) + + return target + + +def _find_param(param_set, param_id=None, param_name=None): + """Find a parameter by numeric id or name.""" + for param in list(param_set): + if param_id is not None: + try: + if int(param.id) == int(param_id): + return param + except Exception: + pass + if param_name is not None: + try: + pname = _safe_str(getattr(param, "visible_name", "") or getattr(param, "name", "")) + if pname == param_name: + return param + except Exception: + pass + return None + + +def main(): + payload = load_payload() + project_path = payload.get("project_path") + if not project_path: + raise ValueError("project_path is required.") + + mappings = payload.get("mappings", []) + import_csv_path = payload.get("import_csv_path", "") + dry_run = bool(payload.get("dry_run", False)) + + if not mappings and not import_csv_path: + raise ValueError("Provide 'mappings' list or 'import_csv_path'.") + + pp = os.path.abspath(os.path.expanduser(project_path)) + pp = _discover_project_file(pp) + + if "projects" not in globals(): + raise RuntimeError("Run through CODESYS ScriptEngine (--runscript).") + + if projects.primary: + projects.primary.close() + + proj = projects.open(pp, primary=True) + + results = [] + warnings = [] + + if import_csv_path: + try: + for top in _children_of(proj): + if getattr(top, "is_device", False): + if dry_run: + results.append({ + "action": "import_csv", + "csv_path": import_csv_path, + "dry_run": True, + "status": "would_import", + }) + else: + top.import_io_mappings_from_csv(import_csv_path) + results.append({ + "action": "import_csv", + "csv_path": import_csv_path, + "status": "imported", + }) + break + except Exception as exc: + warnings.append("csv_import_failed: {0}".format(exc)) + + for m in mappings: + device_path = m.get("device_path", "") + param_id = m.get("param_id") + param_name = m.get("param_name") + variable = m.get("variable", "") + + if not device_path: + warnings.append("mapping skipped: missing device_path") + continue + if not variable: + warnings.append("mapping skipped: missing variable for {0}".format(device_path)) + continue + + try: + device = _find_device_by_path(proj, device_path) + if device is None: + warnings.append("device not found: {0}".format(device_path)) + continue + + param = _find_param(device.device_parameters, param_id=param_id, param_name=param_name) + if param is None: + warnings.append("param not found: id={0} name={1} on {2}".format(param_id, param_name, device_path)) + continue + + iom = param.io_mapping + if iom is None: + warnings.append("no io_mapping on param {0} of {1}".format(param_id or param_name, device_path)) + continue + + old_var = _safe_str(iom.variable) if iom.variable else "" + entry = { + "device_path": device_path, + "param_id": int(param.id) if hasattr(param, "id") else -1, + "param_name": _safe_str(getattr(param, "visible_name", "")), + "old_variable": old_var, + "new_variable": variable, + } + + if dry_run: + entry["status"] = "would_map" + else: + iom.variable = variable + entry["status"] = "mapped" + + results.append(entry) + except Exception as exc: + warnings.append("mapping_failed({0}): {1}".format(device_path, exc)) + + if not dry_run: + try: + proj.save() + except Exception as exc: + warnings.append("project_save_failed: {0}".format(exc)) + + proj.close() + + emit({ + "ok": True, + "action": "write_io_mapping", + "project_path": pp, + "dry_run": dry_run, + "results": results, + "result_count": len(results), + "warnings": warnings, + }) + + +if __name__ == "__main__": + main() diff --git a/python_scripting/windows_codesys_agent/scripts/write_pou.py b/python_scripting/windows_codesys_agent/scripts/write_pou.py new file mode 100644 index 0000000..5b0f828 --- /dev/null +++ b/python_scripting/windows_codesys_agent/scripts/write_pou.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +"""Write/update textual objects (POU, FB, GVL, NVL, struct, program) in a CODESYS project.""" + +from __future__ import print_function + +import os + +from _common import emit, load_payload + + +def _discover_project_file(path_value): + if os.path.isfile(path_value): + lower_path = path_value.lower() + if lower_path.endswith(".project") or lower_path.endswith(".projectarchive"): + return path_value + raise RuntimeError("project_path must be .project or .projectarchive: {0}".format(path_value)) + if os.path.isdir(path_value): + candidates = [] + for entry in os.listdir(path_value): + full_path = os.path.join(path_value, entry) + if os.path.isfile(full_path): + lower_name = entry.lower() + if lower_name.endswith(".project") or lower_name.endswith(".projectarchive"): + candidates.append(full_path) + if len(candidates) == 1: + return candidates[0] + if not candidates: + raise RuntimeError("No .project/.projectarchive in: {0}".format(path_value)) + raise RuntimeError("Multiple project files: {0}".format(", ".join(candidates))) + raise RuntimeError("Path does not exist: {0}".format(path_value)) + + +def _safe_str(val): + try: + return "{0}".format(val) + except Exception: + return "?" + + +def _name_of(obj): + try: + return obj.get_name(False) + except Exception: + try: + return obj.get_name() + except Exception: + return _safe_str(obj) + + +def _children_of(obj): + try: + return list(obj.get_children(False)) + except Exception: + try: + return list(obj.get_children()) + except Exception: + return [] + + +def _find_by_path(root, path_str): + """Walk slash-separated path from project root to find the target object.""" + parts = [p.strip() for p in path_str.split("/") if p.strip()] + current = root + for i, part in enumerate(parts): + found = None + for child in _children_of(current): + if _name_of(child) == part: + found = child + break + if found is None: + raise RuntimeError("Object not found at '{0}' (segment {1} of '{2}')".format( + part, i, path_str)) + current = found + return current + + +def _find_by_name(root, name): + """Use project.find() with recursive search.""" + results = root.find(name, True) + if not results: + raise RuntimeError("Object '{0}' not found in project.".format(name)) + if len(results) > 1: + paths = [] + for r in results: + try: + paths.append(_name_of(r)) + except Exception: + paths.append("?") + raise RuntimeError("Multiple objects named '{0}': {1}. Use object_path instead.".format( + name, ", ".join(paths))) + return results[0] + + +def _set_text(doc, new_text): + """Replace full text of a ScriptTextDocument.""" + try: + doc.replace(new_text=new_text) + except TypeError: + length = doc.length + if length > 0: + doc.remove(offset=0, length=length) + if new_text: + doc.append(new_text) + + +def main(): + payload = load_payload() + project_path = payload.get("project_path") + if not project_path: + raise ValueError("project_path is required.") + + updates = payload.get("updates", []) + if not updates: + single = {} + for key in ("object_path", "object_name", "declaration", "implementation"): + if key in payload: + single[key] = payload[key] + if single: + updates = [single] + + if not updates: + raise ValueError("Provide 'updates' list or single object_path/object_name + declaration/implementation.") + + dry_run = bool(payload.get("dry_run", False)) + + pp = os.path.abspath(os.path.expanduser(project_path)) + pp = _discover_project_file(pp) + + if "projects" not in globals(): + raise RuntimeError("Run through CODESYS ScriptEngine (--runscript).") + + if projects.primary: + projects.primary.close() + + proj = projects.open(pp, primary=True) + + results = [] + warnings = [] + + for upd in updates: + obj_path = upd.get("object_path", "") + obj_name = upd.get("object_name", "") + decl_text = upd.get("declaration") + impl_text = upd.get("implementation") + + if not obj_path and not obj_name: + warnings.append("update skipped: no object_path or object_name") + continue + + try: + if obj_path: + obj = _find_by_path(proj, obj_path) + else: + obj = _find_by_name(proj, obj_name) + + name = _name_of(obj) + entry = { + "name": name, + "object_path": obj_path or obj_name, + "has_declaration": getattr(obj, "has_textual_declaration", False), + "has_implementation": getattr(obj, "has_textual_implementation", False), + "declaration_updated": False, + "implementation_updated": False, + } + + if decl_text is not None: + if not getattr(obj, "has_textual_declaration", False): + warnings.append("{0}: object has no textual declaration".format(name)) + elif dry_run: + entry["declaration_updated"] = "would_update" + else: + _set_text(obj.textual_declaration, decl_text) + entry["declaration_updated"] = True + + if impl_text is not None: + if not getattr(obj, "has_textual_implementation", False): + warnings.append("{0}: object has no textual implementation".format(name)) + elif dry_run: + entry["implementation_updated"] = "would_update" + else: + _set_text(obj.textual_implementation, impl_text) + entry["implementation_updated"] = True + + results.append(entry) + except Exception as exc: + warnings.append("update_failed({0}): {1}".format(obj_path or obj_name, exc)) + + if not dry_run: + try: + proj.save() + except Exception as exc: + warnings.append("project_save_failed: {0}".format(exc)) + + proj.close() + + emit({ + "ok": True, + "action": "write_pou", + "project_path": pp, + "dry_run": dry_run, + "results": results, + "result_count": len(results), + "warnings": warnings, + }) + + +if __name__ == "__main__": + main() diff --git a/vscode_codesys_companion/.eslintrc.json b/vscode_codesys_companion/.eslintrc.json new file mode 100644 index 0000000..1206d55 --- /dev/null +++ b/vscode_codesys_companion/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "env": { + "node": true, + "es2022": true + }, + "ignorePatterns": [ + "out", + "node_modules" + ], + "rules": { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/explicit-module-boundary-types": "off" + } +} diff --git a/vscode_codesys_companion/.vscodeignore b/vscode_codesys_companion/.vscodeignore new file mode 100644 index 0000000..8cb9338 --- /dev/null +++ b/vscode_codesys_companion/.vscodeignore @@ -0,0 +1,6 @@ +src/** +tsconfig.json +.eslintrc.json +**/*.map +**/*.ts +.vscode-test/** diff --git a/vscode_codesys_companion/README.md b/vscode_codesys_companion/README.md new file mode 100644 index 0000000..514621c --- /dev/null +++ b/vscode_codesys_companion/README.md @@ -0,0 +1,84 @@ +# CODESYS MCP Companion Extension + +VS Code/Cursor companion extension for the CODESYS MCP bridge. + +This extension provides command workflows, project/actions/jobs views, status bar state, and diagnostics for the existing Python MCP bridge located at: + +- `python_scripting/mcp_codesys_bridge/server.py` + +## Features + +- MCP bridge lifecycle and health check +- Project-level commands: + - create project + - stage existing project + - read project inventory + - read device I/O + - write POU + - write I/O mapping + - manage device + - sync project + - build project + - download to device +- Job polling UX through Jobs view +- Error diagnostics surfaced in Problems panel +- ST language support: + - syntax grammar + - snippets + - context menu action for current ST file sync + +## Settings + +All settings are under the `codesys.*` namespace: + +- `codesys.pythonPath` +- `codesys.bridgeScriptPath` +- `codesys.agentUrl` +- `codesys.agentToken` +- `codesys.requestTimeoutSec` +- `codesys.pollIntervalMs` +- `codesys.defaultProjectPath` +- `codesys.defaultDryRunMutations` +- `codesys.allowDownloadCommand` + +## Local Development + +Prerequisites: + +- Node.js 20+ +- npm 10+ +- Python 3 available for bridge startup + +Install and build: + +```bash +cd vscode_codesys_companion +npm install +npm run check +``` + +Run extension in Extension Development Host: + +1. Open `vscode_codesys_companion` in VS Code/Cursor. +2. Press `F5`. +3. Use command palette commands with prefix `CODESYS:`. + +## Packaging + +Build VSIX package: + +```bash +cd vscode_codesys_companion +npm install +npm run compile +npm run package +``` + +## Operational Notes + +- Keep mutating operations in dry-run mode first. +- `download_to_device` command is gated by `codesys.allowDownloadCommand`. +- Bridge auth requires `codesys.agentToken` to match VM agent token. +- Align operations with: + - `python_scripting/how-to-use-codesys-mcp.md` + - `python_scripting/codesys-mcp-bridge-operations.md` diff --git a/vscode_codesys_companion/docs/smoke-test.md b/vscode_codesys_companion/docs/smoke-test.md new file mode 100644 index 0000000..51b37dc --- /dev/null +++ b/vscode_codesys_companion/docs/smoke-test.md @@ -0,0 +1,56 @@ +# Companion Extension Smoke Test + +This validates extension + MCP bridge + VM agent integration. + +## 1. Preconditions + +- Windows VM agent is reachable. +- `codesys.agentUrl` and `codesys.agentToken` are configured in extension settings. +- CODESYS bridge script path resolves correctly. + +Reference operational guide: + +- `../python_scripting/codesys-mcp-bridge-operations.md` + +## 2. Connect + Health + +1. Run `CODESYS: Connect MCP Bridge`. +2. Run `CODESYS: Health Check`. +3. Confirm status bar shows connected and output logs health response. + +## 3. Project Selection + +1. Run `CODESYS: Select Project`. +2. Provide a valid project path. +3. Confirm Projects view shows active project. + +## 4. Read Workflows + +1. Run `CODESYS: Read Project Inventory`. +2. Run `CODESYS: Read Device I/O`. +3. Confirm Jobs view shows `succeeded` for both jobs. + +## 5. Safe Mutation (Dry Run) + +1. Run `CODESYS: Sync Project`. +2. Keep dry-run enabled. +3. Confirm job success and no errors in Problems view. + +## 6. Build + +1. Run `CODESYS: Build Project`. +2. Confirm job success. + +## 7. Optional Deploy + +1. Set `codesys.allowDownloadCommand=true`. +2. Run `CODESYS: Download To Device`. +3. Validate target confirmation and completion. + +## 8. Failure Signal Check + +1. Intentionally provide bad input (for example invalid project path) to `Read Project Inventory`. +2. Confirm failure: + - appears in output + - appears in Jobs view + - appears in Problems panel diagnostics diff --git a/vscode_codesys_companion/language-configuration.json b/vscode_codesys_companion/language-configuration.json new file mode 100644 index 0000000..45d66f0 --- /dev/null +++ b/vscode_codesys_companion/language-configuration.json @@ -0,0 +1,47 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": [ + "(*", + "*)" + ] + }, + "brackets": [ + [ + "(", + ")" + ], + [ + "[", + "]" + ] + ], + "autoClosingPairs": [ + { + "open": "(", + "close": ")" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "\"", + "close": "\"" + } + ], + "surroundingPairs": [ + [ + "(", + ")" + ], + [ + "[", + "]" + ], + [ + "\"", + "\"" + ] + ] +} diff --git a/vscode_codesys_companion/media/codesys.svg b/vscode_codesys_companion/media/codesys.svg new file mode 100644 index 0000000..f5ee396 --- /dev/null +++ b/vscode_codesys_companion/media/codesys.svg @@ -0,0 +1,4 @@ + + + + diff --git a/vscode_codesys_companion/package-lock.json b/vscode_codesys_companion/package-lock.json new file mode 100644 index 0000000..cd75040 --- /dev/null +++ b/vscode_codesys_companion/package-lock.json @@ -0,0 +1,6192 @@ +{ + "name": "codesys-mcp-companion", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "codesys-mcp-companion", + "version": "0.1.0", + "devDependencies": { + "@types/mocha": "^10.0.8", + "@types/node": "^20.16.5", + "@types/vscode": "^1.90.0", + "@typescript-eslint/eslint-plugin": "^8.13.0", + "@typescript-eslint/parser": "^8.13.0", + "@vscode/test-electron": "^2.4.1", + "@vscode/vsce": "^3.2.2", + "eslint": "^8.57.1", + "mocha": "^10.7.3", + "typescript": "^5.6.2" + }, + "engines": { + "vscode": "^1.90.0" + } + }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.23.0.tgz", + "integrity": "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.11.0.tgz", + "integrity": "sha512-zkGNYS3TwY8lUpPIafAmsFCYZbgFixY9y/LZB9GUg0IILoHTqpN26j5OrkL1AQThh/YdZsawe4iWXfp85lFVxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.6.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.6.2.tgz", + "integrity": "sha512-hQjjsekAjB00cM1EmatWJlzhEoK2Qhz7Rj5gvM6tYf8iL7RM3tkxlpU9fG0+ofkulzg9AEEA6dIEnSmDr5ZqUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.2.2.tgz", + "integrity": "sha512-toS+2AePxqyzb0YOKttDOOiSl3jrkK9aiqIvpurpis0O34QcIS5gToqrgT39p04Dpxw3YoUU0lxJKTpSFFfA6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.6.2", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.7.1.tgz", + "integrity": "sha512-Wii5UgUKFEh9Uv6wbq1zr4/Kf+dtjiUuzPrrXzKp8H+ifkvKNzi23V4Nz+6wVyHQn5T28AFuc8VH8OtzvGYecA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.7.1.tgz", + "integrity": "sha512-TdwZ/debWYFD05K3CcoHtwvnCrza29wZxD+BjDTk/V5N7iRqkK1dTTHSD4A8AIgROLiDkHJmIKQbasbmsg8AvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.7.1", + "@textlint/resolver": "15.7.1", + "@textlint/types": "15.7.1", + "chalk": "^4.1.2", + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "lodash": "^4.18.1", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.7.1.tgz", + "integrity": "sha512-Jg+sQW2L/cRJypk59wtcMUVVpt8vmit5ZMT3gUnFwevP3A6Qp1HfOtUy9ObT4hBX3lOSGT/ekcCDxR1pL7uH1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.7.1.tgz", + "integrity": "sha512-8XnO0pgF6mXnm41VvWmBbEIdGPhiCUt31uLZkOis1ECeg/1SoUcIT6Mx/F0e1rukq8l0UlOSeY9a31CsvRMK0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.7.1.tgz", + "integrity": "sha512-Vye/GmFNBTgVzZFtIFJTmLB+s2A7oIADxNG6r9UhfPuY+Czv0z5G3xeyFZZudPlfxURsKUyPIU5XsjOFqVp33A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.7.1" + } + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vscode": { + "version": "1.120.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.120.0.tgz", + "integrity": "sha512-feaT4Rst+FkTch5zz/ZbNCxoIvo55YU80Be2kiL7OJcod4+CUYf2lUBPdIJzozNnSEMq1VRTGrWEcCGFB3fBmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.4", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.5.tgz", + "integrity": "sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vscode/test-electron": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/vsce": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.9.1.tgz", + "integrity": "sha512-MPn5p+DoudI+3GfJSpAZZraE1lgLv0LcwbH3+xy7RgEhty3UIkmUMUA+5jPTDaxXae00AnX5u77FxGM8FhfKKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^11.0.0", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^14.1.0", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "secretlint": "^10.1.2", + "semver": "^7.5.2", + "tmp": "^0.2.3", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^3.2.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.9.tgz", + "integrity": "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/vsce/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@vscode/vsce/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/azure-devops-node-api": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/linkify-it": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", + "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/markdown-it": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.1", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", + "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.4.tgz", + "integrity": "sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "json5": "^2.2.3", + "require-from-string": "^2.0.2" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "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" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yauzl": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.3.1.tgz", + "integrity": "sha512-RNPCUkiE/ZgO4w8i9U5yDQVHaFDdnzaFANElRvpJteCspvmv2VqrRb9lvS6odVD+jqI/zDsxAHJVsafpcheVQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/vscode_codesys_companion/package.json b/vscode_codesys_companion/package.json new file mode 100644 index 0000000..1e8ab52 --- /dev/null +++ b/vscode_codesys_companion/package.json @@ -0,0 +1,240 @@ +{ + "name": "codesys-mcp-companion", + "displayName": "CODESYS MCP Companion", + "description": "VS Code/Cursor companion extension for CODESYS MCP bridge workflows.", + "version": "0.1.0", + "publisher": "nearxos", + "private": true, + "engines": { + "vscode": "^1.90.0" + }, + "categories": [ + "Other", + "Programming Languages" + ], + "activationEvents": [ + "onStartupFinished", + "onCommand:codesys.connect", + "onCommand:codesys.health", + "onCommand:codesys.createProject", + "onCommand:codesys.syncProject", + "onCommand:codesys.buildProject", + "onCommand:codesys.downloadToDevice" + ], + "main": "./out/extension.js", + "contributes": { + "commands": [ + { + "command": "codesys.connect", + "title": "CODESYS: Connect MCP Bridge" + }, + { + "command": "codesys.health", + "title": "CODESYS: Health Check" + }, + { + "command": "codesys.createProject", + "title": "CODESYS: Create Project" + }, + { + "command": "codesys.selectProject", + "title": "CODESYS: Select Project" + }, + { + "command": "codesys.stageExistingProject", + "title": "CODESYS: Stage Existing Project" + }, + { + "command": "codesys.readProjectInventory", + "title": "CODESYS: Read Project Inventory" + }, + { + "command": "codesys.readDeviceIo", + "title": "CODESYS: Read Device I/O" + }, + { + "command": "codesys.writePou", + "title": "CODESYS: Write POU" + }, + { + "command": "codesys.writeIoMapping", + "title": "CODESYS: Write IO Mapping" + }, + { + "command": "codesys.manageDevice", + "title": "CODESYS: Manage Device" + }, + { + "command": "codesys.syncProject", + "title": "CODESYS: Sync Project" + }, + { + "command": "codesys.buildProject", + "title": "CODESYS: Build Project" + }, + { + "command": "codesys.downloadToDevice", + "title": "CODESYS: Download To Device" + }, + { + "command": "codesys.refreshJobs", + "title": "CODESYS: Refresh Jobs" + }, + { + "command": "codesys.openOutput", + "title": "CODESYS: Open Output" + }, + { + "command": "codesys.syncCurrentFile", + "title": "CODESYS: Sync Current ST File" + } + ], + "configuration": { + "title": "CODESYS MCP Companion", + "properties": { + "codesys.pythonPath": { + "type": "string", + "default": "python", + "description": "Python executable used to launch the CODESYS MCP bridge server." + }, + "codesys.bridgeScriptPath": { + "type": "string", + "default": "", + "description": "Absolute path to python_scripting/mcp_codesys_bridge/server.py. If empty, default repo-relative path is used." + }, + "codesys.agentUrl": { + "type": "string", + "default": "http://127.0.0.1:8787", + "description": "Windows CODESYS agent URL passed to MCP bridge." + }, + "codesys.agentToken": { + "type": "string", + "default": "", + "description": "Windows CODESYS agent shared token." + }, + "codesys.requestTimeoutSec": { + "type": "number", + "default": 30, + "minimum": 5, + "description": "CODESYS MCP bridge request timeout in seconds." + }, + "codesys.pollIntervalMs": { + "type": "number", + "default": 1500, + "minimum": 500, + "description": "Polling interval for asynchronous job progress." + }, + "codesys.defaultProjectPath": { + "type": "string", + "default": "", + "description": "Default CODESYS project path used by commands." + }, + "codesys.defaultDryRunMutations": { + "type": "boolean", + "default": true, + "description": "Run mutating operations in dry-run mode unless explicitly disabled in command input." + }, + "codesys.allowDownloadCommand": { + "type": "boolean", + "default": false, + "description": "Enable UI command for download_to_device." + }, + "codesys.mockMode": { + "type": "boolean", + "default": false, + "description": "Use local mocked MCP responses for extension integration testing." + } + } + }, + "viewsContainers": { + "activitybar": [ + { + "id": "codesys", + "title": "CODESYS", + "icon": "media/codesys.svg" + } + ] + }, + "views": { + "codesys": [ + { + "id": "codesysProjectsView", + "name": "Projects" + }, + { + "id": "codesysJobsView", + "name": "Jobs" + }, + { + "id": "codesysActionsView", + "name": "Actions" + } + ] + }, + "menus": { + "editor/context": [ + { + "command": "codesys.syncCurrentFile", + "when": "resourceExtname =~ /\\.st$/ || resourceExtname =~ /\\.gvl$/ || resourceExtname =~ /\\.nvl$/ || resourceExtname =~ /\\.txt$/", + "group": "navigation@90" + } + ], + "view/title": [ + { + "command": "codesys.refreshJobs", + "when": "view == codesysJobsView", + "group": "navigation" + } + ] + }, + "languages": [ + { + "id": "codesys-st", + "aliases": [ + "CODESYS Structured Text", + "ST" + ], + "extensions": [ + ".st", + ".gvl", + ".nvl", + ".type.st" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "codesys-st", + "scopeName": "source.codesys.st", + "path": "./syntaxes/codesys-st.tmLanguage.json" + } + ], + "snippets": [ + { + "language": "codesys-st", + "path": "./snippets/codesys-st.json" + } + ] + }, + "scripts": { + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "lint": "eslint src --ext ts", + "test": "npm run compile && node ./out/test/runTest.js", + "package": "vsce package --no-dependencies", + "check": "npm run compile && npm run lint" + }, + "devDependencies": { + "@types/mocha": "^10.0.8", + "@types/node": "^20.16.5", + "@types/vscode": "^1.90.0", + "@typescript-eslint/eslint-plugin": "^8.13.0", + "@typescript-eslint/parser": "^8.13.0", + "@vscode/test-electron": "^2.4.1", + "@vscode/vsce": "^3.2.2", + "eslint": "^8.57.1", + "mocha": "^10.7.3", + "typescript": "^5.6.2" + } +} diff --git a/vscode_codesys_companion/snippets/codesys-st.json b/vscode_codesys_companion/snippets/codesys-st.json new file mode 100644 index 0000000..854b6d5 --- /dev/null +++ b/vscode_codesys_companion/snippets/codesys-st.json @@ -0,0 +1,46 @@ +{ + "Function Block": { + "prefix": "fb", + "body": [ + "FUNCTION_BLOCK ${1:FB_Name}", + "VAR_INPUT", + " ${2:inputVar} : ${3:BOOL};", + "END_VAR", + "", + "VAR_OUTPUT", + " ${4:outputVar} : ${5:BOOL};", + "END_VAR", + "", + "VAR", + " ${6:internalVar} : ${7:BOOL};", + "END_VAR", + "", + "${0}", + "END_FUNCTION_BLOCK" + ], + "description": "CODESYS function block template" + }, + "Program": { + "prefix": "prg", + "body": [ + "PROGRAM ${1:PLC_PRG}", + "VAR", + " ${2:xStart} : BOOL;", + "END_VAR", + "", + "${0}", + "END_PROGRAM" + ], + "description": "CODESYS program template" + }, + "Timer TON": { + "prefix": "ton", + "body": [ + "${1:tonTimer}(IN := ${2:xEnable}, PT := T#${3:1s});", + "IF ${1:tonTimer}.Q THEN", + " ${0}", + "END_IF;" + ], + "description": "Insert TON call template" + } +} diff --git a/vscode_codesys_companion/src/codesys/payloads.ts b/vscode_codesys_companion/src/codesys/payloads.ts new file mode 100644 index 0000000..1c6bf5c --- /dev/null +++ b/vscode_codesys_companion/src/codesys/payloads.ts @@ -0,0 +1,10 @@ +import { JsonObject } from "../types"; + +export function buildDefaultSyncPayload(projectPath: string): JsonObject { + return { + project_path: projectPath, + textual_updates: [], + io_mappings: [], + device_params: [] + }; +} diff --git a/vscode_codesys_companion/src/codesys/service.ts b/vscode_codesys_companion/src/codesys/service.ts new file mode 100644 index 0000000..f512147 --- /dev/null +++ b/vscode_codesys_companion/src/codesys/service.ts @@ -0,0 +1,243 @@ +import * as vscode from "vscode"; +import { CompanionSettings, resolveBridgeScriptPath } from "../config"; +import { Logger } from "../logging"; +import { McpClient } from "../mcp/client"; +import { JsonObject, McpCallResult } from "../types"; + +export interface CodesysCallOptions { + dryRun?: boolean; + poll?: boolean; +} + +export interface JobRecord { + id: string; + action: string; + status: string; + detail?: string; +} + +export class CodesysService { + private client?: McpClient; + private connected = false; + private readonly jobs = new Map(); + + constructor( + private readonly logger: Logger, + private readonly settings: CompanionSettings, + private readonly diagnostics: vscode.DiagnosticCollection + ) {} + + public getJobs(): JobRecord[] { + return Array.from(this.jobs.values()).sort((a, b) => b.id.localeCompare(a.id)); + } + + public isConnected(): boolean { + return this.connected; + } + + public async connect(): Promise { + if (this.settings.mockMode) { + this.connected = true; + this.logger.info("Mock mode enabled. MCP process is not started."); + return; + } + + if (this.connected && this.client) { + return; + } + const bridgeScriptPath = resolveBridgeScriptPath(this.settings); + const client = new McpClient({ + pythonPath: this.settings.pythonPath, + bridgeScriptPath, + environment: { + CODESYS_AGENT_URL: this.settings.agentUrl, + CODESYS_AGENT_TOKEN: this.settings.agentToken, + CODESYS_AGENT_REQUEST_TIMEOUT_SEC: String(this.settings.requestTimeoutSec) + }, + logger: this.logger + }); + await client.connect(); + await client.listTools(); + this.client = client; + this.connected = true; + } + + public async disconnect(): Promise { + await this.client?.disconnect(); + this.client = undefined; + this.connected = false; + } + + public async call(action: string, payload: JsonObject, options: CodesysCallOptions = {}): Promise { + if (this.settings.mockMode) { + return this.mockCall(action, payload, options); + } + + await this.connect(); + const mergedPayload: JsonObject = { ...payload }; + if (options.dryRun !== undefined) { + mergedPayload.dry_run = options.dryRun; + } + + const result = await this.executeTool(action, mergedPayload); + const jobId = extractJobId(result); + if (!jobId || !options.poll) { + return result; + } + + this.jobs.set(jobId, { id: jobId, action, status: "queued", detail: "submitted" }); + return this.pollJob(action, jobId); + } + + private async mockCall(action: string, payload: JsonObject, options: CodesysCallOptions): Promise { + if (action === "codesys_health") { + return { ok: true, service: "codesys-vm-agent", mocked: true }; + } + + if (action === "codesys_get_job") { + return { + id: String(payload.job_id ?? "mock-job"), + action: "mock_action", + status: "succeeded", + result: { mocked: true } + }; + } + + const dryRun = typeof options.dryRun === "boolean" ? options.dryRun : this.settings.defaultDryRunMutations; + return { + mocked: true, + action, + payload, + dry_run: dryRun, + job_id: `mock-${Date.now()}` + }; + } + + public async codesysHealth(): Promise { + return this.call("codesys_health", {}, { poll: false }); + } + + public async codesysSubmitJob(payload: JsonObject): Promise { + return this.call("codesys_submit_job", payload, { poll: false }); + } + + public async codesysGetJob(payload: JsonObject): Promise { + return this.call("codesys_get_job", payload, { poll: false }); + } + + public async createProject(payload: JsonObject, options: CodesysCallOptions): Promise { + return this.call("create_project", payload, { ...options, poll: true }); + } + + public async stageExistingProject(payload: JsonObject, options: CodesysCallOptions): Promise { + return this.call("stage_existing_project", payload, options); + } + + public async readProjectInventory(payload: JsonObject): Promise { + return this.call("read_project_inventory", payload, { poll: true }); + } + + public async readDeviceIo(payload: JsonObject): Promise { + return this.call("read_device_io", payload, { poll: true }); + } + + public async writePou(payload: JsonObject, options: CodesysCallOptions): Promise { + return this.call("write_pou", payload, { ...options, poll: true }); + } + + public async writeIoMapping(payload: JsonObject, options: CodesysCallOptions): Promise { + return this.call("write_io_mapping", payload, { ...options, poll: true }); + } + + public async manageDevice(payload: JsonObject, options: CodesysCallOptions): Promise { + return this.call("manage_device", payload, { ...options, poll: true }); + } + + public async syncProject(payload: JsonObject, options: CodesysCallOptions): Promise { + return this.call("sync_project", payload, { ...options, poll: true }); + } + + public async buildProject(payload: JsonObject): Promise { + return this.call("build_project", payload, { poll: true }); + } + + public async downloadToDevice(payload: JsonObject): Promise { + return this.call("download_to_device", payload, { poll: true }); + } + + public async pollJob(action: string, jobId: string): Promise { + const pollInterval = Math.max(500, this.settings.pollIntervalMs); + while (true) { + const result = await this.executeTool("codesys_get_job", { job_id: jobId }); + const status = String(result.status ?? "unknown"); + this.jobs.set(jobId, { + id: jobId, + action, + status, + detail: status === "failed" ? String(result.error ?? "Unknown error") : undefined + }); + + if (status === "succeeded" || status === "failed") { + if (status === "failed") { + this.raiseDiagnostic(String(result.error ?? `Job ${jobId} failed.`)); + } else { + this.clearDiagnostics(); + } + return result; + } + await sleep(pollInterval); + } + } + + private async executeTool(tool: string, payload: JsonObject): Promise { + const response = await this.client?.callTool(tool, payload); + if (!response) { + throw new Error(`No response from MCP tool ${tool}`); + } + const parsed = this.parseToolResult(response); + return parsed; + } + + private parseToolResult(response: McpCallResult): JsonObject { + if (response.structuredContent) { + return response.structuredContent; + } + const text = response.content?.find((item) => item.type === "text")?.text; + if (!text) { + return {}; + } + try { + return JSON.parse(text) as JsonObject; + } catch { + return { text }; + } + } + + private raiseDiagnostic(message: string): void { + const wsFolder = vscode.workspace.workspaceFolders?.[0]?.uri; + if (!wsFolder) { + return; + } + const uri = vscode.Uri.joinPath(wsFolder, ".codesys-mcp-errors"); + const diagnostic = new vscode.Diagnostic( + new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 1)), + message, + vscode.DiagnosticSeverity.Error + ); + diagnostic.source = "codesys-mcp"; + this.diagnostics.set(uri, [diagnostic]); + } + + private clearDiagnostics(): void { + this.diagnostics.clear(); + } +} + +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +function extractJobId(result: JsonObject): string | undefined { + const id = result.job_id; + return typeof id === "string" && id.length > 0 ? id : undefined; +} diff --git a/vscode_codesys_companion/src/commands.ts b/vscode_codesys_companion/src/commands.ts new file mode 100644 index 0000000..8ceb5de --- /dev/null +++ b/vscode_codesys_companion/src/commands.ts @@ -0,0 +1,296 @@ +import * as path from "path"; +import * as vscode from "vscode"; +import { CompanionSettings } from "./config"; +import { buildDefaultSyncPayload } from "./codesys/payloads"; +import { CodesysService } from "./codesys/service"; +import { Logger } from "./logging"; +import { ExtensionState } from "./state"; +import { JsonObject } from "./types"; +import { ActionsViewProvider, JobsViewProvider, ProjectsViewProvider } from "./views"; + +interface CommandDeps { + service: CodesysService; + logger: Logger; + state: ExtensionState; + settings: CompanionSettings; + status: vscode.StatusBarItem; + projectsView: ProjectsViewProvider; + jobsView: JobsViewProvider; + actionsView: ActionsViewProvider; +} + +export function registerCommands(context: vscode.ExtensionContext, deps: CommandDeps): void { + const register = (name: string, callback: () => Promise) => + context.subscriptions.push( + vscode.commands.registerCommand(name, async () => { + try { + await callback(); + deps.jobsView.refresh(); + deps.projectsView.refresh(); + deps.actionsView.refresh(); + updateStatus(deps); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + deps.logger.error(message); + vscode.window.showErrorMessage(`CODESYS: ${message}`); + } + }) + ); + + register("codesys.connect", async () => { + await deps.service.connect(); + deps.logger.info("Connected to MCP bridge."); + vscode.window.showInformationMessage("Connected to CODESYS MCP bridge."); + }); + + register("codesys.health", async () => { + const result = await deps.service.codesysHealth(); + deps.logger.info(`health: ${JSON.stringify(result)}`); + vscode.window.showInformationMessage(`CODESYS health ok=${String(result.ok ?? "unknown")}`); + }); + + register("codesys.createProject", async () => { + const projectName = await ask("Project name", "NewProject"); + const outputDir = await ask("Output directory", "C:\\\\Temp\\\\codesys-projects"); + if (!projectName || !outputDir) { + return; + } + const template = (await ask("Template", "standard")) ?? "standard"; + const dryRun = await askDryRun(deps.settings.defaultDryRunMutations); + const result = await deps.service.createProject( + { project_name: projectName, output_dir: outputDir, template }, + { dryRun } + ); + deps.logger.info(`create_project: ${JSON.stringify(result)}`); + }); + + register("codesys.selectProject", async () => { + const value = await vscode.window.showInputBox({ + prompt: "Project path (.project/.projectarchive or folder)", + value: deps.state.activeProjectPath || deps.settings.defaultProjectPath + }); + if (!value) { + return; + } + await deps.state.setActiveProjectPath(value); + }); + + register("codesys.stageExistingProject", async () => { + const sourcePath = await ask("Source path", ""); + const projectsRootDir = await ask("Projects root dir", ""); + if (!sourcePath || !projectsRootDir) { + return; + } + const projectFolderName = (await ask("Project folder name (optional)", "")) ?? ""; + const move = await askQuickPickBoolean("Move source instead of copy?", false); + const overwrite = await askQuickPickBoolean("Overwrite existing destination?", false); + const dryRun = await askDryRun(deps.settings.defaultDryRunMutations); + + const result = await deps.service.stageExistingProject( + { source_path: sourcePath, projects_root_dir: projectsRootDir, project_folder_name: projectFolderName, move, overwrite }, + { dryRun, poll: true } + ); + deps.logger.info(`stage_existing_project: ${JSON.stringify(result)}`); + }); + + register("codesys.readProjectInventory", async () => { + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const maxDepth = Number.parseInt((await ask("max_depth", "3")) ?? "3", 10); + const result = await deps.service.readProjectInventory({ project_path: projectPath, max_depth: Number.isFinite(maxDepth) ? maxDepth : 3 }); + deps.logger.info(`read_project_inventory: ${JSON.stringify(result)}`); + }); + + register("codesys.readDeviceIo", async () => { + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const channelsOnly = await askQuickPickBoolean("Read channels only?", true); + const maxDepth = Number.parseInt((await ask("max_depth", "10")) ?? "10", 10); + const result = await deps.service.readDeviceIo({ + project_path: projectPath, + channels_only: channelsOnly, + max_depth: Number.isFinite(maxDepth) ? maxDepth : 10 + }); + deps.logger.info(`read_device_io: ${JSON.stringify(result)}`); + }); + + register("codesys.writePou", async () => { + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const objectPath = await ask("Object path", ""); + const declaration = await ask("Declaration text (optional)", ""); + const implementation = await ask("Implementation text (optional)", ""); + if (!objectPath) { + return; + } + const dryRun = await askDryRun(deps.settings.defaultDryRunMutations); + const result = await deps.service.writePou( + { project_path: projectPath, object_path: objectPath, declaration: declaration ?? "", implementation: implementation ?? "" }, + { dryRun } + ); + deps.logger.info(`write_pou: ${JSON.stringify(result)}`); + }); + + register("codesys.writeIoMapping", async () => { + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const devicePath = await ask("Device path", ""); + const paramId = await ask("Param id", ""); + const variable = await ask("IEC variable", ""); + if (!devicePath || !paramId || !variable) { + return; + } + const dryRun = await askDryRun(deps.settings.defaultDryRunMutations); + const mappings = [{ device_path: devicePath, param_id: paramId, variable }]; + const result = await deps.service.writeIoMapping( + { project_path: projectPath, mappings }, + { dryRun } + ); + deps.logger.info(`write_io_mapping: ${JSON.stringify(result)}`); + }); + + register("codesys.manageDevice", async () => { + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const operationText = await ask("Operation JSON (single operation object)", '{"action_type":"rename","device_path":"Device/X","new_name":"X"}'); + if (!operationText) { + return; + } + const operation = JSON.parse(operationText) as JsonObject; + const dryRun = await askDryRun(deps.settings.defaultDryRunMutations); + const result = await deps.service.manageDevice({ project_path: projectPath, operations: [operation] }, { dryRun }); + deps.logger.info(`manage_device: ${JSON.stringify(result)}`); + }); + + register("codesys.syncProject", async () => { + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const dryRun = await askDryRun(deps.settings.defaultDryRunMutations); + const result = await deps.service.syncProject(buildDefaultSyncPayload(projectPath), { dryRun }); + deps.logger.info(`sync_project: ${JSON.stringify(result)}`); + }); + + register("codesys.buildProject", async () => { + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const result = await deps.service.buildProject({ project_path: projectPath }); + deps.logger.info(`build_project: ${JSON.stringify(result)}`); + }); + + register("codesys.downloadToDevice", async () => { + if (!deps.settings.allowDownloadCommand) { + vscode.window.showWarningMessage("Download command disabled by setting codesys.allowDownloadCommand."); + return; + } + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const targetName = await ask("Target name", ""); + if (!targetName) { + return; + } + const startApplication = await askQuickPickBoolean("Start application after download?", false); + const result = await deps.service.downloadToDevice( + { project_path: projectPath, target_name: targetName, start_application: startApplication }, + ); + deps.logger.info(`download_to_device: ${JSON.stringify(result)}`); + }); + + register("codesys.refreshJobs", async () => { + deps.jobsView.refresh(); + deps.logger.info("Jobs view refreshed."); + }); + + register("codesys.openOutput", async () => { + deps.logger.show(); + }); + + register("codesys.syncCurrentFile", async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + const projectPath = await requireProjectPath(deps); + if (!projectPath) { + return; + } + const absFilePath = editor.document.uri.fsPath; + const objectPath = inferCodesysObjectPath(absFilePath); + const declaration = editor.document.getText(); + const dryRun = await askDryRun(deps.settings.defaultDryRunMutations); + const result = await deps.service.writePou( + { project_path: projectPath, object_path: objectPath, declaration, implementation: "" }, + { dryRun } + ); + deps.logger.info(`syncCurrentFile write_pou: ${JSON.stringify(result)}`); + }); + + updateStatus(deps); +} + +function updateStatus(deps: CommandDeps): void { + deps.status.text = deps.service.isConnected() ? "CODESYS: Connected" : "CODESYS: Disconnected"; + deps.status.tooltip = deps.state.activeProjectPath || "No active project selected"; +} + +async function requireProjectPath(deps: CommandDeps): Promise { + if (deps.state.activeProjectPath) { + return deps.state.activeProjectPath; + } + const fallback = deps.settings.defaultProjectPath; + const chosen = await vscode.window.showInputBox({ + prompt: "Project path", + value: fallback + }); + if (!chosen) { + return undefined; + } + await deps.state.setActiveProjectPath(chosen); + return chosen; +} + +async function ask(prompt: string, value: string): Promise { + return vscode.window.showInputBox({ prompt, value }); +} + +async function askQuickPickBoolean(prompt: string, defaultValue: boolean): Promise { + const defaultLabel = defaultValue ? "yes" : "no"; + const selected = await vscode.window.showQuickPick(["yes", "no"], { placeHolder: `${prompt} (default: ${defaultLabel})` }); + return (selected ?? defaultLabel) === "yes"; +} + +async function askDryRun(defaultValue: boolean): Promise { + return askQuickPickBoolean("Run in dry-run mode?", defaultValue); +} + +function inferCodesysObjectPath(absFilePath: string): string { + const marker = `${path.sep}codesys_tree${path.sep}`; + const index = absFilePath.indexOf(marker); + if (index < 0) { + return path.basename(absFilePath); + } + const relative = absFilePath.slice(index + marker.length).replaceAll(path.sep, "/"); + const withoutExt = relative + .replace(/\.program\.st$/i, "") + .replace(/\.fb\.st$/i, "") + .replace(/\.type\.st$/i, "") + .replace(/\.gvl$/i, "") + .replace(/\.nvl$/i, "") + .replace(/\.st$/i, ""); + return withoutExt; +} diff --git a/vscode_codesys_companion/src/config.ts b/vscode_codesys_companion/src/config.ts new file mode 100644 index 0000000..77e5a77 --- /dev/null +++ b/vscode_codesys_companion/src/config.ts @@ -0,0 +1,44 @@ +import * as path from "path"; +import * as vscode from "vscode"; + +export interface CompanionSettings { + pythonPath: string; + bridgeScriptPath: string; + agentUrl: string; + agentToken: string; + requestTimeoutSec: number; + pollIntervalMs: number; + defaultProjectPath: string; + defaultDryRunMutations: boolean; + allowDownloadCommand: boolean; + mockMode: boolean; +} + +export function readSettings(): CompanionSettings { + const cfg = vscode.workspace.getConfiguration("codesys"); + return { + pythonPath: cfg.get("pythonPath", "python"), + bridgeScriptPath: cfg.get("bridgeScriptPath", ""), + agentUrl: cfg.get("agentUrl", "http://127.0.0.1:8787"), + agentToken: cfg.get("agentToken", ""), + requestTimeoutSec: cfg.get("requestTimeoutSec", 30), + pollIntervalMs: cfg.get("pollIntervalMs", 1500), + defaultProjectPath: cfg.get("defaultProjectPath", ""), + defaultDryRunMutations: cfg.get("defaultDryRunMutations", true), + allowDownloadCommand: cfg.get("allowDownloadCommand", false), + mockMode: cfg.get("mockMode", false) + }; +} + +export function resolveBridgeScriptPath(settings: CompanionSettings): string { + if (settings.bridgeScriptPath.trim()) { + return settings.bridgeScriptPath.trim(); + } + + const root = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + if (root) { + return path.resolve(root, "python_scripting", "mcp_codesys_bridge", "server.py"); + } + + throw new Error("No workspace open and codesys.bridgeScriptPath is empty."); +} diff --git a/vscode_codesys_companion/src/extension.ts b/vscode_codesys_companion/src/extension.ts new file mode 100644 index 0000000..93edcaf --- /dev/null +++ b/vscode_codesys_companion/src/extension.ts @@ -0,0 +1,51 @@ +import * as vscode from "vscode"; +import { registerCommands } from "./commands"; +import { readSettings } from "./config"; +import { CodesysService } from "./codesys/service"; +import { Logger } from "./logging"; +import { ExtensionState } from "./state"; +import { ActionsViewProvider, JobsViewProvider, ProjectsViewProvider } from "./views"; + +export async function activate(context: vscode.ExtensionContext): Promise { + const logger = new Logger(); + const settings = readSettings(); + const diagnostics = vscode.languages.createDiagnosticCollection("codesys-mcp"); + const service = new CodesysService(logger, settings, diagnostics); + const state = new ExtensionState(context); + + const status = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 20); + status.command = "codesys.openOutput"; + status.text = "CODESYS: Disconnected"; + status.show(); + + const projectsView = new ProjectsViewProvider(state); + const jobsView = new JobsViewProvider(service); + const actionsView = new ActionsViewProvider(); + + context.subscriptions.push( + logger, + diagnostics, + status, + vscode.window.registerTreeDataProvider("codesysProjectsView", projectsView), + vscode.window.registerTreeDataProvider("codesysJobsView", jobsView), + vscode.window.registerTreeDataProvider("codesysActionsView", actionsView), + { + dispose: () => { + void service.disconnect(); + } + } + ); + + registerCommands(context, { + service, + logger, + state, + settings, + status, + projectsView, + jobsView, + actionsView + }); +} + +export function deactivate(): void {} diff --git a/vscode_codesys_companion/src/logging.ts b/vscode_codesys_companion/src/logging.ts new file mode 100644 index 0000000..594e52e --- /dev/null +++ b/vscode_codesys_companion/src/logging.ts @@ -0,0 +1,29 @@ +import * as vscode from "vscode"; + +export class Logger { + private readonly output: vscode.OutputChannel; + + constructor() { + this.output = vscode.window.createOutputChannel("CODESYS MCP Companion"); + } + + public info(message: string): void { + this.output.appendLine(`[INFO] ${new Date().toISOString()} ${message}`); + } + + public warn(message: string): void { + this.output.appendLine(`[WARN] ${new Date().toISOString()} ${message}`); + } + + public error(message: string): void { + this.output.appendLine(`[ERROR] ${new Date().toISOString()} ${message}`); + } + + public show(): void { + this.output.show(true); + } + + public dispose(): void { + this.output.dispose(); + } +} diff --git a/vscode_codesys_companion/src/mcp/client.ts b/vscode_codesys_companion/src/mcp/client.ts new file mode 100644 index 0000000..33d9b71 --- /dev/null +++ b/vscode_codesys_companion/src/mcp/client.ts @@ -0,0 +1,190 @@ +import { ChildProcessWithoutNullStreams, spawn } from "child_process"; +import * as fs from "fs"; +import { Logger } from "../logging"; +import { JsonObject, JsonValue, McpCallResult, McpTool } from "../types"; + +interface PendingRequest { + resolve: (value: JsonValue) => void; + reject: (reason?: unknown) => void; +} + +interface McpClientOptions { + pythonPath: string; + bridgeScriptPath: string; + environment: Record; + logger: Logger; +} + +export class McpClient { + private process?: ChildProcessWithoutNullStreams; + private readonly pending = new Map(); + private readonly logger: Logger; + private nextRequestId = 1; + private readBuffer = Buffer.alloc(0); + private initialized = false; + + constructor(private readonly options: McpClientOptions) { + this.logger = options.logger; + } + + public async connect(): Promise { + if (this.initialized) { + return; + } + + if (!fs.existsSync(this.options.bridgeScriptPath)) { + throw new Error(`Bridge script not found: ${this.options.bridgeScriptPath}`); + } + + this.process = spawn(this.options.pythonPath, [this.options.bridgeScriptPath], { + env: { + ...process.env, + ...this.options.environment + }, + stdio: "pipe" + }); + + this.process.stderr.on("data", (chunk: Buffer) => { + this.logger.warn(`MCP stderr: ${chunk.toString("utf8").trim()}`); + }); + + this.process.on("exit", (code) => { + this.logger.warn(`MCP process exited: ${code ?? -1}`); + this.initialized = false; + }); + + this.process.stdout.on("data", (chunk: Buffer) => this.handleStdout(chunk)); + + await this.request("initialize", { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { + name: "codesys-mcp-companion", + version: "0.1.0" + } + }); + await this.notify("notifications/initialized", {}); + this.initialized = true; + this.logger.info("MCP connection initialized."); + } + + public async disconnect(): Promise { + if (!this.process) { + return; + } + this.process.kill(); + this.process = undefined; + this.pending.clear(); + this.initialized = false; + } + + public async listTools(): Promise { + const response = (await this.request("tools/list", {})) as JsonObject; + const tools = response.tools as JsonValue[] | undefined; + if (!Array.isArray(tools)) { + return []; + } + return tools as unknown as McpTool[]; + } + + public async callTool(name: string, argumentsPayload: JsonObject): Promise { + const response = (await this.request("tools/call", { + name, + arguments: argumentsPayload + })) as JsonObject; + return response as unknown as McpCallResult; + } + + private async notify(method: string, params: JsonObject): Promise { + this.ensureConnected(); + this.writeMessage({ jsonrpc: "2.0", method, params }); + } + + private async request(method: string, params: JsonObject): Promise { + this.ensureConnected(); + const id = this.nextRequestId++; + const payload = { + jsonrpc: "2.0", + id, + method, + params + }; + const responsePromise = new Promise((resolve, reject) => { + this.pending.set(id, { resolve, reject }); + setTimeout(() => { + if (this.pending.has(id)) { + this.pending.delete(id); + reject(new Error(`MCP request timeout (${method})`)); + } + }, 60_000); + }); + this.writeMessage(payload); + return responsePromise; + } + + private ensureConnected(): void { + if (!this.process || !this.process.stdin.writable) { + throw new Error("MCP process is not connected."); + } + } + + private writeMessage(message: JsonObject): void { + const raw = JSON.stringify(message); + const framed = `Content-Length: ${Buffer.byteLength(raw, "utf8")}\r\n\r\n${raw}`; + this.process?.stdin.write(framed, "utf8"); + } + + private handleStdout(chunk: Buffer): void { + this.readBuffer = Buffer.concat([this.readBuffer, chunk]); + + while (true) { + const splitMarker = this.readBuffer.indexOf("\r\n\r\n"); + if (splitMarker === -1) { + return; + } + const header = this.readBuffer.subarray(0, splitMarker).toString("utf8"); + const contentLengthLine = header + .split("\r\n") + .find((line) => line.toLowerCase().startsWith("content-length:")); + if (!contentLengthLine) { + this.logger.warn("Dropping MCP frame with no Content-Length."); + this.readBuffer = this.readBuffer.subarray(splitMarker + 4); + continue; + } + const lengthText = contentLengthLine.split(":")[1]?.trim() ?? "0"; + const length = Number.parseInt(lengthText, 10); + const bodyStart = splitMarker + 4; + const bodyEnd = bodyStart + length; + if (this.readBuffer.length < bodyEnd) { + return; + } + const bodyRaw = this.readBuffer.subarray(bodyStart, bodyEnd).toString("utf8"); + this.readBuffer = this.readBuffer.subarray(bodyEnd); + this.handleMessage(bodyRaw); + } + } + + private handleMessage(raw: string): void { + try { + const message = JSON.parse(raw) as { + id?: number; + result?: JsonValue; + error?: { message?: string }; + }; + if (typeof message.id === "number") { + const pending = this.pending.get(message.id); + if (!pending) { + return; + } + this.pending.delete(message.id); + if (message.error) { + pending.reject(new Error(message.error.message ?? "Unknown MCP error")); + return; + } + pending.resolve(message.result ?? null); + } + } catch (error) { + this.logger.error(`Failed to parse MCP message: ${String(error)}`); + } + } +} diff --git a/vscode_codesys_companion/src/state.ts b/vscode_codesys_companion/src/state.ts new file mode 100644 index 0000000..18802e4 --- /dev/null +++ b/vscode_codesys_companion/src/state.ts @@ -0,0 +1,15 @@ +import * as vscode from "vscode"; + +const ACTIVE_PROJECT_KEY = "codesys.activeProjectPath"; + +export class ExtensionState { + constructor(private readonly context: vscode.ExtensionContext) {} + + public get activeProjectPath(): string { + return this.context.globalState.get(ACTIVE_PROJECT_KEY, ""); + } + + public async setActiveProjectPath(path: string): Promise { + await this.context.globalState.update(ACTIVE_PROJECT_KEY, path); + } +} diff --git a/vscode_codesys_companion/src/test/runTest.ts b/vscode_codesys_companion/src/test/runTest.ts new file mode 100644 index 0000000..0e5ed2d --- /dev/null +++ b/vscode_codesys_companion/src/test/runTest.ts @@ -0,0 +1,19 @@ +import * as path from "path"; +import { runTests } from "@vscode/test-electron"; + +async function main(): Promise { + try { + const extensionDevelopmentPath = path.resolve(__dirname, "..", ".."); + const extensionTestsPath = path.resolve(__dirname, "./suite/index"); + + await runTests({ + extensionDevelopmentPath, + extensionTestsPath + }); + } catch (error) { + console.error("Failed to run tests", error); + process.exit(1); + } +} + +void main(); diff --git a/vscode_codesys_companion/src/test/suite/extension.test.ts b/vscode_codesys_companion/src/test/suite/extension.test.ts new file mode 100644 index 0000000..8429b88 --- /dev/null +++ b/vscode_codesys_companion/src/test/suite/extension.test.ts @@ -0,0 +1,7 @@ +import * as assert from "assert"; + +suite("Extension Smoke", () => { + test("sanity", () => { + assert.strictEqual(1 + 1, 2); + }); +}); diff --git a/vscode_codesys_companion/src/test/suite/index.ts b/vscode_codesys_companion/src/test/suite/index.ts new file mode 100644 index 0000000..4b42d22 --- /dev/null +++ b/vscode_codesys_companion/src/test/suite/index.ts @@ -0,0 +1,22 @@ +import Mocha from "mocha"; +import * as path from "path"; + +export function run(): Promise { + const mocha = new Mocha({ + ui: "tdd", + color: true + }); + const testsRoot = path.resolve(__dirname, "..", "suite"); + + return new Promise((resolve, reject) => { + mocha.addFile(path.resolve(testsRoot, "extension.test.js")); + mocha.addFile(path.resolve(testsRoot, "payloads.test.js")); + mocha.run((failures: number) => { + if (failures > 0) { + reject(new Error(`${failures} tests failed.`)); + return; + } + resolve(); + }); + }); +} diff --git a/vscode_codesys_companion/src/test/suite/payloads.test.ts b/vscode_codesys_companion/src/test/suite/payloads.test.ts new file mode 100644 index 0000000..6bb5f82 --- /dev/null +++ b/vscode_codesys_companion/src/test/suite/payloads.test.ts @@ -0,0 +1,12 @@ +import * as assert from "assert"; +import { buildDefaultSyncPayload } from "../../codesys/payloads"; + +suite("Payloads", () => { + test("buildDefaultSyncPayload creates empty arrays", () => { + const payload = buildDefaultSyncPayload("C:\\project.project"); + assert.strictEqual(payload.project_path, "C:\\project.project"); + assert.deepStrictEqual(payload.textual_updates, []); + assert.deepStrictEqual(payload.io_mappings, []); + assert.deepStrictEqual(payload.device_params, []); + }); +}); diff --git a/vscode_codesys_companion/src/types.ts b/vscode_codesys_companion/src/types.ts new file mode 100644 index 0000000..5ea58a5 --- /dev/null +++ b/vscode_codesys_companion/src/types.ts @@ -0,0 +1,27 @@ +export type JsonValue = string | number | boolean | null | JsonObject | JsonValue[]; + +export interface JsonObject { + [key: string]: JsonValue; +} + +export interface McpTool { + name: string; + description?: string; + inputSchema?: JsonObject; +} + +export interface McpCallResult { + content?: Array<{ type: string; text?: string }>; + structuredContent?: JsonObject; + isError?: boolean; +} + +export interface JobSummary { + id: string; + action: string; + status: string; + created_at: number; + started_at?: number; + finished_at?: number; + error?: string | null; +} diff --git a/vscode_codesys_companion/src/views.ts b/vscode_codesys_companion/src/views.ts new file mode 100644 index 0000000..15b3225 --- /dev/null +++ b/vscode_codesys_companion/src/views.ts @@ -0,0 +1,91 @@ +import * as vscode from "vscode"; +import { CodesysService, JobRecord } from "./codesys/service"; +import { ExtensionState } from "./state"; + +export class SimpleItem extends vscode.TreeItem { + constructor(label: string, description?: string, command?: vscode.Command) { + super(label, vscode.TreeItemCollapsibleState.None); + this.description = description; + this.command = command; + } +} + +export class ProjectsViewProvider implements vscode.TreeDataProvider { + private emitter = new vscode.EventEmitter(); + public readonly onDidChangeTreeData = this.emitter.event; + + constructor(private readonly state: ExtensionState) {} + + public refresh(): void { + this.emitter.fire(undefined); + } + + public getTreeItem(element: SimpleItem): vscode.TreeItem { + return element; + } + + public getChildren(): Thenable { + const active = this.state.activeProjectPath || "(none)"; + return Promise.resolve([ + new SimpleItem("Active Project", active), + new SimpleItem("Select Project", "Pick a project path", { + title: "Select Project", + command: "codesys.selectProject" + }) + ]); + } +} + +export class JobsViewProvider implements vscode.TreeDataProvider { + private emitter = new vscode.EventEmitter(); + public readonly onDidChangeTreeData = this.emitter.event; + + constructor(private readonly service: CodesysService) {} + + public refresh(): void { + this.emitter.fire(undefined); + } + + public getTreeItem(element: SimpleItem): vscode.TreeItem { + return element; + } + + public getChildren(): Thenable { + const jobs = this.service.getJobs(); + if (jobs.length === 0) { + return Promise.resolve([new SimpleItem("No jobs yet")]); + } + return Promise.resolve(jobs.slice(0, 20).map(toJobItem)); + } +} + +export class ActionsViewProvider implements vscode.TreeDataProvider { + private emitter = new vscode.EventEmitter(); + public readonly onDidChangeTreeData = this.emitter.event; + + public refresh(): void { + this.emitter.fire(undefined); + } + + public getTreeItem(element: SimpleItem): vscode.TreeItem { + return element; + } + + public getChildren(): Thenable { + const make = (label: string, command: string) => + new SimpleItem(label, undefined, { title: label, command }); + return Promise.resolve([ + make("Health Check", "codesys.health"), + make("Create Project", "codesys.createProject"), + make("Read Project Inventory", "codesys.readProjectInventory"), + make("Read Device I/O", "codesys.readDeviceIo"), + make("Sync Project", "codesys.syncProject"), + make("Build Project", "codesys.buildProject"), + make("Download To Device", "codesys.downloadToDevice") + ]); + } +} + +function toJobItem(job: JobRecord): SimpleItem { + return new SimpleItem(`${job.action}`, `${job.status} ${job.id.slice(0, 8)}`); +} diff --git a/vscode_codesys_companion/syntaxes/codesys-st.tmLanguage.json b/vscode_codesys_companion/syntaxes/codesys-st.tmLanguage.json new file mode 100644 index 0000000..2a13598 --- /dev/null +++ b/vscode_codesys_companion/syntaxes/codesys-st.tmLanguage.json @@ -0,0 +1,36 @@ +{ + "scopeName": "source.codesys.st", + "name": "CODESYS ST", + "patterns": [ + { + "name": "comment.block.codesys", + "begin": "\\(\\*", + "end": "\\*\\)" + }, + { + "name": "comment.line.double-slash.codesys", + "match": "//.*$" + }, + { + "name": "keyword.control.codesys", + "match": "\\b(IF|THEN|ELSE|ELSIF|CASE|OF|FOR|TO|BY|DO|WHILE|REPEAT|UNTIL|RETURN|EXIT)\\b" + }, + { + "name": "keyword.declaration.codesys", + "match": "\\b(PROGRAM|FUNCTION|FUNCTION_BLOCK|TYPE|STRUCT|END_STRUCT|VAR|VAR_INPUT|VAR_OUTPUT|VAR_IN_OUT|VAR_GLOBAL|VAR_TEMP|END_VAR|END_FUNCTION|END_FUNCTION_BLOCK|END_PROGRAM|END_TYPE)\\b" + }, + { + "name": "constant.language.bool.codesys", + "match": "\\b(TRUE|FALSE)\\b" + }, + { + "name": "constant.numeric.codesys", + "match": "\\b\\d+(?:\\.\\d+)?\\b" + }, + { + "name": "string.quoted.double.codesys", + "begin": "\"", + "end": "\"" + } + ] +} diff --git a/vscode_codesys_companion/tsconfig.json b/vscode_codesys_companion/tsconfig.json new file mode 100644 index 0000000..21e78c9 --- /dev/null +++ b/vscode_codesys_companion/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2022", + "outDir": "out", + "lib": [ + "ES2022" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "moduleResolution": "node", + "types": [ + "node", + "vscode" + ], + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + ".vscode-test" + ] +}