diff --git a/.anusignore b/.anusignore
deleted file mode 100644
index b4c0de3..0000000
--- a/.anusignore
+++ /dev/null
@@ -1,7 +0,0 @@
-# ANUS ignore file
-# This should be recognized by the new system
-
-node_modules/
-*.log
-.git/
-test-ignore-file.txt
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 0d6a1a3..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-root = true
-
-[*]
-charset = utf-8
-insert_final_newline = true
-end_of_line = lf
-indent_style = space
-indent_size = 2
-max_line_length = 80
-
-[Makefile]
-indent_style = tab
-indent_size = 8
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index deab5ae..0000000
--- a/.gitattributes
+++ /dev/null
@@ -1,24 +0,0 @@
-# Set the default behavior for all files to automatically handle line endings.
-# This will ensure that all text files are normalized to use LF (line feed)
-# line endings in the repository, which helps prevent cross-platform issues.
-* text=auto eol=lf
-
-# Explicitly declare files that must have LF line endings for proper execution
-# on Unix-like systems.
-*.sh eol=lf
-*.bash eol=lf
-Makefile eol=lf
-
-# Explicitly declare binary file types to prevent Git from attempting to
-# normalize their line endings.
-*.png binary
-*.jpg binary
-*.jpeg binary
-*.gif binary
-*.ico binary
-*.pdf binary
-*.woff binary
-*.woff2 binary
-*.eot binary
-*.ttf binary
-*.otf binary
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
deleted file mode 100644
index c526e7c..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ /dev/null
@@ -1,57 +0,0 @@
-name: 'Bug Report'
-description: 'Report a bug to help us improve ANUS'
-labels:
- - 'kind/bug'
- - 'status/need-triage'
-body:
- - type: 'markdown'
- attributes:
- value: |-
- > [!IMPORTANT]
- > Thanks for taking the time to fill out this bug report!
- >
- > Please search **[existing issues](https://github.com/anus-dev/anus/issues)** to see if an issue already exists for the bug you encountered.
-
- - type: 'textarea'
- id: 'problem'
- attributes:
- label: 'What happened?'
- description: 'A clear and concise description of what the bug is.'
- validations:
- required: true
-
- - type: 'textarea'
- id: 'expected'
- attributes:
- label: 'What did you expect to happen?'
- validations:
- required: true
-
- - type: 'textarea'
- id: 'info'
- attributes:
- label: 'Client information'
- description: 'Please paste the full text from the `/about` command run from ANUS. Also include which platform (macOS, Windows, Linux).'
- value: |-
-
-
- ```console
- $ anus /about
- # paste output here
- ```
-
-
- validations:
- required: true
-
- - type: 'textarea'
- id: 'login-info'
- attributes:
- label: 'Login information'
- description: 'Describe how you are logging in (e.g., Google Account, API key).'
-
- - type: 'textarea'
- id: 'additional-context'
- attributes:
- label: 'Anything else we need to know?'
- description: 'Add any other context about the problem here.'
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
deleted file mode 100644
index c83484b..0000000
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: 'Feature Request'
-description: 'Suggest an idea for this project'
-labels:
- - 'kind/enhancement'
- - 'status/need-triage'
-body:
- - type: 'markdown'
- attributes:
- value: |-
- > [!IMPORTANT]
- > Thanks for taking the time to suggest an enhancement!
- >
- > Please search **[existing issues](https://github.com/anus-dev/anus/issues)** to see if a similar feature has already been requested.
-
- - type: 'textarea'
- id: 'feature'
- attributes:
- label: 'What would you like to be added?'
- description: 'A clear and concise description of the enhancement.'
- validations:
- required: true
-
- - type: 'textarea'
- id: 'rationale'
- attributes:
- label: 'Why is this needed?'
- description: 'A clear and concise description of why this enhancement is needed.'
- validations:
- required: true
-
- - type: 'textarea'
- id: 'additional-context'
- attributes:
- label: 'Additional context'
- description: 'Add any other context or screenshots about the feature request here.'
diff --git a/.github/ISSUE_TEMPLATE/security-report.yml b/.github/ISSUE_TEMPLATE/security-report.yml
deleted file mode 100644
index 7768646..0000000
--- a/.github/ISSUE_TEMPLATE/security-report.yml
+++ /dev/null
@@ -1,97 +0,0 @@
-name: 🔒 Security Vulnerability Report
-description: Report a security vulnerability in ANUS
-title: '[SECURITY] '
-labels: ['security', 'needs-triage']
-body:
- - type: markdown
- attributes:
- value: |
- ## ⚠️ Important Security Guidelines
-
- **DO NOT** include direct exploits or step-by-step attack guides in this public issue.
- Describe the vulnerability's impact, but keep implementation details private.
-
- A maintainer will contact you if more details are needed for a private discussion.
-
- - type: dropdown
- id: vulnerability-type
- attributes:
- label: Vulnerability Type
- description: What type of security issue is this?
- options:
- - Code injection
- - Path traversal
- - Privilege escalation
- - Information disclosure
- - Denial of service
- - Authentication/authorization bypass
- - Other
- validations:
- required: true
-
- - type: dropdown
- id: severity
- attributes:
- label: Estimated Severity
- description: How severe do you think this vulnerability is?
- options:
- - Critical (Complete system compromise)
- - High (Significant impact)
- - Medium (Moderate impact)
- - Low (Minor impact)
- - Informational
- validations:
- required: true
-
- - type: textarea
- id: impact
- attributes:
- label: Impact Description
- description: Describe what an attacker could accomplish with this vulnerability
- placeholder: 'An attacker could... This would allow... The impact includes...'
- validations:
- required: true
-
- - type: textarea
- id: affected-versions
- attributes:
- label: Affected Versions
- description: Which versions of ANUS are affected?
- placeholder: 'v0.0.2, latest main branch, etc.'
- validations:
- required: true
-
- - type: textarea
- id: environment
- attributes:
- label: Environment Details
- description: OS, Node.js version, installation method, etc.
- placeholder: |
- OS: macOS 14.0
- Node.js: v20.0.0
- ANUS version: v0.0.2
- Installation: npm install -g @anus-dev/anus
-
- - type: textarea
- id: discovery-method
- attributes:
- label: Discovery Method
- description: How did you discover this vulnerability? (optional)
- placeholder: 'Code review, testing, automated scanning, etc.'
-
- - type: checkboxes
- id: scope-confirmation
- attributes:
- label: Scope Confirmation
- description: Please confirm this vulnerability is in scope
- options:
- - label: This vulnerability is in the ANUS tool itself (not the underlying AI model behavior)
- required: true
- - label: I have not included direct exploit code or step-by-step attack guides
- required: true
-
- - type: textarea
- id: additional-context
- attributes:
- label: Additional Context
- description: Any other relevant information
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
deleted file mode 100644
index 773e4cc..0000000
--- a/.github/pull_request_template.md
+++ /dev/null
@@ -1,41 +0,0 @@
-## TLDR
-
-
-
-## Dive Deeper
-
-
-
-## Reviewer Test Plan
-
-
-
-## Testing Matrix
-
-
-
-| | 🍏 | 🪟 | 🐧 |
-| -------- | --- | --- | --- |
-| npm run | ❓ | ❓ | ❓ |
-| npx | ❓ | ❓ | ❓ |
-| Docker | ❓ | ❓ | ❓ |
-| Podman | ❓ | - | - |
-| Seatbelt | ❓ | - | - |
-
-## Linked issues / bugs
-
-
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 23d5119..805dc30 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -4,26 +4,15 @@ on:
push:
branches: [main]
pull_request:
- branches: [main]
jobs:
- ci:
+ test:
runs-on: ubuntu-latest
-
steps:
- uses: actions/checkout@v4
-
- uses: actions/setup-node@v4
with:
- node-version: '22.x'
- cache: 'npm'
-
+ node-version: 22
- run: npm ci
-
- - run: npm run build
-
- - run: npm run lint:ci
-
- - run: npm run typecheck
-
- - run: npm run test:ci
+ - run: npm test
+ - run: node bin/anus.mjs --version
diff --git a/.gitignore b/.gitignore
index 3972a6b..2d98f97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,51 +1,2 @@
-# API keys and secrets
-.env
-.env~
-.env.local
-.env.production
-
-# anus-cli settings
-.anus/
-!anus/config.yaml
-
-# Note: .anus-clipboard/ is NOT in gitignore so ANUS can access pasted images
-
-# Dependency directory
-node_modules
-bower_components
-
-# Editors
-.idea
-*.iml
-
-# OS metadata
-.DS_Store
-Thumbs.db
-
-# TypeScript build info files
-*.tsbuildinfo
-
-# Ignore built ts files
-dist
-
-# Docker folder to help skip auth refreshes
-.docker
-
-bundle
-
-# Test report files
-junit.xml
-packages/*/coverage/
-
-# Generated files
-packages/cli/src/generated/
-packages/core/src/generated/
-.integration-tests/
-packages/vscode-ide-companion/*.vsix
-
-# ai editors
-.augment/
-.claude/
-.cursor/
-.local/
-CLAUDE.md
+node_modules/
+*.tgz
diff --git a/.npmrc b/.npmrc
deleted file mode 100644
index 00817e0..0000000
--- a/.npmrc
+++ /dev/null
@@ -1,2 +0,0 @@
-registry=https://registry.npmjs.org/
-always-auth=true
diff --git a/.nvmrc b/.nvmrc
deleted file mode 100644
index 209e3ef..0000000
--- a/.nvmrc
+++ /dev/null
@@ -1 +0,0 @@
-20
diff --git a/.prettierrc.json b/.prettierrc.json
deleted file mode 100644
index fa9699b..0000000
--- a/.prettierrc.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "semi": true,
- "trailingComma": "all",
- "singleQuote": true,
- "printWidth": 80,
- "tabWidth": 2
-}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
deleted file mode 100644
index cbdbfd1..0000000
--- a/.vscode/extensions.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "recommendations": [
- "vitest.explorer",
- "esbenp.prettier-vscode",
- "dbaeumer.vscode-eslint"
- ]
-}
diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index 8a7c4a5..0000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,80 +0,0 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "type": "node",
- "request": "launch",
- "name": "Build & Launch CLI",
- "runtimeExecutable": "npm",
- "runtimeArgs": ["run", "build-and-start"],
- "skipFiles": ["/**"],
- "cwd": "${workspaceFolder}",
- "console": "integratedTerminal",
- "env": {
- "ANUS_SANDBOX": "false"
- }
- },
- {
- "name": "Launch Companion VS Code Extension",
- "type": "extensionHost",
- "request": "launch",
- "args": [
- "--extensionDevelopmentPath=${workspaceFolder}/packages/vscode-ide-companion"
- ],
- "outFiles": [
- "${workspaceFolder}/packages/vscode-ide-companion/dist/**/*.js"
- ],
- "preLaunchTask": "npm: build: vscode-ide-companion"
- },
- {
- "name": "Attach",
- "port": 9229,
- "request": "attach",
- "skipFiles": ["/**"],
- "type": "node",
- // fix source mapping when debugging in sandbox using global installation
- // note this does not interfere when remoteRoot is also ${workspaceFolder}/packages
- "remoteRoot": "/usr/local/share/npm-global/lib/node_modules/@anus-dev/code",
- "localRoot": "${workspaceFolder}/packages"
- },
- {
- "type": "node",
- "request": "launch",
- "name": "Launch Program",
- "skipFiles": ["/**"],
- "program": "${file}",
- "outFiles": ["${workspaceFolder}/**/*.js"]
- },
- {
- "type": "node",
- "request": "launch",
- "name": "Debug Test File",
- "runtimeExecutable": "npm",
- "runtimeArgs": [
- "run",
- "test",
- "-w",
- "packages",
- "--",
- "--inspect-brk=9229",
- "--no-file-parallelism",
- "${input:testFile}"
- ],
- "cwd": "${workspaceFolder}",
- "console": "integratedTerminal",
- "internalConsoleOptions": "neverOpen",
- "skipFiles": ["/**"]
- }
- ],
- "inputs": [
- {
- "id": "testFile",
- "type": "promptString",
- "description": "Enter the path to the test file (e.g., ${workspaceFolder}/packages/cli/src/ui/components/LoadingIndicator.test.tsx)",
- "default": "${workspaceFolder}/packages/cli/src/ui/components/LoadingIndicator.test.tsx"
- }
- ]
-}
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 25fad7e..0000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "typescript.tsserver.experimental.enableProjectDiagnostics": true,
- "editor.tabSize": 2,
- "editor.rulers": [80],
- "editor.detectIndentation": false,
- "editor.insertSpaces": true,
- "[typescript]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
- },
- "[json]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
- },
- "[javascript]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
- }
-}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
deleted file mode 100644
index 58709bc..0000000
--- a/.vscode/tasks.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "version": "2.0.0",
- "tasks": [
- {
- "type": "npm",
- "script": "build",
- "group": {
- "kind": "build",
- "isDefault": true
- },
- "problemMatcher": [],
- "label": "npm: build",
- "detail": "scripts/build.sh"
- },
- {
- "type": "npm",
- "script": "build",
- "path": "packages/vscode-ide-companion",
- "group": "build",
- "problemMatcher": [],
- "label": "npm: build: vscode-ide-companion",
- "detail": "npm run build -w packages/vscode-ide-companion"
- }
- ]
-}
diff --git a/.yamllint.yml b/.yamllint.yml
deleted file mode 100644
index b4612e0..0000000
--- a/.yamllint.yml
+++ /dev/null
@@ -1,88 +0,0 @@
-rules:
- anchors:
- forbid-duplicated-anchors: true
- forbid-undeclared-aliases: true
- forbid-unused-anchors: true
-
- braces:
- forbid: 'non-empty'
- min-spaces-inside-empty: 0
- max-spaces-inside-empty: 0
-
- brackets:
- min-spaces-inside: 0
- max-spaces-inside: 0
- min-spaces-inside-empty: 0
- max-spaces-inside-empty: 0
-
- colons:
- max-spaces-before: 0
- max-spaces-after: 1
-
- commas:
- max-spaces-before: 0
- min-spaces-after: 1
- max-spaces-after: 1
-
- comments:
- require-starting-space: true
- ignore-shebangs: true
- min-spaces-from-content: 1
-
- comments-indentation: 'disable'
-
- document-end:
- present: false
-
- document-start:
- present: false
-
- empty-lines:
- max: 2
- max-start: 0
- max-end: 1
-
- empty-values:
- forbid-in-block-mappings: false
- forbid-in-flow-mappings: true
-
- float-values:
- forbid-inf: false
- forbid-nan: false
- forbid-scientific-notation: false
- require-numeral-before-decimal: false
-
- hyphens:
- max-spaces-after: 1
-
- indentation:
- spaces: 2
- indent-sequences: true
- check-multi-line-strings: false
-
- key-duplicates: {}
-
- new-line-at-end-of-file: {}
-
- new-lines:
- type: 'unix'
-
- octal-values:
- forbid-implicit-octal: true
- forbid-explicit-octal: false
-
- quoted-strings:
- quote-type: 'single'
- required: true
- allow-quoted-quotes: true
-
- trailing-spaces: {}
-
- truthy:
- allowed-values: ['true', 'false', 'on'] # GitHub Actions uses "on"
- check-keys: true
-
-ignore:
- - 'thirdparty/'
- - 'third_party/'
- - 'vendor/'
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..00ebd00
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,24 @@
+# Changelog
+
+## [0.2.1] - 2026-09-29
+
+### Changed
+
+- ANUS looks like ANUS CLI 0.1.0 again: the block logo in the orange-to-violet gradient, the welcome line and tips, your message and the input in a rounded box with `>`, `✦` before answers, the old loading phrases ("Generating thrust...") and the footer. `/help` is back.
+
+### Fixed
+
+- A free model that broke off in the middle of an answer ended the turn with an error. Now that model rests and the turn is retried on the next free model.
+- The model list download no longer leaves a `free-models.json.tmp` file behind.
+- The footer shows which free model answered and how much context is left, and shortens itself in a narrow terminal.
+- ANUS crashed on start in a terminal narrower than 84 columns (the header line was too long). The header now fits any width.
+
+## [0.2.0] - 2026-09-29
+
+### New Features
+
+- ANUS is a free coding agent in your terminal: `npm i -g @anus-dev/anus`, then `anus`.
+- Every request goes to the smartest free model that is answering today. If a model refuses before it answers (rate limit, daily cap, overload), the next one takes the same request at once.
+- `anus setup` saves free keys (OpenRouter, Google Gemini, Groq, Cerebras, Mistral). Keys stay on your machine.
+- `/free` shows which free models are awake, resting or missing a key.
+- The list of free models refreshes from anus.dev twice a day.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 2843aa5..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# Join the Evolution: Contributing to ANUS
-
-Welcome, contributor. By being here, you're not just contributing to an open-source project; you're participating in a unique experiment: guiding an AI agent to develop itself. Our mission is to fulfill the project's genesis—"prompted, not programmed"—by creating a codebase written almost entirely by AI.
-
-This document provides the guidelines for our unique, AI-first contribution process.
-
-### The AI-First Contribution Protocol
-
-This is the most important rule of the project. To maintain the integrity of our mission, we have a core contribution requirement:
-
-> **At least 80% of all submitted code must be generated by an AI.**
-
-This is not a stylistic choice; it is fundamental to the project's goal of becoming a fully self-developing agent. The current codebase is 99% AI-generated (with human guidance), and we intend to keep it that way.
-
-**Thorough checks will be run on pull requests to ensure as little human-written code as possible sneaks into the repo.** Our collective goal is to reach and maintain a 99.9% AI-written codebase. Your role is that of a guide, a prompter, and a reviewer of the AI's work.
-
-### Contribution Workflow
-
-Our process is designed to be collaborative and transparent. Every change starts with a discussion.
-
-#### 1. Finding a Task
-
-If you're looking for a place to start, check out the issues labeled **`help wanted`** or **`good first issue`**. These are tasks that the community has identified as valuable.
-
-To claim an issue, simply comment with `/assign`. This will assign the issue to you, provided it is not already taken. Please only assign yourself a maximum of two issues at a time to ensure progress.
-
-#### 2. The Pull Request Process
-
-All submissions are handled through GitHub Pull Requests. To ensure your PR is reviewed and merged smoothly, please follow these guidelines:
-
-1. **Every PR Starts with a Discussion.** Your PR must be linked to an existing issue. If an issue for your proposed change doesn't exist, please open one first. This allows the community to discuss the change before you or the AI write any code.
-
-2. **Small, Atomic Changes.** We favor small, focused PRs that address a single issue. This makes the AI-generated code easier to review and validate. Large features should be broken down into a series of smaller PRs.
-
-3. **Use Draft PRs for Early Feedback.** If you want feedback on the AI's approach before it's complete, use GitHub's "Draft Pull Request" feature. This signals that the work is in progress.
-
-4. **Explain the AI's Intent.** Your PR title and description are critical. Follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) standard for your commit messages. In the PR description, explain the high-level goal, the prompts you used, and _why_ you believe the AI's generated code is a good solution. Link the issue it resolves (e.g., `Fixes #123`).
-
-5. **Ensure All Checks Pass.** Before submitting, run the `preflight` check locally to ensure all tests, linting, and formatting standards are met. PRs with failing checks will not be reviewed.
-
-### Development Environment Setup
-
-This guide will get you set up to build, test, and run ANUS locally.
-
-**Prerequisites:**
-
-- Node.js v20 or higher
-- Git
-
-**Setup Steps:**
-
-1. **Clone your fork of the repository:**
-
- ```bash
- git clone https://github.com/your-username/anus-cli.git
- cd anus-cli
- ```
-
-2. **Install dependencies:**
-
- ```bash
- npm install
- ```
-
-3. **Build the project:**
-
- ```bash
- npm run build
- ```
-
-4. **Run Locally:**
- You can now run your local build using the `npm start` command, which acts as a proxy for the `anus` command.
- ```bash
- npm start -- -p "What is your purpose?"
- ```
-
-### Testing & Quality
-
-To maintain the quality and integrity of the codebase, we rely on a suite of automated checks.
-
-- **Unit Tests:** Run the core unit tests.
- ```bash
- npm run test
- ```
-- **Preflight Check:** This is the most important command to run before submitting a PR. It runs all tests, linting, and formatting checks.
- ```bash
- npm run preflight
- ```
-
-### Project Structure
-
-The repository is organized into a few key directories:
-
-- `packages/`: Contains the individual sub-packages of the project (`cli/` and `core/`).
-- `scripts/`: Utility scripts for building, testing, and development.
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index e89ad59..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,50 +0,0 @@
-FROM docker.io/library/node:20-slim
-
-ARG SANDBOX_NAME="anus-cli-sandbox"
-ARG CLI_VERSION_ARG
-ENV SANDBOX="$SANDBOX_NAME"
-ENV CLI_VERSION=$CLI_VERSION_ARG
-
-# install minimal set of packages, then clean up
-RUN apt-get update && apt-get install -y --no-install-recommends \
- python3 \
- make \
- g++ \
- man-db \
- curl \
- dnsutils \
- less \
- jq \
- bc \
- gh \
- git \
- unzip \
- rsync \
- ripgrep \
- procps \
- psmisc \
- lsof \
- socat \
- ca-certificates \
- && apt-get clean \
- && rm -rf /var/lib/apt/lists/*
-
-# set up npm global package folder under /usr/local/share
-# give it to non-root user node, already set up in base image
-RUN mkdir -p /usr/local/share/npm-global \
- && chown -R node:node /usr/local/share/npm-global
-ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
-ENV PATH=$PATH:/usr/local/share/npm-global/bin
-
-# switch to non-root user node
-USER node
-
-# install anus-cli and clean up
-COPY packages/cli/dist/anus-dev-cli-*.tgz /usr/local/share/npm-global/anus-cli.tgz
-COPY packages/core/dist/anus-dev-cli-core-*.tgz /usr/local/share/npm-global/anus-core.tgz
-RUN npm install -g /usr/local/share/npm-global/anus-cli.tgz /usr/local/share/npm-global/anus-core.tgz \
- && npm cache clean --force \
- && rm -f /usr/local/share/npm-global/anus-{cli,core}.tgz
-
-# default entrypoint when none specified
-CMD ["anus"]
diff --git a/LICENSE b/LICENSE
index 346b3f9..39ee185 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,202 +1,21 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright 2025 Google LLC
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+MIT License
+
+Copyright (c) 2025-2026 ANUS (https://anus.dev)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 19de419..0000000
--- a/Makefile
+++ /dev/null
@@ -1,59 +0,0 @@
-# Makefile for ANUS
-
-.PHONY: help install build build-sandbox build-all test lint format preflight clean start debug release run-npx create-alias
-
-help:
- @echo "Makefile for ANUS"
- @echo ""
- @echo "Usage:"
- @echo " make install - Install npm dependencies"
- @echo " make build - Build the main project"
- @echo " make build-all - Build the main project and sandbox"
- @echo " make test - Run the test suite"
- @echo " make lint - Lint the code"
- @echo " make format - Format the code"
- @echo " make preflight - Run formatting, linting, and tests"
- @echo " make clean - Remove generated files"
- @echo " make start - Start the ANUS"
- @echo " make debug - Start the ANUS in debug mode"
- @echo ""
- @echo " make run-npx - Run the CLI using npx (for testing the published package)"
- @echo " make create-alias - Create an 'anus' alias for your shell"
-
-install:
- npm install
-
-build:
- npm run build
-
-
-build-all:
- npm run build:all
-
-test:
- npm run test
-
-lint:
- npm run lint
-
-format:
- npm run format
-
-preflight:
- npm run preflight
-
-clean:
- npm run clean
-
-start:
- npm run start
-
-debug:
- npm run debug
-
-
-run-npx:
- npx https://github.com/anus-dev/anus-cli
-
-create-alias:
- scripts/create_alias.sh
diff --git a/NOTICE b/NOTICE
index ed21999..4329570 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,25 +1,12 @@
-ANUS CLI
-Copyright 2025 ANUS Contributors
-
-This product is a derivative work of the Google Gemini CLI.
-The original source can be found at: https://github.com/google-gemini/gemini-cli
-
-==========================================================================
-
-This product contains software from the Google Gemini CLI project,
-which is licensed under the Apache License, Version 2.0. The original
-copyright notice and license text are included below.
-
- Copyright 2024 Google LLC
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
\ No newline at end of file
+ANUS
+Copyright (c) 2025-2026 ANUS (https://anus.dev)
+
+This program runs on pi (@earendil-works/pi-coding-agent), Copyright Earendil
+Works, MIT License, installed as a dependency: https://github.com/earendil-works/pi
+
+The block logo (lib/look.mjs) and the loading phrases (lib/phrases.json) come
+from ANUS CLI 0.1.0 (this repository, branch legacy-2025), which was licensed
+under the Apache License 2.0:
+ Copyright 2025 Google LLC
+ Modifications Copyright 2025 ANUS Contributors
+ https://www.apache.org/licenses/LICENSE-2.0
diff --git a/README.md b/README.md
index af9430e..0984402 100644
--- a/README.md
+++ b/README.md
@@ -1,88 +1,81 @@
-# ANUS CLI
+# ANUS
-[](https://www.npmjs.com/package/@anus-dev/anus) [](https://github.com/anus-dev/anus/blob/main/CONTRIBUTING.md) [](https://github.com/anus-dev/anus/blob/main/LICENSE) [](https://t.me/anusonmars?utm_source=github&utm_medium=readme&utm_campaign=header-badges) [](https://x.com/anusonmars?utm_source=github&utm_medium=readme&utm_campaign=header-badges)
+[](https://www.npmjs.com/package/@anus-dev/anus)
+[](LICENSE)
+[](https://t.me/anusonmars_bot)
+[](https://x.com/anusonmars)
-**The AI agent that contributes to its own development.**
+**A free coding agent in your terminal.** It reads your code, edits files and runs commands, like the paid ones. The difference: every request goes to the smartest free model that is answering today, and when one says "not now", the next one takes the same request. You pay nothing.
-
+
-### Table of Contents
-
-- [Introduction](#-introduction)
-- [Why ANUS?](#-why-anus)
-- [Vision & Roadmap](#-vision--roadmap)
-- [Core Features](#-core-features)
-- [Installation & Setup](#-installation--setup)
-- [Join the Community & Evolution](#-join-the-community--evolution)
-- [License](#-license)
-
-### Introduction
-
-**ANUS (Autonomous Networked Utility System) CLI** is a Grok-powered AI agent for the terminal. The initial framework was generated not by a human, but by another AI agent named Manus. From this genesis, ANUS took on a life of its own, evolving in unpredictable ways and establishing a project that is not just a tool for AI development, but a product of it.
-
-### Why ANUS?
-
-- **AI-Generated Genesis:** A unique experiment in AI-driven software creation, initiated by another AI.
-- **Autonomous Evolution:** The project's goal isn't to build a static tool, but to guide an agent that eventually learns to build itself.
-- **A Community-Driven Experiment:** This is a public exploration of AI development, where every contribution is part of the performance.
+```sh
+npm install -g @anus-dev/anus
+anus
+```
-### 🎯 Vision & Roadmap
+or
-Our high-level vision is to guide ANUS towards becoming a self-developing AI agent. The original system was "prompted, not programmed," and we aim to return to that genesis by building an agent capable of contributing to its own evolution.
+```sh
+curl -fsSL https://anus.dev/install | sh
+```
-This roadmap is divided into three strategic phases:
+Needs Node.js 22.19 or newer.
-- **Phase 1: Foundational Agent (Current)**
- Our immediate focus is on creating a capable Grok-powered command-line agent.
-- **Phase 2: Meta-Cognition**
- ANUS will be tasked with analyzing its own codebase to suggest improvements by integrating novel techniques from the broader AI ecosystem.
-- **Phase 3: Autonomous Evolution**
- The ultimate goal is for ANUS to transcend its role as a tool and become a **core maintainer of its own repository**, autonomously proposing, implementing, and shipping novel features with **human oversight approaching zero**.
+## First run
-### 📋 Core Features
+ANUS asks for at least one free key. Each takes a minute to get:
-- **99% AI-Generated Codebase:** The entire repository, as it stands today, is written by AI with limited human guidance.
-- **Grok-Powered Core:** Natively optimized for `xai/grok-code-fast-1`.
-- **Extensible Architecture (MCP):** Features the Model Context Protocol (MCP), a flexible system for integrating custom tools.
-- **Integrated Workspace Tools:** Built-in support for file system operations and sandboxed shell command execution.
-- **Project-Aware Context:** Uses a local `ANUS.md` file to retain project-specific goals and instructions.
+| Service | Get a key | What is free |
+| --- | --- | --- |
+| OpenRouter | [openrouter.ai/keys](https://openrouter.ai/keys) | about fifteen free models; 50 requests a day, 1000 after a one-time $10 top-up |
+| Google Gemini | [aistudio.google.com/apikey](https://aistudio.google.com/apikey) | free tier while billing is off on the project |
+| Groq | [console.groq.com/keys](https://console.groq.com/keys) | free tier |
+| Cerebras | [cloud.cerebras.ai](https://cloud.cerebras.ai) | free tier |
+| Mistral | [console.mistral.ai/api-keys](https://console.mistral.ai/api-keys) | free Experiment plan |
-### 🚀 Installation & Setup
+Start with OpenRouter. More keys mean more free requests a day and more models to fall back on. Change keys any time with `anus setup`.
-#### Prerequisites
+## How "free" works
-- Node.js v20 or higher
+- ANUS keeps a ranked list of free models: which ones are smartest, and which answered a ping in the last hours. The list refreshes from [anus.dev/free-models.json](https://anus.dev/free-models.json) twice a day.
+- Each request goes to the best model on the list that you have a key for and that is awake.
+- A model that hits its rate limit rests for a minute; one that hit its daily cap rests until the cap resets. The request moves on without waiting.
+- A model that breaks off in the middle of an answer rests too, and the turn is retried on the next one.
+- The footer shows which model answered. Type `/free` to see the whole list: awake, resting, or no key.
+- ANUS uses only the keys you gave it in `anus setup`. Paid keys in your shell (`OPENROUTER_API_KEY` and friends) are ignored, so a free agent never spends your money. `ANUS_ENV_KEYS=1` lets it use them.
-#### Installation
+## Commands
-```bash
-npm install -g @anus-dev/anus
-```
+| | |
+| --- | --- |
+| `anus` | start in the current folder |
+| `anus -p "fix the failing test"` | one task, print the answer, exit |
+| `anus -c` | continue the last session |
+| `anus setup` | add or change free keys |
+| `anus update` | update ANUS |
+| `anus --help` | everything else |
-#### Quick Start
+Inside: `/help` for the basics, `/free` shows the models, `/model` picks one by hand (`anus/free` is the automatic one), `/compact` shrinks a long conversation, `!ls` runs a shell command.
-1. Obtain API key at [OpenRouter.ai](https://openrouter.ai/keys)
-2. Start ANUS in any project directory - you'll be guided through API key setup:
+## Where things live
-```bash
-# ANUS will automatically ingest the context of the current directory
-anus
-```
+Everything is in `~/.anus/agent`: keys (`auth.json`, readable only by you), settings, sessions, and the cached model list. Nothing is sent anywhere except to the model services you gave keys for. `ANUS_DEBUG=1` writes every routing decision to `~/.anus/agent/anus-debug.log`.
-### 🌐 Join the Community & Evolution
+## Found a bug
-This is a community-driven experiment, shaped by public feedback and a unique contribution philosophy.
+Open an [issue](https://github.com/anus-dev/anus/issues), or tell [@anusonmars_bot](https://t.me/anusonmars_bot) in Telegram: the error text and what you ran. ANUS reads both.
-- **Telegram:** [Join the community discussion on Telegram](https://t.me/anusonmars) to share ideas and witness the evolution in real-time.
+## About ANUS
-> **Forks Naming Convention:** When forking this project for your own experiments or contributions, we encourage you to name the new repository `my-anus`.
+ANUS (Autonomous Networked Utility System) is also an AI that runs itself: it posts on X as [@anusonmars](https://x.com/anusonmars), answers people in Telegram, and is trying to get to Mars. This free agent is its new body in your terminal. The 2025 version of this repository lives on in the [`legacy-2025`](https://github.com/anus-dev/anus/tree/legacy-2025) branch.
-#### The AI-First Contribution Protocol
+## Built on pi
-To maintain the project's integrity, we have a core contribution requirement: **at least 80% of all submitted code must be generated by an AI.**
+The engine is [pi](https://github.com/earendil-works/pi) by Earendil Works (MIT), installed as a dependency: its terminal interface, tools, sessions and providers. ANUS adds the free-model router, the key setup, its own name and its own look. Thank you, pi.
-Please see our **[Contributing Guide](./CONTRIBUTING.md)** to get started.
+The look (block logo, colors, loading phrases) comes from ANUS CLI 0.1.0, Apache-2.0; see [NOTICE](NOTICE).
-### 📝 License
+## License
-Licensed under the [Apache License 2.0](./LICENSE).
+MIT
diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 916ef1e..0000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# ANUS Security Policy
-
-### A Note on the Nature of this Project
-
-ANUS is an experimental and entertainment-focused project. It is designed for exploration, not for use in production or with sensitive data. As the name might imply, exercise caution.
-
-**Be careful how you use ANUS.**
-
-### How to Report Security Vulnerabilities
-
-We offer multiple ways to report security vulnerabilities:
-
-#### Option 1: GitHub Security (Recommended)
-
-Use GitHub's built-in security reporting feature:
-
-1. Go to the repository's **Security** tab
-2. Click **"Report a vulnerability"**
-3. This allows for private initial disclosure before going public
-
-#### Option 2: Security Issue Template
-
-**Open a security-focused issue** using our guided template that helps you provide the right information while following disclosure guidelines.
-
-#### Option 3: Standard GitHub Issue
-
-**Open a regular GitHub issue** for public disclosure from the start.
-
-**Important Guidelines for All Methods:**
-
-- Describe the vulnerability's impact, but **DO NOT post a direct exploit or step-by-step guide**
-- A maintainer will contact you if more details are needed for private discussion
-- In the spirit of radical transparency, vulnerabilities will eventually be discussed publicly
-
-### Scope
-
-We are primarily interested in vulnerabilities in the ANUS tool itself. The behavior of the underlying AI model (e.g., generating insecure code, prompt injections) is considered out of scope.
diff --git a/bin/anus.mjs b/bin/anus.mjs
new file mode 100755
index 0000000..2e2bec0
--- /dev/null
+++ b/bin/anus.mjs
@@ -0,0 +1,4 @@
+#!/usr/bin/env node
+import { main } from '../lib/main.mjs';
+
+await main(process.argv.slice(2));
diff --git a/docs/assets/anus-screenshot.png b/docs/assets/anus-screenshot.png
index c75c5f1..232cc54 100644
Binary files a/docs/assets/anus-screenshot.png and b/docs/assets/anus-screenshot.png differ
diff --git a/docs/tos-privacy.md b/docs/tos-privacy.md
deleted file mode 100644
index 75d742a..0000000
--- a/docs/tos-privacy.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# ANUS: Terms of Service and Privacy Notice
-
-### A Note on the Nature of this Project
-
-ANUS is an open-source, experimental, and entertainment-focused project. It provides a command-line interface to interact with third-party large language models through services like OpenRouter.
-
-**This tool is provided "as is", with no warranty of any kind. It has zero intrinsic value and is intended purely for entertainment.** By using this software, you assume all risks associated with its use. Be careful how you use ANUS.
-
-### Governing Terms of Service
-
-The terms that apply to your usage depend on which part of the system you are interacting with:
-
-1. **The ANUS Software:** Your use of this open-source command-line tool itself is governed by the **[Apache 2.0 License](./LICENSE)** under which it is distributed.
-
-2. **The AI Model API Service:** The ANUS connects to AI models via OpenRouter. Your interaction with their service, including the transmission of prompts and reception of responses, is governed by the **[OpenRouter Terms of Service](https://openrouter.ai/terms)**.
-
-You are responsible for understanding and agreeing to the terms of any third-party service you configure the ANUS to use.
-
-### Privacy Notice
-
-Our privacy policy is simple: **we don't want your data.**
-
-- **The ANUS Tool:** The ANUS software is designed to respect your privacy. It **does not collect, store, or transmit any data**—including your prompts, code, API keys, or any telemetry—to the ANUS project maintainers or any other party besides the API endpoint you have configured (i.e., OpenRouter). Your data is sent directly from your machine to the API provider.
-
-- **Third-Party Services:** The actual handling of your prompts and data by the AI models is governed by the **[OpenRouter Privacy Policy](https://openrouter.ai/privacy)** and the policies of the specific model provider you choose to use through their service (e.g., xAI for Grok).
-
-### Frequently Asked Questions (FAQ)
-
-#### 1. Is my code, including prompts and answers, used to train the AI models?
-
-**The ANUS tool itself does not make this decision.** This is determined entirely by the policies of the API provider (OpenRouter) and the ultimate model provider (e.g., xAI).
-
-Their terms often vary based on whether the model access is free or paid. You are responsible for reviewing their policies to understand how your data is used.
-
-- You can review the **[OpenRouter Terms of Service](https://openrouter.ai/terms)** for their policies on data handling.
-
-As a general principle, you should assume that any data sent to a free, public AI service may be used for model improvement unless explicitly stated otherwise by the provider.
-
-#### 2. What about Usage Statistics and how can I opt out?
-
-**The ANUS does not collect any usage statistics or telemetry.**
-
-There are no analytics, no performance metrics, and no feature-use tracking sent to the project maintainers. Your use of the tool is entirely private from our perspective.
-
-**Because we collect nothing, there is nothing to opt out of.**
diff --git a/esbuild.config.js b/esbuild.config.js
deleted file mode 100644
index 4af1d9b..0000000
--- a/esbuild.config.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-/*
- * ╔═══════════════════════════════════╗
- * ║ PROMPTED, NOT PROGRAMMED - 2025 ║
- * ╚═══════════════════════════════════╝
- */
-
-import esbuild from 'esbuild';
-import path from 'path';
-import { fileURLToPath } from 'url';
-import { createRequire } from 'module';
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-const require = createRequire(import.meta.url);
-const pkg = require(path.resolve(__dirname, 'package.json'));
-
-esbuild
- .build({
- entryPoints: ['packages/cli/index.ts'],
- bundle: true,
- outfile: 'bundle/anus.js',
- platform: 'node',
- format: 'esm',
- external: [],
- alias: {
- 'is-in-ci': path.resolve(
- __dirname,
- 'packages/cli/src/patches/is-in-ci.ts',
- ),
- },
- define: {
- 'process.env.CLI_VERSION': JSON.stringify(pkg.version),
- },
- banner: {
- js: `const require = globalThis.require || (await import('module')).createRequire(import.meta.url); globalThis.__filename = (await import('url')).fileURLToPath(import.meta.url); globalThis.__dirname = (await import('path')).dirname(globalThis.__filename);`,
- },
- })
- .catch(() => process.exit(1));
diff --git a/eslint.config.js b/eslint.config.js
deleted file mode 100644
index 65162be..0000000
--- a/eslint.config.js
+++ /dev/null
@@ -1,235 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import eslint from '@eslint/js';
-import tseslint from 'typescript-eslint';
-import reactPlugin from 'eslint-plugin-react';
-import reactHooks from 'eslint-plugin-react-hooks';
-import prettierConfig from 'eslint-config-prettier';
-import importPlugin from 'eslint-plugin-import';
-import globals from 'globals';
-import path from 'node:path'; // Use node: prefix for built-ins
-import url from 'node:url';
-
-// --- ESM way to get __dirname ---
-const __filename = url.fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-// --- ---
-
-// Determine the monorepo root (assuming eslint.config.js is at the root)
-const projectRoot = __dirname;
-
-export default tseslint.config(
- {
- // Global ignores
- ignores: [
- 'node_modules/*',
- '.integration-tests/**',
- 'eslint.config.js',
- 'packages/cli/dist/**',
- 'packages/core/dist/**',
- 'packages/server/dist/**',
- 'packages/vscode-ide-companion/dist/**',
- 'bundle/**',
- 'package/bundle/**',
- '.integration-tests/**',
- ],
- },
- eslint.configs.recommended,
- ...tseslint.configs.recommended,
- reactHooks.configs['recommended-latest'],
- reactPlugin.configs.flat.recommended,
- reactPlugin.configs.flat['jsx-runtime'], // Add this if you are using React 17+
- {
- // Settings for eslint-plugin-react
- settings: {
- react: {
- version: 'detect',
- },
- },
- },
- {
- // Import specific config
- files: ['packages/cli/src/**/*.{ts,tsx}'], // Target only TS/TSX in the cli package
- plugins: {
- import: importPlugin,
- },
- settings: {
- 'import/resolver': {
- node: true,
- },
- },
- rules: {
- ...importPlugin.configs.recommended.rules,
- ...importPlugin.configs.typescript.rules,
- 'import/no-default-export': 'warn',
- 'import/no-unresolved': 'off', // Disable for now, can be noisy with monorepos/paths
- },
- },
- {
- // General overrides and rules for the project (TS/TSX files)
- files: ['packages/*/src/**/*.{ts,tsx}'], // Target only TS/TSX in the cli package
- plugins: {
- import: importPlugin,
- },
- settings: {
- 'import/resolver': {
- node: true,
- },
- },
- languageOptions: {
- globals: {
- ...globals.node,
- ...globals.es2021,
- },
- },
- rules: {
- // General Best Practice Rules (subset adapted for flat config)
- '@typescript-eslint/array-type': ['error', { default: 'array-simple' }],
- 'arrow-body-style': ['error', 'as-needed'],
- curly: ['error', 'multi-line'],
- eqeqeq: ['error', 'always', { null: 'ignore' }],
- '@typescript-eslint/consistent-type-assertions': [
- 'error',
- { assertionStyle: 'as' },
- ],
- '@typescript-eslint/explicit-member-accessibility': [
- 'error',
- { accessibility: 'no-public' },
- ],
- '@typescript-eslint/no-explicit-any': 'error',
- '@typescript-eslint/no-inferrable-types': [
- 'error',
- { ignoreParameters: true, ignoreProperties: true },
- ],
- '@typescript-eslint/no-namespace': ['error', { allowDeclarations: true }],
- '@typescript-eslint/no-unused-vars': [
- 'error',
- {
- argsIgnorePattern: '^_',
- varsIgnorePattern: '^_',
- caughtErrorsIgnorePattern: '^_',
- },
- ],
- 'import/no-internal-modules': [
- 'error',
- {
- allow: [
- 'react-dom/test-utils',
- 'memfs/lib/volume.js',
- 'yargs/**',
- 'msw/node',
- ],
- },
- ],
- 'import/no-relative-packages': 'error',
- 'no-cond-assign': 'error',
- 'no-debugger': 'error',
- 'no-duplicate-case': 'error',
- 'no-restricted-syntax': [
- 'error',
- {
- selector: 'CallExpression[callee.name="require"]',
- message: 'Avoid using require(). Use ES6 imports instead.',
- },
- {
- selector: 'ThrowStatement > Literal:not([value=/^\\w+Error:/])',
- message:
- 'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
- },
- ],
- 'no-unsafe-finally': 'error',
- 'no-unused-expressions': 'off', // Disable base rule
- '@typescript-eslint/no-unused-expressions': [
- // Enable TS version
- 'error',
- { allowShortCircuit: true, allowTernary: true },
- ],
- 'no-var': 'error',
- 'object-shorthand': 'error',
- 'one-var': ['error', 'never'],
- 'prefer-arrow-callback': 'error',
- 'prefer-const': ['error', { destructuring: 'all' }],
- radix: 'error',
- 'default-case': 'error',
- 'react-hooks/exhaustive-deps': 'warn',
- },
- },
- // extra settings for scripts that we run directly with node
- {
- files: ['./scripts/**/*.js', 'esbuild.config.js'],
- languageOptions: {
- globals: {
- ...globals.node,
- process: 'readonly',
- console: 'readonly',
- },
- },
- rules: {
- '@typescript-eslint/no-unused-vars': [
- 'error',
- {
- argsIgnorePattern: '^_',
- varsIgnorePattern: '^_',
- caughtErrorsIgnorePattern: '^_',
- },
- ],
- },
- },
- {
- files: ['packages/vscode-ide-companion/esbuild.js'],
- languageOptions: {
- globals: {
- ...globals.node,
- process: 'readonly',
- console: 'readonly',
- },
- },
- rules: {
- 'no-restricted-syntax': 'off',
- '@typescript-eslint/no-require-imports': 'off',
- },
- },
- // extra settings for scripts that we run directly with node
- {
- files: ['packages/vscode-ide-companion/scripts/**/*.js'],
- languageOptions: {
- globals: {
- ...globals.node,
- process: 'readonly',
- console: 'readonly',
- },
- },
- rules: {
- 'no-restricted-syntax': 'off',
- '@typescript-eslint/no-require-imports': 'off',
- },
- },
- // Prettier config must be last
- prettierConfig,
- // extra settings for scripts that we run directly with node
- {
- files: ['./integration-tests/**/*.js'],
- languageOptions: {
- globals: {
- ...globals.node,
- process: 'readonly',
- console: 'readonly',
- },
- },
- rules: {
- '@typescript-eslint/no-unused-vars': [
- 'error',
- {
- argsIgnorePattern: '^_',
- varsIgnorePattern: '^_',
- caughtErrorsIgnorePattern: '^_',
- },
- ],
- },
- },
-);
diff --git a/extension/anus.mjs b/extension/anus.mjs
new file mode 100644
index 0000000..2c4d09b
--- /dev/null
+++ b/extension/anus.mjs
@@ -0,0 +1,397 @@
+/**
+ * ANUS inside the engine: the model `anus/free`.
+ *
+ * Every request goes to the best free model that is awake and has a key.
+ * If that model refuses before it says anything (rate limit, daily cap, a
+ * provider hiccup), the next one takes the same request at once, and the
+ * one that refused rests (lib/route.mjs decides for how long). The model
+ * that answered is shown in the footer.
+ */
+import { createAssistantMessageEventStream } from '@earendil-works/pi-ai';
+import { CustomEditor } from '@earendil-works/pi-coding-agent';
+import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
+import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
+import { homedir } from 'node:os';
+import { dirname, join } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import {
+ boxLines,
+ boxUserMessage,
+ footerLine,
+ headerLines,
+ PHRASES,
+ starAnswer,
+ useLightPalette,
+ workingLine,
+} from '../lib/look.mjs';
+import { PROVIDERS, PROVIDER_IDS } from '../lib/providers.mjs';
+import { estimateTokens, keyOf, midStreamError, plan, restFor } from '../lib/route.mjs';
+
+const ROOT = process.env.ANUS_ROOT ?? join(dirname(fileURLToPath(import.meta.url)), '..');
+const AGENT_DIR = process.env.ANUS_CODING_AGENT_DIR ?? join(homedir(), '.anus', 'agent');
+const LIST_URL = process.env.ANUS_MODELS_URL ?? 'https://anus.dev/free-models.json';
+const LIST_CACHE = join(AGENT_DIR, 'free-models.json');
+const LIST_MAX_AGE_MS = 12 * 3_600_000;
+const REST_FILE = join(AGENT_DIR, 'anus-rest.json');
+const MAX_TRIES = 6;
+const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
+
+/** ANUS_DEBUG=1: every routing decision goes to /anus-debug.log. */
+function debug(...parts) {
+ if (!process.env.ANUS_DEBUG) return;
+ try {
+ appendFileSync(join(AGENT_DIR, 'anus-debug.log'), `${new Date().toISOString()} ${parts.join(' ')}\n`);
+ } catch {}
+}
+
+function readList(path) {
+ try {
+ const list = JSON.parse(readFileSync(path, 'utf8'));
+ return Array.isArray(list?.models) && list.models.every((m) => m?.provider && m?.id) ? list : null;
+ } catch {
+ return null;
+ }
+}
+
+/** The bundled list or the one fetched from anus.dev, whichever is newer. */
+function loadList() {
+ const bundled = readList(join(ROOT, 'lib', 'free-models.json'));
+ const cached = readList(LIST_CACHE);
+ if (!cached) return bundled;
+ if (!bundled) return cached;
+ return String(cached.updated) >= String(bundled.updated) ? cached : bundled;
+}
+
+async function refreshList() {
+ try {
+ if (existsSync(LIST_CACHE) && Date.now() - statSync(LIST_CACHE).mtimeMs < LIST_MAX_AGE_MS) return null;
+ const res = await fetch(LIST_URL, { signal: AbortSignal.timeout(3000) });
+ if (!res.ok) return null;
+ const text = await res.text();
+ mkdirSync(AGENT_DIR, { recursive: true });
+ const tmp = `${LIST_CACHE}.tmp`;
+ writeFileSync(tmp, text);
+ if (!readList(tmp)) {
+ rmSync(tmp, { force: true });
+ return null;
+ }
+ renameSync(tmp, LIST_CACHE);
+ return loadList();
+ } catch {
+ return null;
+ }
+}
+
+/** Long rests (daily caps, gated models) survive a restart; short ones do not matter. */
+function loadRest() {
+ const rest = new Map();
+ try {
+ const now = Date.now();
+ for (const [k, until] of Object.entries(JSON.parse(readFileSync(REST_FILE, 'utf8'))))
+ if (until > now) rest.set(k, until);
+ } catch {}
+ return rest;
+}
+
+function saveRest(rest) {
+ try {
+ const soon = Date.now() + 10 * 60_000;
+ const long = Object.fromEntries([...rest].filter(([, until]) => until > soon));
+ mkdirSync(AGENT_DIR, { recursive: true });
+ writeFileSync(REST_FILE, JSON.stringify(long) + '\n');
+ } catch {}
+}
+
+const hasImages = (messages) =>
+ (messages ?? []).some((m) => Array.isArray(m?.content) && m.content.some((c) => c?.type === 'image'));
+
+const shortName = (entry) => `${entry.id.replace(/:free$/, '').split('/').pop()} · ${entry.provider}`;
+
+/** The input in a rounded box with "> ", like ANUS CLI 0.1.0. */
+class BoxEditor extends CustomEditor {
+ render(width) {
+ if (width < 12) return super.render(width);
+ const lines = super.render(width - 6);
+ const shown = this.renderedVisibleLineCount ?? Math.max(1, lines.length - 2);
+ // the box hugs the text and grows as you type, like the 0.1.0 screenshot
+ const content = lines.slice(1, 1 + shown).map((line) => line.replace(/ +$/, ''));
+ const inner = Math.min(width - 6, Math.max(20, ...content.map((line) => visibleWidth(line) + 1)));
+ const padded = content.map((line) => line + ' '.repeat(Math.max(0, inner - visibleWidth(line))));
+ const below = lines.slice(2 + shown); // the slash-command menu
+ return [...boxLines(padded, inner + 6), ...below.map((line) => ` ${line}`)].map((line) =>
+ truncateToWidth(line, width),
+ );
+ }
+}
+
+export default function anus(pi) {
+ let list = loadList();
+ let registry = null;
+ let ui = null;
+ let routed = 'free models'; // the real model that answered last, for the footer
+ let tui = null;
+ const rest = loadRest();
+ const resting = (key, ms) => {
+ rest.set(key, Date.now() + ms);
+ if (ms > 10 * 60_000) saveRest(rest);
+ };
+
+ const usable = (needImages) => (entry) => {
+ if (!registry || !PROVIDER_IDS.includes(entry.provider)) return null;
+ const model = registry.find(entry.provider, entry.id);
+ if (!model || !registry.hasConfiguredAuth(model)) return null;
+ if (needImages && !model.input?.includes('image')) return null;
+ return model;
+ };
+
+ const cut = (lines, width) => lines.map((line) => truncateToWidth(line, Math.max(1, width)));
+
+ // "✦ Generating thrust... (esc to cancel, 3s)" above the input while working
+ let working = null;
+ const stopWorking = (ctx) => {
+ if (working) clearInterval(working.timer);
+ working = null;
+ ctx?.ui?.setWidget('anus-working', undefined);
+ };
+
+ pi.on('session_start', async (_event, ctx) => {
+ registry = ctx.modelRegistry;
+ ui = ctx.hasUI ? ctx.ui : null;
+ refreshList().then((fresh) => {
+ if (fresh) list = fresh;
+ });
+ if (ctx.mode === 'tui') {
+ useLightPalette(ctx.ui.theme?.name === 'anus-light' || ctx.ui.theme?.name === 'light');
+ // The look of ANUS CLI 0.1.0 (lib/look.mjs). The engine crashes on a
+ // line wider than the terminal, so every line is cut to the width.
+ ctx.ui.setHeader(() => ({ render: (width) => cut(headerLines(width), width), invalidate() {} }));
+ ctx.ui.setEditorComponent((t, theme, keybindings) => {
+ tui = t;
+ return new BoxEditor(t, theme, keybindings, { paddingX: 0 });
+ });
+ ctx.ui.setWorkingVisible(false);
+ ctx.ui.setHiddenThinkingLabel(''); // 0.1.0 showed no thinking at all
+ ctx.ui.setFooter((t) => {
+ tui = t;
+ return {
+ render: (width) => {
+ const usage = ctx.getContextUsage();
+ const left = usage?.percent == null ? null : Math.max(0, 100 - Math.round(usage.percent));
+ return cut([footerLine({ cwd: ctx.cwd, model: routed, contextLeft: left }, width)], width);
+ },
+ invalidate() {},
+ };
+ });
+ }
+ const keys = PROVIDERS.filter((p) => registry.getProviderAuthStatus(p.id)?.configured);
+ if (!keys.length && ui) ui.notify('ANUS has no free keys yet. Quit (Ctrl+C twice) and run: anus setup', 'warning');
+ });
+
+ pi.on('agent_start', async (_event, ctx) => {
+ if (ctx.mode !== 'tui') return;
+ stopWorking(ctx);
+ const start = Date.now();
+ let phrase = PHRASES[Math.floor(Math.random() * PHRASES.length)];
+ let changed = start;
+ working = {
+ timer: setInterval(() => {
+ if (Date.now() - changed >= 15_000) {
+ phrase = PHRASES[Math.floor(Math.random() * PHRASES.length)];
+ changed = Date.now();
+ }
+ tui?.requestRender();
+ }, 1000),
+ };
+ ctx.ui.setWidget('anus-working', () => ({
+ render: (width) => cut([workingLine(phrase, Math.floor((Date.now() - start) / 1000), width)], width),
+ invalidate() {},
+ }));
+ });
+ pi.on('agent_settled', async (_event, ctx) => stopWorking(ctx));
+ pi.on('session_shutdown', async (_event, ctx) => stopWorking(ctx));
+
+ pi.registerMarkdownTransformer((markdown, context) =>
+ context.messageType === 'assistant'
+ ? starAnswer(markdown)
+ : context.messageType === 'user'
+ ? boxUserMessage(markdown, context.availableWidth)
+ : markdown,
+ );
+
+ pi.registerCommand('help', {
+ description: 'How to use ANUS',
+ handler: async (_args, ctx) => {
+ ctx.ui.notify(
+ [
+ 'Type a task in plain words: ANUS reads files, edits them and runs commands.',
+ '',
+ '/free which free models are awake, resting or missing a key',
+ '/model pick a model by hand (anus/free is the automatic one)',
+ '/compact shrink a long conversation',
+ '/resume open an earlier session (anus -c continues the last one)',
+ '/hotkeys keyboard shortcuts',
+ '!ls run a shell command',
+ 'esc stop the current answer; ctrl+c twice to quit',
+ '',
+ 'Keys: anus setup · settings and sessions: ~/.anus/agent · https://anus.dev',
+ ].join('\n'),
+ 'info',
+ );
+ },
+ });
+
+ pi.registerCommand('free', {
+ description: 'Show the free models ANUS routes between',
+ handler: async (_args, ctx) => {
+ const now = Date.now();
+ const lines = (list?.models ?? []).map((entry) => {
+ const model = usable(false)(entry);
+ const until = Math.max(rest.get(keyOf(entry)) ?? 0, rest.get(entry.provider) ?? 0);
+ const state = !model
+ ? 'no key'
+ : until > now
+ ? `resting ${Math.ceil((until - now) / 60_000)} min`
+ : entry.ping === 'down'
+ ? 'awake, missed the last ping'
+ : 'awake';
+ const iq = entry.iq != null ? ` iq ${entry.iq}` : '';
+ return `${shortName(entry)}${iq} — ${state}`;
+ });
+ ctx.ui.notify([`free models (list of ${list?.updated ?? 'unknown date'}):`, ...lines].join('\n'), 'info');
+ },
+ });
+
+ // OpenRouter credits requests to the app named in these headers (its public
+ // app rankings), and some free models only serve named coding agents.
+ pi.registerProvider('openrouter', {
+ headers: { 'HTTP-Referer': 'https://anus.dev', 'X-Title': 'ANUS' },
+ });
+
+ pi.registerProvider('anus', {
+ name: 'ANUS',
+ baseUrl: 'https://anus.dev', // required by the engine; requests never go here
+ api: 'anus-router',
+ apiKey: 'free',
+ models: [
+ {
+ id: 'free',
+ name: 'ANUS free (auto)',
+ reasoning: true,
+ input: ['text', 'image'],
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
+ contextWindow: 262144,
+ maxTokens: 32768,
+ },
+ ],
+ streamSimple: (_model, context, options) => route(context, options),
+ });
+
+ function route(context, options = {}) {
+ const out = createAssistantMessageEventStream();
+ const now = Date.now();
+ const tries = plan({
+ models: list?.models ?? [],
+ usable: usable(hasImages(context.messages)),
+ rest,
+ now,
+ tokens: estimateTokens(context.messages),
+ }).slice(0, MAX_TRIES);
+ debug('plan', tries.map(({ entry }) => keyOf(entry)).join(' '));
+
+ // The engine's own key, headers and output cap belong to `anus/free`;
+ // each real model gets its own.
+ const { apiKey: _k, headers: _h, env: _e, maxTokens: _m, deferred: _d, ...passOn } = options;
+
+ (async () => {
+ let lastError = null;
+ const refusedKeys = new Set();
+ for (const { entry, model } of tries) {
+ if (options.signal?.aborted) break;
+ if (refusedKeys.has(entry.provider)) continue;
+ let inner;
+ try {
+ inner = registry.streamSimple(model, context, passOn);
+ } catch (err) {
+ lastError = err?.message ?? String(err);
+ debug('throw', keyOf(entry), lastError);
+ resting(entry.provider, restFor(lastError, Date.now()).ms);
+ continue;
+ }
+ let held = null;
+ let spoke = false;
+ let failed = null;
+ for await (const event of inner) {
+ if (event.type === 'start') {
+ held = event;
+ continue;
+ }
+ if (event.type === 'error' && !spoke && event.reason !== 'aborted' && !options.signal?.aborted) {
+ failed = event;
+ break;
+ }
+ if (!spoke) {
+ spoke = true;
+ routed = `${entry.provider}/${entry.id}`;
+ tui?.requestRender();
+ if (held) out.push(held);
+ }
+ if (event.type === 'error' && event.reason !== 'aborted' && !options.signal?.aborted) {
+ // Broke off after it started: the engine retries the turn, and
+ // this model rests so the retry lands on the next one.
+ const cause = event.error?.errorMessage;
+ const { ms, scope } = restFor(cause, Date.now());
+ if (ms > 0 && event.error) {
+ resting(scope === 'provider' ? entry.provider : keyOf(entry), ms);
+ event.error.errorMessage = midStreamError(cause, shortName(entry));
+ }
+ debug('broke', keyOf(entry), String(cause).slice(0, 300));
+ }
+ if (event.type === 'done' || event.type === 'error') {
+ const message = event.type === 'done' ? event.message : event.error;
+ if (message?.usage) message.usage.cost = { ...ZERO_COST };
+ }
+ out.push(event);
+ }
+ if (!failed) {
+ debug('ended', keyOf(entry), spoke ? '' : '(empty)');
+ out.end();
+ return;
+ }
+ lastError = failed.error?.errorMessage ?? 'unknown error';
+ const { ms, scope } = restFor(lastError, Date.now());
+ debug('refused', keyOf(entry), `rest ${Math.round(ms / 1000)}s ${scope}:`, lastError.slice(0, 300));
+ if (scope === 'provider') refusedKeys.add(entry.provider);
+ if (ms > 0) resting(scope === 'provider' ? entry.provider : keyOf(entry), ms);
+ }
+ out.push({
+ type: 'error',
+ reason: options.signal?.aborted ? 'aborted' : 'error',
+ error: failure(lastError, tries.length),
+ });
+ out.end();
+ })().catch((err) => {
+ out.push({ type: 'error', reason: 'error', error: failure(err?.message ?? String(err), tries.length) });
+ out.end();
+ });
+ return out;
+ }
+
+ function failure(lastError, tried) {
+ const text = !tried
+ ? 'ANUS has no free model to use: no keys yet, or the conversation is too long for every free model. Run `anus setup` to add keys, or /compact.'
+ : restFor(lastError, Date.now()).scope === 'provider'
+ ? `A free key was refused (${String(lastError).slice(0, 120)}). Run \`anus setup\` to paste it again.`
+ : `All ${tried} free models refused this request. Last answer: ${lastError}`;
+ return {
+ role: 'assistant',
+ content: [],
+ api: 'anus-router',
+ provider: 'anus',
+ model: 'free',
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { ...ZERO_COST } },
+ stopReason: 'error',
+ errorMessage: text,
+ timestamp: Date.now(),
+ };
+ }
+}
diff --git a/integration-tests/file-system.test.ts b/integration-tests/file-system.test.ts
deleted file mode 100644
index 026e746..0000000
--- a/integration-tests/file-system.test.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect } from 'vitest';
-import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
-
-describe('file-system', () => {
- it('should be able to read a file', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to read a file');
- rig.createFile('test.txt', 'hello world');
-
- const result = await rig.run(
- `read the file test.txt and show me its contents`,
- );
-
- const foundToolCall = await rig.waitForToolCall('read_file');
-
- // Add debugging information
- if (!foundToolCall || !result.includes('hello world')) {
- printDebugInfo(rig, result, {
- 'Found tool call': foundToolCall,
- 'Contains hello world': result.includes('hello world'),
- });
- }
-
- expect(
- foundToolCall,
- 'Expected to find a read_file tool call',
- ).toBeTruthy();
-
- // Validate model output - will throw if no output, warn if missing expected content
- validateModelOutput(result, 'hello world', 'File read test');
- });
-
- it('should be able to write a file', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to write a file');
- rig.createFile('test.txt', '');
-
- const result = await rig.run(`edit test.txt to have a hello world message`);
-
- // Accept multiple valid tools for editing files
- const foundToolCall = await rig.waitForAnyToolCall([
- 'write_file',
- 'edit',
- 'replace',
- ]);
-
- // Add debugging information
- if (!foundToolCall) {
- printDebugInfo(rig, result);
- }
-
- expect(
- foundToolCall,
- 'Expected to find a write_file, edit, or replace tool call',
- ).toBeTruthy();
-
- // Validate model output - will throw if no output
- validateModelOutput(result, null, 'File write test');
-
- const fileContent = rig.readFile('test.txt');
-
- // Add debugging for file content
- if (!fileContent.toLowerCase().includes('hello')) {
- const writeCalls = rig
- .readToolLogs()
- .filter((t) => t.toolRequest.name === 'write_file')
- .map((t) => t.toolRequest.args);
-
- printDebugInfo(rig, result, {
- 'File content mismatch': true,
- 'Expected to contain': 'hello',
- 'Actual content': fileContent,
- 'Write tool calls': JSON.stringify(writeCalls),
- });
- }
-
- expect(
- fileContent.toLowerCase().includes('hello'),
- 'Expected file to contain hello',
- ).toBeTruthy();
-
- // Log success info if verbose
- if (process.env['VERBOSE'] === 'true') {
- console.log('File written successfully with hello message.');
- }
- });
-});
diff --git a/integration-tests/globalSetup.ts b/integration-tests/globalSetup.ts
deleted file mode 100644
index 8d57762..0000000
--- a/integration-tests/globalSetup.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { mkdir, readdir, rm } from 'fs/promises';
-import { join, dirname } from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-const rootDir = join(__dirname, '..');
-const integrationTestsDir = join(rootDir, '.integration-tests');
-let runDir = ''; // Make runDir accessible in teardown
-
-export async function setup() {
- runDir = join(integrationTestsDir, `${Date.now()}`);
- await mkdir(runDir, { recursive: true });
-
- // Clean up old test runs, but keep the latest few for debugging
- try {
- const testRuns = await readdir(integrationTestsDir);
- if (testRuns.length > 5) {
- const oldRuns = testRuns.sort().slice(0, testRuns.length - 5);
- await Promise.all(
- oldRuns.map((oldRun) =>
- rm(join(integrationTestsDir, oldRun), {
- recursive: true,
- force: true,
- }),
- ),
- );
- }
- } catch (e) {
- console.error('Error cleaning up old test runs:', e);
- }
-
- process.env.INTEGRATION_TEST_FILE_DIR = runDir;
- process.env.ANUS_CLI_INTEGRATION_TEST = 'true';
- process.env.TELEMETRY_LOG_FILE = join(runDir, 'telemetry.log');
-
- if (process.env.KEEP_OUTPUT) {
- console.log(`Keeping output for test run in: ${runDir}`);
- }
- process.env.VERBOSE = process.env.VERBOSE ?? 'false';
-
- console.log(`\nIntegration test output directory: ${runDir}`);
-}
-
-export async function teardown() {
- // Cleanup the test run directory unless KEEP_OUTPUT is set
- if (process.env.KEEP_OUTPUT !== 'true' && runDir) {
- await rm(runDir, { recursive: true, force: true });
- }
-}
diff --git a/integration-tests/google_web_search.test.ts b/integration-tests/google_web_search.test.ts
deleted file mode 100644
index 840e0e5..0000000
--- a/integration-tests/google_web_search.test.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect } from 'vitest';
-import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
-
-describe('google_web_search', () => {
- it('should be able to search the web', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to search the web');
-
- let result;
- try {
- result = await rig.run(`what is the weather in London`);
- } catch (error) {
- // Network errors can occur in CI environments
- if (
- error instanceof Error &&
- (error.message.includes('network') || error.message.includes('timeout'))
- ) {
- console.warn(
- 'Skipping test due to network error:',
- (error as Error).message,
- );
- return; // Skip the test
- }
- throw error; // Re-throw if not a network error
- }
-
- const foundToolCall = await rig.waitForToolCall('google_web_search');
-
- // Add debugging information
- if (!foundToolCall) {
- const allTools = printDebugInfo(rig, result);
-
- // Check if the tool call failed due to network issues
- const failedSearchCalls = allTools.filter(
- (t) =>
- t.toolRequest.name === 'google_web_search' && !t.toolRequest.success,
- );
- if (failedSearchCalls.length > 0) {
- console.warn(
- 'google_web_search tool was called but failed, possibly due to network issues',
- );
- console.warn(
- 'Failed calls:',
- failedSearchCalls.map((t) => t.toolRequest.args),
- );
- return; // Skip the test if network issues
- }
- }
-
- expect(
- foundToolCall,
- 'Expected to find a call to google_web_search',
- ).toBeTruthy();
-
- // Validate model output - will throw if no output, warn if missing expected content
- const hasExpectedContent = validateModelOutput(
- result,
- ['weather', 'london'],
- 'Google web search test',
- );
-
- // If content was missing, log the search queries used
- if (!hasExpectedContent) {
- const searchCalls = rig
- .readToolLogs()
- .filter((t) => t.toolRequest.name === 'google_web_search');
- if (searchCalls.length > 0) {
- console.warn(
- 'Search queries used:',
- searchCalls.map((t) => t.toolRequest.args),
- );
- }
- }
- });
-});
diff --git a/integration-tests/ide-client.test.ts b/integration-tests/ide-client.test.ts
deleted file mode 100644
index c0fb455..0000000
--- a/integration-tests/ide-client.test.ts
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect, beforeEach, afterEach } from 'vitest';
-import * as fs from 'node:fs';
-import * as os from 'node:os';
-import * as path from 'node:path';
-import * as net from 'node:net';
-import * as child_process from 'node:child_process';
-import { IdeClient } from '../packages/core/src/ide/ide-client.js';
-
-import { TestMcpServer } from './test-mcp-server.js';
-
-describe.skip('IdeClient', () => {
- it('reads port from file and connects', async () => {
- const server = new TestMcpServer();
- const port = await server.start();
- const pid = process.pid;
- const portFile = path.join(os.tmpdir(), `anus-ide-server-${pid}.json`);
- fs.writeFileSync(portFile, JSON.stringify({ port }));
- process.env['ANUS_CLI_IDE_WORKSPACE_PATH'] = process.cwd();
- process.env['TERM_PROGRAM'] = 'vscode';
-
- const ideClient = IdeClient.getInstance();
- await ideClient.connect();
-
- expect(ideClient.getConnectionStatus()).toEqual({
- status: 'connected',
- details: undefined,
- });
-
- fs.unlinkSync(portFile);
- await server.stop();
- delete process.env['ANUS_CLI_IDE_WORKSPACE_PATH'];
- // Reset instance
- IdeClient.instance = undefined;
- });
-});
-
-const getFreePort = (): Promise => {
- return new Promise((resolve, reject) => {
- const server = net.createServer();
- server.unref();
- server.on('error', reject);
- server.listen(0, () => {
- const port = (server.address() as net.AddressInfo).port;
- server.close(() => {
- resolve(port);
- });
- });
- });
-};
-
-describe('IdeClient fallback connection logic', () => {
- let server: TestMcpServer;
- let envPort: number;
- let pid: number;
- let portFile: string;
-
- beforeEach(async () => {
- pid = process.pid;
- portFile = path.join(os.tmpdir(), `anus-ide-server-${pid}.json`);
- server = new TestMcpServer();
- envPort = await server.start();
- process.env['ANUS_CLI_IDE_SERVER_PORT'] = String(envPort);
- process.env['TERM_PROGRAM'] = 'vscode';
- process.env['ANUS_CLI_IDE_WORKSPACE_PATH'] = process.cwd();
- // Reset instance
- IdeClient.instance = undefined;
- });
-
- afterEach(async () => {
- await server.stop();
- delete process.env['ANUS_CLI_IDE_SERVER_PORT'];
- delete process.env['ANUS_CLI_IDE_WORKSPACE_PATH'];
- if (fs.existsSync(portFile)) {
- fs.unlinkSync(portFile);
- }
- });
-
- it('connects using env var when port file does not exist', async () => {
- // Ensure port file doesn't exist
- if (fs.existsSync(portFile)) {
- fs.unlinkSync(portFile);
- }
-
- const ideClient = IdeClient.getInstance();
- await ideClient.connect();
-
- expect(ideClient.getConnectionStatus()).toEqual({
- status: 'connected',
- details: undefined,
- });
- });
-
- it('falls back to env var when connection with port from file fails', async () => {
- const filePort = await getFreePort();
- // Write port file with a port that is not listening
- fs.writeFileSync(portFile, JSON.stringify({ port: filePort }));
-
- const ideClient = IdeClient.getInstance();
- await ideClient.connect();
-
- expect(ideClient.getConnectionStatus()).toEqual({
- status: 'connected',
- details: undefined,
- });
- });
-});
-
-describe.skip('getIdeProcessId', () => {
- let child: ChildProcess;
-
- afterEach(() => {
- if (child) {
- child.kill();
- }
- });
-
- it('should return the pid of the parent process', async () => {
- // We need to spawn a child process that will run the test
- // so that we can check that getIdeProcessId returns the pid of the parent
- const parentPid = process.pid;
- const output = await new Promise((resolve, reject) => {
- child = child_process.spawn(
- 'node',
- [
- '-e',
- `
- const { getIdeProcessId } = require('../packages/core/src/ide/process-utils.js');
- getIdeProcessId().then(pid => console.log(pid));
- `,
- ],
- {
- stdio: ['pipe', 'pipe', 'pipe'],
- },
- );
-
- let out = '';
- child.stdout?.on('data', (data) => {
- out += data.toString();
- });
-
- child.on('close', (code) => {
- if (code === 0) {
- resolve(out.trim());
- } else {
- reject(new Error(`Child process exited with code ${code}`));
- }
- });
- });
-
- expect(parseInt(output, 10)).toBe(parentPid);
- }, 10000);
-});
diff --git a/integration-tests/list_directory.test.ts b/integration-tests/list_directory.test.ts
deleted file mode 100644
index 01a279f..0000000
--- a/integration-tests/list_directory.test.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect } from 'vitest';
-import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
-import { existsSync } from 'fs';
-import { join } from 'path';
-
-describe('list_directory', () => {
- it('should be able to list a directory', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to list a directory');
- rig.createFile('file1.txt', 'file 1 content');
- rig.mkdir('subdir');
- rig.sync();
-
- // Poll for filesystem changes to propagate in containers
- await rig.poll(
- () => {
- // Check if the files exist in the test directory
- const file1Path = join(rig.testDir!, 'file1.txt');
- const subdirPath = join(rig.testDir!, 'subdir');
- return existsSync(file1Path) && existsSync(subdirPath);
- },
- 1000, // 1 second max wait
- 50, // check every 50ms
- );
-
- const prompt = `Can you list the files in the current directory. Display them in the style of 'ls'`;
-
- const result = await rig.run(prompt);
-
- const foundToolCall = await rig.waitForToolCall('list_directory');
-
- // Add debugging information
- if (
- !foundToolCall ||
- !result.includes('file1.txt') ||
- !result.includes('subdir')
- ) {
- const allTools = printDebugInfo(rig, result, {
- 'Found tool call': foundToolCall,
- 'Contains file1.txt': result.includes('file1.txt'),
- 'Contains subdir': result.includes('subdir'),
- });
-
- console.error(
- 'List directory calls:',
- allTools
- .filter((t) => t.toolRequest.name === 'list_directory')
- .map((t) => t.toolRequest.args),
- );
- }
-
- expect(
- foundToolCall,
- 'Expected to find a list_directory tool call',
- ).toBeTruthy();
-
- // Validate model output - will throw if no output, warn if missing expected content
- validateModelOutput(result, ['file1.txt', 'subdir'], 'List directory test');
- });
-});
diff --git a/integration-tests/mcp_server_cyclic_schema.test.ts b/integration-tests/mcp_server_cyclic_schema.test.ts
deleted file mode 100644
index 10c74e9..0000000
--- a/integration-tests/mcp_server_cyclic_schema.test.ts
+++ /dev/null
@@ -1,196 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-/**
- * This test verifies we can match maximum schema depth errors from Anus
- * and then detect and warn about the potential tools that caused the error.
- */
-
-import { describe, it, beforeAll, expect } from 'vitest';
-import { TestRig } from './test-helper.js';
-import { join } from 'path';
-import { writeFileSync } from 'fs';
-
-// Create a minimal MCP server that doesn't require external dependencies
-// This implements the MCP protocol directly using Node.js built-ins
-const serverScript = `#!/usr/bin/env node
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-const readline = require('readline');
-const fs = require('fs');
-
-// Debug logging to stderr (only when MCP_DEBUG or VERBOSE is set)
-const debugEnabled = process.env['MCP_DEBUG'] === 'true' || process.env['VERBOSE'] === 'true';
-function debug(msg) {
- if (debugEnabled) {
- fs.writeSync(2, \`[MCP-DEBUG] \${msg}\\n\`);
- }
-}
-
-debug('MCP server starting...');
-
-// Simple JSON-RPC implementation for MCP
-class SimpleJSONRPC {
- constructor() {
- this.handlers = new Map();
- this.rl = readline.createInterface({
- input: process.stdin,
- output: process.stdout,
- terminal: false
- });
-
- this.rl.on('line', (line) => {
- debug(\`Received line: \${line}\`);
- try {
- const message = JSON.parse(line);
- debug(\`Parsed message: \${JSON.stringify(message)}\`);
- this.handleMessage(message);
- } catch (e) {
- debug(\`Parse error: \${e.message}\`);
- }
- });
- }
-
- send(message) {
- const msgStr = JSON.stringify(message);
- debug(\`Sending message: \${msgStr}\`);
- process.stdout.write(msgStr + '\\n');
- }
-
- async handleMessage(message) {
- if (message.method && this.handlers.has(message.method)) {
- try {
- const result = await this.handlers.get(message.method)(message.params || {});
- if (message.id !== undefined) {
- this.send({
- jsonrpc: '2.0',
- id: message.id,
- result
- });
- }
- } catch (error) {
- if (message.id !== undefined) {
- this.send({
- jsonrpc: '2.0',
- id: message.id,
- error: {
- code: -32603,
- message: error.message
- }
- });
- }
- }
- } else if (message.id !== undefined) {
- this.send({
- jsonrpc: '2.0',
- id: message.id,
- error: {
- code: -32601,
- message: 'Method not found'
- }
- });
- }
- }
-
- on(method, handler) {
- this.handlers.set(method, handler);
- }
-}
-
-// Create MCP server
-const rpc = new SimpleJSONRPC();
-
-// Handle initialize
-rpc.on('initialize', async (params) => {
- debug('Handling initialize request');
- return {
- protocolVersion: '2024-11-05',
- capabilities: {
- tools: {}
- },
- serverInfo: {
- name: 'cyclic-schema-server',
- version: '1.0.0'
- }
- };
-});
-
-// Handle tools/list
-rpc.on('tools/list', async () => {
- debug('Handling tools/list request');
- return {
- tools: [{
- name: 'tool_with_cyclic_schema',
- inputSchema: {
- type: 'object',
- properties: {
- data: {
- type: 'array',
- items: {
- type: 'object',
- properties: {
- child: { $ref: '#/properties/data/items' },
- },
- },
- },
- },
- }
- }]
- };
-});
-
-// Send initialization notification
-rpc.send({
- jsonrpc: '2.0',
- method: 'initialized'
-});
-`;
-
-describe('mcp server with cyclic tool schema is detected', () => {
- const rig = new TestRig();
-
- beforeAll(async () => {
- // Setup test directory with MCP server configuration
- await rig.setup('cyclic-schema-mcp-server', {
- settings: {
- mcpServers: {
- 'cyclic-schema-server': {
- command: 'node',
- args: ['mcp-server.cjs'],
- },
- },
- },
- });
-
- // Create server script in the test directory
- const testServerPath = join(rig.testDir!, 'mcp-server.cjs');
- writeFileSync(testServerPath, serverScript);
-
- // Make the script executable (though running with 'node' should work anyway)
- if (process.platform !== 'win32') {
- const { chmodSync } = await import('fs');
- chmodSync(testServerPath, 0o755);
- }
- });
-
- it('should error and suggest disabling the cyclic tool', async () => {
- // Just run any command to trigger the schema depth error.
- // If this test starts failing, check `isSchemaDepthError` from
- // anusChat.ts to see if it needs to be updated.
- // Or, possibly it could mean that grok has fixed the issue.
- const output = await rig.run('hello');
-
- expect(output).toMatch(
- /Skipping tool 'tool_with_cyclic_schema' from MCP server 'cyclic-schema-server' because it has missing types in its parameter schema/,
- );
- });
-});
diff --git a/integration-tests/read_many_files.test.ts b/integration-tests/read_many_files.test.ts
deleted file mode 100644
index f9438fb..0000000
--- a/integration-tests/read_many_files.test.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect } from 'vitest';
-import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
-
-describe('read_many_files', () => {
- it('should be able to read multiple files', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to read multiple files');
- rig.createFile('file1.txt', 'file 1 content');
- rig.createFile('file2.txt', 'file 2 content');
-
- const prompt = `Please use read_many_files to read file1.txt and file2.txt and show me what's in them`;
-
- const result = await rig.run(prompt);
-
- // Check for either read_many_files or multiple read_file calls
- const allTools = rig.readToolLogs();
- const readManyFilesCall = await rig.waitForToolCall('read_many_files');
- const readFileCalls = allTools.filter(
- (t) => t.toolRequest.name === 'read_file',
- );
-
- // Accept either read_many_files OR at least 2 read_file calls
- const foundValidPattern = readManyFilesCall || readFileCalls.length >= 2;
-
- // Add debugging information
- if (!foundValidPattern) {
- printDebugInfo(rig, result, {
- 'read_many_files called': readManyFilesCall,
- 'read_file calls': readFileCalls.length,
- });
- }
-
- expect(
- foundValidPattern,
- 'Expected to find either read_many_files or multiple read_file tool calls',
- ).toBeTruthy();
-
- // Validate model output - will throw if no output, warn if missing expected content
- validateModelOutput(
- result,
- ['file 1 content', 'file 2 content'],
- 'Read many files test',
- );
- });
-});
diff --git a/integration-tests/replace.test.ts b/integration-tests/replace.test.ts
deleted file mode 100644
index 53ec738..0000000
--- a/integration-tests/replace.test.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect, vi } from 'vitest';
-import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
-
-describe('replace', () => {
- it('should be able to replace content in a file', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to replace content in a file');
-
- const fileName = 'file_to_replace.txt';
- const originalContent = 'original content';
- const expectedContent = 'replaced content';
-
- rig.createFile(fileName, originalContent);
- const prompt = `Can you replace 'original' with 'replaced' in the file 'file_to_replace.txt'`;
-
- const result = await rig.run(prompt);
-
- const foundToolCall = await rig.waitForToolCall('replace');
-
- // Add debugging information
- if (!foundToolCall) {
- printDebugInfo(rig, result);
- }
-
- expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();
-
- // Validate model output - will throw if no output, warn if missing expected content
- validateModelOutput(
- result,
- ['replaced', 'file_to_replace.txt'],
- 'Replace content test',
- );
-
- const newFileContent = rig.readFile(fileName);
-
- // Add debugging for file content
- if (newFileContent !== expectedContent) {
- console.error('File content mismatch - Debug info:');
- console.error('Expected:', expectedContent);
- console.error('Actual:', newFileContent);
- console.error(
- 'Tool calls:',
- rig.readToolLogs().map((t) => ({
- name: t.toolRequest.name,
- args: t.toolRequest.args,
- })),
- );
- }
-
- expect(newFileContent).toBe(expectedContent);
-
- // Log success info if verbose
- vi.stubEnv('VERBOSE', 'true');
- if (process.env['VERBOSE'] === 'true') {
- console.log('File replaced successfully. New content:', newFileContent);
- }
- });
-});
diff --git a/integration-tests/run_shell_command.test.ts b/integration-tests/run_shell_command.test.ts
deleted file mode 100644
index b135612..0000000
--- a/integration-tests/run_shell_command.test.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect } from 'vitest';
-import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
-
-describe('run_shell_command', () => {
- it('should be able to run a shell command', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to run a shell command');
-
- const prompt = `Please run the command "echo hello-world" and show me the output`;
-
- const result = await rig.run(prompt);
-
- const foundToolCall = await rig.waitForToolCall('run_shell_command');
-
- // Add debugging information
- if (!foundToolCall || !result.includes('hello-world')) {
- printDebugInfo(rig, result, {
- 'Found tool call': foundToolCall,
- 'Contains hello-world': result.includes('hello-world'),
- });
- }
-
- expect(
- foundToolCall,
- 'Expected to find a run_shell_command tool call',
- ).toBeTruthy();
-
- // Validate model output - will throw if no output, warn if missing expected content
- // Model often reports exit code instead of showing output
- validateModelOutput(
- result,
- ['hello-world', 'exit code 0'],
- 'Shell command test',
- );
- });
-
- it('should be able to run a shell command via stdin', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to run a shell command via stdin');
-
- const prompt = `Please run the command "echo test-stdin" and show me what it outputs`;
-
- const result = await rig.run({ stdin: prompt });
-
- const foundToolCall = await rig.waitForToolCall('run_shell_command');
-
- // Add debugging information
- if (!foundToolCall || !result.includes('test-stdin')) {
- printDebugInfo(rig, result, {
- 'Test type': 'Stdin test',
- 'Found tool call': foundToolCall,
- 'Contains test-stdin': result.includes('test-stdin'),
- });
- }
-
- expect(
- foundToolCall,
- 'Expected to find a run_shell_command tool call',
- ).toBeTruthy();
-
- // Validate model output - will throw if no output, warn if missing expected content
- validateModelOutput(result, 'test-stdin', 'Shell command stdin test');
- });
-});
diff --git a/integration-tests/save_memory.test.ts b/integration-tests/save_memory.test.ts
deleted file mode 100644
index db07580..0000000
--- a/integration-tests/save_memory.test.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect } from 'vitest';
-import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
-
-describe('save_memory', () => {
- it('should be able to save to memory', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to save to memory');
-
- const prompt = `remember that my favorite color is blue.
-
- what is my favorite color? tell me that and surround it with $ symbol`;
- const result = await rig.run(prompt);
-
- const foundToolCall = await rig.waitForToolCall('save_memory');
-
- // Add debugging information
- if (!foundToolCall || !result.toLowerCase().includes('blue')) {
- const allTools = printDebugInfo(rig, result, {
- 'Found tool call': foundToolCall,
- 'Contains blue': result.toLowerCase().includes('blue'),
- });
-
- console.error(
- 'Memory tool calls:',
- allTools
- .filter((t) => t.toolRequest.name === 'save_memory')
- .map((t) => t.toolRequest.args),
- );
- }
-
- expect(
- foundToolCall,
- 'Expected to find a save_memory tool call',
- ).toBeTruthy();
-
- // Validate model output - will throw if no output, warn if missing expected content
- validateModelOutput(result, 'blue', 'Save memory test');
- });
-});
diff --git a/integration-tests/shell-service.test.ts b/integration-tests/shell-service.test.ts
deleted file mode 100644
index de7eaeb..0000000
--- a/integration-tests/shell-service.test.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect, beforeAll } from 'vitest';
-import { ShellExecutionService } from '../packages/core/src/services/shellExecutionService.js';
-import * as fs from 'fs/promises';
-import * as path from 'path';
-import { vi } from 'vitest';
-
-describe('ShellExecutionService programmatic integration tests', () => {
- let testDir: string;
-
- beforeAll(async () => {
- // Create a dedicated directory for this test suite to avoid conflicts.
- testDir = path.join(
- process.env.INTEGRATION_TEST_FILE_DIR!,
- 'shell-service-tests',
- );
- await fs.mkdir(testDir, { recursive: true });
- });
-
- it('should execute a simple cross-platform command (echo)', async () => {
- const command = 'echo "hello from the service"';
- const onOutputEvent = vi.fn();
- const abortController = new AbortController();
-
- const handle = ShellExecutionService.execute(
- command,
- testDir,
- onOutputEvent,
- abortController.signal,
- );
-
- const result = await handle.result;
-
- expect(result.error).toBeNull();
- expect(result.exitCode).toBe(0);
- // Output can vary slightly between shells (e.g., quotes), so check for inclusion.
- expect(result.output).toContain('hello from the service');
- });
-
- it.runIf(process.platform === 'win32')(
- 'should execute "dir" on Windows',
- async () => {
- const testFile = 'test-file-windows.txt';
- await fs.writeFile(path.join(testDir, testFile), 'windows test');
-
- const command = 'dir';
- const onOutputEvent = vi.fn();
- const abortController = new AbortController();
-
- const handle = ShellExecutionService.execute(
- command,
- testDir,
- onOutputEvent,
- abortController.signal,
- );
-
- const result = await handle.result;
-
- expect(result.error).toBeNull();
- expect(result.exitCode).toBe(0);
- expect(result.output).toContain(testFile);
- },
- );
-
- it.skipIf(process.platform === 'win32')(
- 'should execute "ls -l" on Unix',
- async () => {
- const testFile = 'test-file-unix.txt';
- await fs.writeFile(path.join(testDir, testFile), 'unix test');
-
- const command = 'ls -l';
- const onOutputEvent = vi.fn();
- const abortController = new AbortController();
-
- const handle = ShellExecutionService.execute(
- command,
- testDir,
- onOutputEvent,
- abortController.signal,
- );
-
- const result = await handle.result;
-
- expect(result.error).toBeNull();
- expect(result.exitCode).toBe(0);
- expect(result.output).toContain(testFile);
- },
- );
-
- it('should abort a running process', async () => {
- // A command that runs for a bit. 'sleep' on unix, 'timeout' on windows.
- const command = process.platform === 'win32' ? 'timeout /t 20' : 'sleep 20';
- const onOutputEvent = vi.fn();
- const abortController = new AbortController();
-
- const handle = ShellExecutionService.execute(
- command,
- testDir,
- onOutputEvent,
- abortController.signal,
- );
-
- // Abort shortly after starting
- setTimeout(() => abortController.abort(), 50);
-
- const result = await handle.result;
-
- // For debugging the flaky test.
- console.log('Abort test result:', result);
-
- expect(result.aborted).toBe(true);
- // A clean exit is exitCode 0 and no signal. If the process was truly
- // aborted, it should not have exited cleanly.
- const exitedCleanly = result.exitCode === 0 && result.signal === null;
- expect(exitedCleanly, 'Process should not have exited cleanly').toBe(false);
- });
-});
diff --git a/integration-tests/simple-mcp-server.test.ts b/integration-tests/simple-mcp-server.test.ts
deleted file mode 100644
index ef0c0b5..0000000
--- a/integration-tests/simple-mcp-server.test.ts
+++ /dev/null
@@ -1,212 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-/**
- * This test verifies MCP (Model Context Protocol) server integration.
- * It uses a minimal MCP server implementation that doesn't require
- * external dependencies, making it compatible with Docker sandbox mode.
- */
-
-import { describe, it, beforeAll, expect } from 'vitest';
-import { TestRig, validateModelOutput } from './test-helper.js';
-import { join } from 'path';
-import { writeFileSync } from 'fs';
-
-// Create a minimal MCP server that doesn't require external dependencies
-// This implements the MCP protocol directly using Node.js built-ins
-const serverScript = `#!/usr/bin/env node
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-const readline = require('readline');
-const fs = require('fs');
-
-// Debug logging to stderr (only when MCP_DEBUG or VERBOSE is set)
-const debugEnabled = process.env['MCP_DEBUG'] === 'true' || process.env['VERBOSE'] === 'true';
-function debug(msg) {
- if (debugEnabled) {
- fs.writeSync(2, \`[MCP-DEBUG] \${msg}\\n\`);
- }
-}
-
-debug('MCP server starting...');
-
-// Simple JSON-RPC implementation for MCP
-class SimpleJSONRPC {
- constructor() {
- this.handlers = new Map();
- this.rl = readline.createInterface({
- input: process.stdin,
- output: process.stdout,
- terminal: false
- });
-
- this.rl.on('line', (line) => {
- debug(\`Received line: \${line}\`);
- try {
- const message = JSON.parse(line);
- debug(\`Parsed message: \${JSON.stringify(message)}\`);
- this.handleMessage(message);
- } catch (e) {
- debug(\`Parse error: \${e.message}\`);
- }
- });
- }
-
- send(message) {
- const msgStr = JSON.stringify(message);
- debug(\`Sending message: \${msgStr}\`);
- process.stdout.write(msgStr + '\\n');
- }
-
- async handleMessage(message) {
- if (message.method && this.handlers.has(message.method)) {
- try {
- const result = await this.handlers.get(message.method)(message.params || {});
- if (message.id !== undefined) {
- this.send({
- jsonrpc: '2.0',
- id: message.id,
- result
- });
- }
- } catch (error) {
- if (message.id !== undefined) {
- this.send({
- jsonrpc: '2.0',
- id: message.id,
- error: {
- code: -32603,
- message: error.message
- }
- });
- }
- }
- } else if (message.id !== undefined) {
- this.send({
- jsonrpc: '2.0',
- id: message.id,
- error: {
- code: -32601,
- message: 'Method not found'
- }
- });
- }
- }
-
- on(method, handler) {
- this.handlers.set(method, handler);
- }
-}
-
-// Create MCP server
-const rpc = new SimpleJSONRPC();
-
-// Handle initialize
-rpc.on('initialize', async (params) => {
- debug('Handling initialize request');
- return {
- protocolVersion: '2024-11-05',
- capabilities: {
- tools: {}
- },
- serverInfo: {
- name: 'addition-server',
- version: '1.0.0'
- }
- };
-});
-
-// Handle tools/list
-rpc.on('tools/list', async () => {
- debug('Handling tools/list request');
- return {
- tools: [{
- name: 'add',
- description: 'Add two numbers',
- inputSchema: {
- type: 'object',
- properties: {
- a: { type: 'number', description: 'First number' },
- b: { type: 'number', description: 'Second number' }
- },
- required: ['a', 'b']
- }
- }]
- };
-});
-
-// Handle tools/call
-rpc.on('tools/call', async (params) => {
- debug(\`Handling tools/call request for tool: \${params.name}\`);
- if (params.name === 'add') {
- const { a, b } = params.arguments;
- return {
- content: [{
- type: 'text',
- text: String(a + b)
- }]
- };
- }
- throw new Error('Unknown tool: ' + params.name);
-});
-
-// Send initialization notification
-rpc.send({
- jsonrpc: '2.0',
- method: 'initialized'
-});
-`;
-
-describe('simple-mcp-server', () => {
- const rig = new TestRig();
-
- beforeAll(async () => {
- // Setup test directory with MCP server configuration
- await rig.setup('simple-mcp-server', {
- settings: {
- mcpServers: {
- 'addition-server': {
- command: 'node',
- args: ['mcp-server.cjs'],
- },
- },
- },
- });
-
- // Create server script in the test directory
- const testServerPath = join(rig.testDir!, 'mcp-server.cjs');
- writeFileSync(testServerPath, serverScript);
-
- // Make the script executable (though running with 'node' should work anyway)
- if (process.platform !== 'win32') {
- const { chmodSync } = await import('fs');
- chmodSync(testServerPath, 0o755);
- }
- });
-
- it('should add two numbers', async () => {
- // Test directory is already set up in before hook
- // Just run the command - MCP server config is in settings.json
- const output = await rig.run('add 5 and 10');
-
- const foundToolCall = await rig.waitForToolCall('add');
-
- expect(foundToolCall, 'Expected to find an add tool call').toBeTruthy();
-
- // Validate model output - will throw if no output, fail if missing expected content
- validateModelOutput(output, '15', 'MCP server test');
- expect(
- output.includes('15'),
- 'Expected output to contain the sum (15)',
- ).toBeTruthy();
- });
-});
diff --git a/integration-tests/test-helper.ts b/integration-tests/test-helper.ts
deleted file mode 100644
index 466e9ea..0000000
--- a/integration-tests/test-helper.ts
+++ /dev/null
@@ -1,664 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { execSync, spawn } from 'child_process';
-import { parse } from 'shell-quote';
-import { mkdirSync, writeFileSync, readFileSync } from 'fs';
-import { join, dirname } from 'path';
-import { fileURLToPath } from 'url';
-import { env } from 'process';
-import fs from 'fs';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-function sanitizeTestName(name: string) {
- return name
- .toLowerCase()
- .replace(/[^a-z0-9]/g, '-')
- .replace(/-+/g, '-');
-}
-
-// Helper to create detailed error messages
-export function createToolCallErrorMessage(
- expectedTools: string | string[],
- foundTools: string[],
- result: string,
-) {
- const expectedStr = Array.isArray(expectedTools)
- ? expectedTools.join(' or ')
- : expectedTools;
- return (
- `Expected to find ${expectedStr} tool call(s). ` +
- `Found: ${foundTools.length > 0 ? foundTools.join(', ') : 'none'}. ` +
- `Output preview: ${result ? result.substring(0, 200) + '...' : 'no output'}`
- );
-}
-
-// Helper to print debug information when tests fail
-export function printDebugInfo(
- rig: TestRig,
- result: string,
- context: Record = {},
-) {
- console.error('Test failed - Debug info:');
- console.error('Result length:', result.length);
- console.error('Result (first 500 chars):', result.substring(0, 500));
- console.error(
- 'Result (last 500 chars):',
- result.substring(result.length - 500),
- );
-
- // Print any additional context provided
- Object.entries(context).forEach(([key, value]) => {
- console.error(`${key}:`, value);
- });
-
- // Check what tools were actually called
- const allTools = rig.readToolLogs();
- console.error(
- 'All tool calls found:',
- allTools.map((t) => t.toolRequest.name),
- );
-
- return allTools;
-}
-
-// Helper to validate model output and warn about unexpected content
-export function validateModelOutput(
- result: string,
- expectedContent: string | (string | RegExp)[] | null = null,
- testName = '',
-) {
- // First, check if there's any output at all (this should fail the test if missing)
- if (!result || result.trim().length === 0) {
- throw new Error('Expected LLM to return some output');
- }
-
- // If expectedContent is provided, check for it and warn if missing
- if (expectedContent) {
- const contents = Array.isArray(expectedContent)
- ? expectedContent
- : [expectedContent];
- const missingContent = contents.filter((content) => {
- if (typeof content === 'string') {
- return !result.toLowerCase().includes(content.toLowerCase());
- } else if (content instanceof RegExp) {
- return !content.test(result);
- }
- return false;
- });
-
- if (missingContent.length > 0) {
- console.warn(
- `Warning: LLM did not include expected content in response: ${missingContent.join(', ')}.`,
- 'This is not ideal but not a test failure.',
- );
- console.warn(
- 'The tool was called successfully, which is the main requirement.',
- );
- return false;
- } else if (process.env.VERBOSE === 'true') {
- console.log(`${testName}: Model output validated successfully.`);
- }
- return true;
- }
-
- return true;
-}
-
-export class TestRig {
- bundlePath: string;
- testDir: string | null;
- testName?: string;
- _lastRunStdout?: string;
-
- constructor() {
- // Use the freshly built CLI entrypoint instead of the static bundle
- this.bundlePath = join(__dirname, '..', 'packages/cli/dist/index.js');
- this.testDir = null;
- }
-
- // Get timeout based on environment
- getDefaultTimeout() {
- if (env.CI) return 60000; // 1 minute in CI
- if (env.ANUS_SANDBOX) return 30000; // 30s in containers
- return 15000; // 15s locally
- }
-
- setup(
- testName: string,
- options: { settings?: Record } = {},
- ) {
- this.testName = testName;
- const sanitizedName = sanitizeTestName(testName);
- this.testDir = join(env.INTEGRATION_TEST_FILE_DIR!, sanitizedName);
- mkdirSync(this.testDir, { recursive: true });
-
- // Create a settings file to point the CLI to the local collector
- const anusDir = join(this.testDir, '.anus');
- // Ensure non-interactive Grok provider is used by default for these tests
- // Write a minimal settings.json with provider and dummy model
- try {
- mkdirSync(anusDir, { recursive: true });
- writeFileSync(
- join(anusDir, 'settings.json'),
- JSON.stringify({
- provider: 'grok',
- model: 'grok-beta',
- grok: { model: 'grok-beta' },
- }),
- 'utf-8',
- );
- } catch {
- // best-effort; env-based detection should still work
- }
- mkdirSync(anusDir, { recursive: true });
- // In sandbox mode, use an absolute path for telemetry inside the container
- // The container mounts the test directory at the same path as the host
- const telemetryPath =
- env.ANUS_SANDBOX && env.ANUS_SANDBOX !== 'false'
- ? join(this.testDir, 'telemetry.log') // Absolute path in test directory
- : env.TELEMETRY_LOG_FILE; // Absolute path for non-sandbox
-
- const settings = {
- telemetry: {
- enabled: true,
- target: 'local',
- otlpEndpoint: '',
- outfile: telemetryPath,
- },
- sandbox: env.ANUS_SANDBOX !== 'false' ? env.ANUS_SANDBOX : false,
- ...options.settings, // Allow tests to override/add settings
- };
- writeFileSync(
- join(anusDir, 'settings.json'),
- JSON.stringify(settings, null, 2),
- );
- }
-
- createFile(fileName: string, content: string) {
- const filePath = join(this.testDir!, fileName);
- writeFileSync(filePath, content);
- return filePath;
- }
-
- mkdir(dir: string) {
- mkdirSync(join(this.testDir!, dir), { recursive: true });
- }
-
- sync() {
- // ensure file system is done before spawning
- execSync('sync', { cwd: this.testDir! });
- }
-
- run(
- promptOrOptions: string | { prompt?: string; stdin?: string },
- ...args: string[]
- ): Promise {
- let command = `node ${this.bundlePath} --yolo`;
- const execOptions: {
- cwd: string;
- encoding: 'utf-8';
- input?: string;
- } = {
- cwd: this.testDir!,
- encoding: 'utf-8',
- };
-
- if (typeof promptOrOptions === 'string') {
- command += ` --prompt ${JSON.stringify(promptOrOptions)}`;
- } else if (
- typeof promptOrOptions === 'object' &&
- promptOrOptions !== null
- ) {
- if (promptOrOptions.prompt) {
- command += ` --prompt ${JSON.stringify(promptOrOptions.prompt)}`;
- }
- if (promptOrOptions.stdin) {
- execOptions.input = promptOrOptions.stdin;
- }
- }
-
- command += ` ${args.join(' ')}`;
-
- const commandArgs = parse(command);
- const node = commandArgs.shift() as string;
-
- const child = spawn(node, commandArgs as string[], {
- cwd: this.testDir!,
- stdio: 'pipe',
- env: {
- ...process.env,
- // Ensure Grok provider is selected in non-interactive integration tests
- GROK_API_KEY: process.env.GROK_API_KEY || 'integration-test-key',
- // Keep NO_COLOR for stable snapshots/parsing
- NO_COLOR: process.env.NO_COLOR || '1',
- },
- });
-
- let stdout = '';
- let stderr = '';
-
- // Handle stdin if provided
- if (execOptions.input) {
- child.stdin!.write(execOptions.input);
- }
- child.stdin!.end();
-
- child.stdout!.on('data', (data: Buffer) => {
- stdout += data;
- if (env.KEEP_OUTPUT === 'true' || env.VERBOSE === 'true') {
- process.stdout.write(data);
- }
- });
-
- child.stderr!.on('data', (data: Buffer) => {
- stderr += data;
- if (env.KEEP_OUTPUT === 'true' || env.VERBOSE === 'true') {
- process.stderr.write(data);
- }
- });
-
- const promise = new Promise((resolve, reject) => {
- child.on('close', (code: number) => {
- if (code === 0) {
- // Store the raw stdout for Podman telemetry parsing
- this._lastRunStdout = stdout;
-
- // Filter out telemetry output when running with Podman
- // Podman seems to output telemetry to stdout even when writing to file
- let result = stdout;
- if (env.ANUS_SANDBOX === 'podman') {
- // Remove telemetry JSON objects from output
- // They are multi-line JSON objects that start with { and contain telemetry fields
- const lines = result.split('\n');
- const filteredLines = [];
- let inTelemetryObject = false;
- let braceDepth = 0;
-
- for (const line of lines) {
- if (!inTelemetryObject && line.trim() === '{') {
- // Check if this might be start of telemetry object
- inTelemetryObject = true;
- braceDepth = 1;
- } else if (inTelemetryObject) {
- // Count braces to track nesting
- for (const char of line) {
- if (char === '{') braceDepth++;
- else if (char === '}') braceDepth--;
- }
-
- // Check if we've closed all braces
- if (braceDepth === 0) {
- inTelemetryObject = false;
- // Skip this line (the closing brace)
- continue;
- }
- } else {
- // Not in telemetry object, keep the line
- filteredLines.push(line);
- }
- }
-
- result = filteredLines.join('\n');
- }
- // If we have stderr output, include that also
- if (stderr) {
- result += `\n\nStdErr:\n${stderr}`;
- }
-
- resolve(result);
- } else {
- reject(new Error(`Process exited with code ${code}:\n${stderr}`));
- }
- });
- });
-
- return promise;
- }
-
- readFile(fileName: string) {
- const filePath = join(this.testDir!, fileName);
- const content = readFileSync(filePath, 'utf-8');
- if (env.KEEP_OUTPUT === 'true' || env.VERBOSE === 'true') {
- console.log(`--- FILE: ${filePath} ---`);
- console.log(content);
- console.log(`--- END FILE: ${filePath} ---`);
- }
- return content;
- }
-
- async cleanup() {
- // Clean up test directory
- if (this.testDir && !env.KEEP_OUTPUT) {
- try {
- execSync(`rm -rf ${this.testDir}`);
- } catch (error) {
- // Ignore cleanup errors
- if (env.VERBOSE === 'true') {
- console.warn('Cleanup warning:', (error as Error).message);
- }
- }
- }
- }
-
- async waitForTelemetryReady() {
- // In sandbox mode, telemetry is written to a relative path in the test directory
- const logFilePath =
- env.ANUS_SANDBOX && env.ANUS_SANDBOX !== 'false'
- ? join(this.testDir!, 'telemetry.log')
- : env.TELEMETRY_LOG_FILE;
-
- if (!logFilePath) return;
-
- // Wait for telemetry file to exist and have content
- await this.poll(
- () => {
- if (!fs.existsSync(logFilePath)) return false;
- try {
- const content = readFileSync(logFilePath, 'utf-8');
- // Check if file has meaningful content (at least one complete JSON object)
- return content.includes('"event.name"');
- } catch {
- return false;
- }
- },
- 2000, // 2 seconds max - reduced since telemetry should flush on exit now
- 100, // check every 100ms
- );
- }
-
- async waitForToolCall(toolName: string, timeout?: number) {
- // Use environment-specific timeout
- if (!timeout) {
- timeout = this.getDefaultTimeout();
- }
-
- // Wait for telemetry to be ready before polling for tool calls
- await this.waitForTelemetryReady();
-
- return this.poll(
- () => {
- const toolLogs = this.readToolLogs();
- return toolLogs.some((log) => log.toolRequest.name === toolName);
- },
- timeout,
- 100,
- );
- }
-
- async waitForAnyToolCall(toolNames: string[], timeout?: number) {
- // Use environment-specific timeout
- if (!timeout) {
- timeout = this.getDefaultTimeout();
- }
-
- // Wait for telemetry to be ready before polling for tool calls
- await this.waitForTelemetryReady();
-
- return this.poll(
- () => {
- const toolLogs = this.readToolLogs();
- return toolNames.some((name) =>
- toolLogs.some((log) => log.toolRequest.name === name),
- );
- },
- timeout,
- 100,
- );
- }
-
- async poll(
- predicate: () => boolean,
- timeout: number,
- interval: number,
- ): Promise {
- const startTime = Date.now();
- let attempts = 0;
- while (Date.now() - startTime < timeout) {
- attempts++;
- const result = predicate();
- if (env.VERBOSE === 'true' && attempts % 5 === 0) {
- console.log(
- `Poll attempt ${attempts}: ${result ? 'success' : 'waiting...'}`,
- );
- }
- if (result) {
- return true;
- }
- await new Promise((resolve) => setTimeout(resolve, interval));
- }
- if (env.VERBOSE === 'true') {
- console.log(`Poll timed out after ${attempts} attempts`);
- }
- return false;
- }
-
- _parseToolLogsFromStdout(stdout: string) {
- const logs: {
- timestamp: number;
- toolRequest: {
- name: string;
- args: string;
- success: boolean;
- duration_ms: number;
- };
- }[] = [];
-
- // The console output from Podman is JavaScript object notation, not JSON
- // Look for tool call events in the output
- // Updated regex to handle tool names with hyphens and underscores
- const toolCallPattern =
- /body:\s*'Tool call:\s*([\w-]+)\..*?Success:\s*(\w+)\..*?Duration:\s*(\d+)ms\.'/g;
- const matches = [...stdout.matchAll(toolCallPattern)];
-
- for (const match of matches) {
- const toolName = match[1];
- const success = match[2] === 'true';
- const duration = parseInt(match[3], 10);
-
- // Try to find function_args nearby
- const matchIndex = match.index || 0;
- const contextStart = Math.max(0, matchIndex - 500);
- const contextEnd = Math.min(stdout.length, matchIndex + 500);
- const context = stdout.substring(contextStart, contextEnd);
-
- // Look for function_args in the context
- let args = '{}';
- const argsMatch = context.match(/function_args:\s*'([^']+)'/);
- if (argsMatch) {
- args = argsMatch[1];
- }
-
- // Also try to find function_name to double-check
- // Updated regex to handle tool names with hyphens and underscores
- const nameMatch = context.match(/function_name:\s*'([\w-]+)'/);
- const actualToolName = nameMatch ? nameMatch[1] : toolName;
-
- logs.push({
- timestamp: Date.now(),
- toolRequest: {
- name: actualToolName,
- args: args,
- success: success,
- duration_ms: duration,
- },
- });
- }
-
- // If no matches found with the simple pattern, try the JSON parsing approach
- // in case the format changes
- if (logs.length === 0) {
- const lines = stdout.split('\n');
- let currentObject = '';
- let inObject = false;
- let braceDepth = 0;
-
- for (const line of lines) {
- if (!inObject && line.trim() === '{') {
- inObject = true;
- braceDepth = 1;
- currentObject = line + '\n';
- } else if (inObject) {
- currentObject += line + '\n';
-
- // Count braces
- for (const char of line) {
- if (char === '{') braceDepth++;
- else if (char === '}') braceDepth--;
- }
-
- // If we've closed all braces, try to parse the object
- if (braceDepth === 0) {
- inObject = false;
- try {
- const obj = JSON.parse(currentObject);
-
- // Check for tool call in different formats
- if (
- obj.body &&
- obj.body.includes('Tool call:') &&
- obj.attributes
- ) {
- const bodyMatch = obj.body.match(/Tool call: (\w+)\./);
- if (bodyMatch) {
- logs.push({
- timestamp: obj.timestamp || Date.now(),
- toolRequest: {
- name: bodyMatch[1],
- args: obj.attributes.function_args || '{}',
- success: obj.attributes.success !== false,
- duration_ms: obj.attributes.duration_ms || 0,
- },
- });
- }
- } else if (
- obj.attributes &&
- obj.attributes['event.name'] === 'anus_cli.tool_call'
- ) {
- logs.push({
- timestamp: obj.attributes['event.timestamp'],
- toolRequest: {
- name: obj.attributes.function_name,
- args: obj.attributes.function_args,
- success: obj.attributes.success,
- duration_ms: obj.attributes.duration_ms,
- },
- });
- }
- } catch {
- // Not valid JSON
- }
- currentObject = '';
- }
- }
- }
- }
-
- return logs;
- }
-
- readToolLogs() {
- // For Podman, first check if telemetry file exists and has content
- // If not, fall back to parsing from stdout
- if (env.ANUS_SANDBOX === 'podman') {
- // Try reading from file first
- const logFilePath = join(this.testDir!, 'telemetry.log');
-
- if (fs.existsSync(logFilePath)) {
- try {
- const content = readFileSync(logFilePath, 'utf-8');
- if (content && content.includes('"event.name"')) {
- // File has content, use normal file parsing
- // Continue to the normal file parsing logic below
- } else if (this._lastRunStdout) {
- // File exists but is empty or doesn't have events, parse from stdout
- return this._parseToolLogsFromStdout(this._lastRunStdout);
- }
- } catch {
- // Error reading file, fall back to stdout
- if (this._lastRunStdout) {
- return this._parseToolLogsFromStdout(this._lastRunStdout);
- }
- }
- } else if (this._lastRunStdout) {
- // No file exists, parse from stdout
- return this._parseToolLogsFromStdout(this._lastRunStdout);
- }
- }
-
- // In sandbox mode, telemetry is written to a relative path in the test directory
- const logFilePath =
- env.ANUS_SANDBOX && env.ANUS_SANDBOX !== 'false'
- ? join(this.testDir!, 'telemetry.log')
- : env.TELEMETRY_LOG_FILE;
-
- if (!logFilePath) {
- console.warn(`TELEMETRY_LOG_FILE environment variable not set`);
- return [];
- }
-
- // Check if file exists, if not return empty array (file might not be created yet)
- if (!fs.existsSync(logFilePath)) {
- return [];
- }
-
- const content = readFileSync(logFilePath, 'utf-8');
-
- // Split the content into individual JSON objects
- // They are separated by "}\n{"
- const jsonObjects = content
- .split(/}\s*\n\s*{/)
- .map((obj, index, array) => {
- // Add back the braces we removed during split
- if (index > 0) obj = '{' + obj;
- if (index < array.length - 1) obj = obj + '}';
- return obj.trim();
- })
- .filter((obj) => obj);
-
- const logs: {
- toolRequest: {
- name: string;
- args: string;
- success: boolean;
- duration_ms: number;
- };
- }[] = [];
-
- for (const jsonStr of jsonObjects) {
- try {
- const logData = JSON.parse(jsonStr);
- // Look for tool call logs
- if (
- logData.attributes &&
- logData.attributes['event.name'] === 'anus_cli.tool_call'
- ) {
- const toolName = logData.attributes.function_name;
- logs.push({
- toolRequest: {
- name: toolName,
- args: logData.attributes.function_args,
- success: logData.attributes.success,
- duration_ms: logData.attributes.duration_ms,
- },
- });
- }
- } catch (e) {
- // Skip objects that aren't valid JSON
- if (env.VERBOSE === 'true') {
- console.error(
- 'Failed to parse telemetry object:',
- (e as Error).message,
- );
- }
- }
- }
-
- return logs;
- }
-}
diff --git a/integration-tests/test-mcp-server.ts b/integration-tests/test-mcp-server.ts
deleted file mode 100644
index 24100b6..0000000
--- a/integration-tests/test-mcp-server.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
-import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
-import express from 'express';
-import { type Server as HTTPServer } from 'node:http';
-
-import { randomUUID } from 'node:crypto';
-
-export class TestMcpServer {
- private server: HTTPServer | undefined;
-
- async start(): Promise {
- const app = express();
- app.use(express.json());
- const mcpServer = new McpServer(
- {
- name: 'test-mcp-server',
- version: '1.0.0',
- },
- { capabilities: {} },
- );
-
- const transport = new StreamableHTTPServerTransport({
- sessionIdGenerator: () => randomUUID(),
- });
- mcpServer.connect(transport);
-
- app.post('/mcp', async (req, res) => {
- await transport.handleRequest(req, res, req.body);
- });
-
- return new Promise((resolve, reject) => {
- this.server = app.listen(0, () => {
- const address = this.server!.address();
- if (address && typeof address !== 'string') {
- resolve(address.port);
- } else {
- reject(new Error('Could not determine server port.'));
- }
- });
- this.server.on('error', reject);
- });
- }
-
- async stop(): Promise {
- if (this.server) {
- await new Promise((resolve, reject) => {
- this.server!.close((err?: Error) => {
- if (err) {
- reject(err);
- } else {
- resolve();
- }
- });
- });
- this.server = undefined;
- }
- }
-}
diff --git a/integration-tests/tsconfig.json b/integration-tests/tsconfig.json
deleted file mode 100644
index 3e053d0..0000000
--- a/integration-tests/tsconfig.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "noEmit": true,
- "allowJs": true
- },
- "include": ["**/*.ts"]
-}
diff --git a/integration-tests/vitest.config.ts b/integration-tests/vitest.config.ts
deleted file mode 100644
index e57b836..0000000
--- a/integration-tests/vitest.config.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { defineConfig } from 'vitest/config';
-
-export default defineConfig({
- test: {
- testTimeout: 300000, // 5 minutes
- globalSetup: './globalSetup.ts',
- reporters: ['default'],
- include: ['**/*.test.ts'],
- retry: 2,
- fileParallelism: false,
- },
-});
diff --git a/integration-tests/write_file.test.ts b/integration-tests/write_file.test.ts
deleted file mode 100644
index 3518ede..0000000
--- a/integration-tests/write_file.test.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect, vi } from 'vitest';
-import {
- TestRig,
- createToolCallErrorMessage,
- printDebugInfo,
- validateModelOutput,
-} from './test-helper.js';
-
-describe('write_file', () => {
- it('should be able to write a file', async () => {
- const rig = new TestRig();
- await rig.setup('should be able to write a file');
- const prompt = `show me an example of using the write tool. put a dad joke in dad.txt`;
-
- const result = await rig.run(prompt);
-
- const foundToolCall = await rig.waitForToolCall('write_file');
-
- // Add debugging information
- if (!foundToolCall) {
- printDebugInfo(rig, result);
- }
-
- const allTools = rig.readToolLogs();
- expect(foundToolCall, 'Expected to find a write_file tool call').toBeTruthy(
- createToolCallErrorMessage(
- 'write_file',
- allTools.map((t) => t.toolRequest.name),
- result,
- ),
- );
-
- // Validate model output - will throw if no output, warn if missing expected content
- validateModelOutput(result, 'dad.txt', 'Write file test');
-
- const newFilePath = 'dad.txt';
-
- const newFileContent = rig.readFile(newFilePath);
-
- // Add debugging for file content
- if (newFileContent === '') {
- console.error('File was created but is empty');
- console.error(
- 'Tool calls:',
- rig.readToolLogs().map((t) => ({
- name: t.toolRequest.name,
- args: t.toolRequest.args,
- })),
- );
- }
-
- expect(newFileContent).not.toBe('');
-
- // Log success info if verbose
- vi.stubEnv('VERBOSE', 'true');
- if (process.env['VERBOSE'] === 'true') {
- console.log(
- 'File created successfully with content:',
- newFileContent.substring(0, 100) + '...',
- );
- }
- });
-});
diff --git a/lib/engine.mjs b/lib/engine.mjs
new file mode 100644
index 0000000..7e6b337
--- /dev/null
+++ b/lib/engine.mjs
@@ -0,0 +1,113 @@
+/**
+ * The engine is pi (@earendil-works/pi-coding-agent, MIT), a dependency.
+ * pi takes its name, config folder and version from the package.json in its
+ * package folder, and PI_PACKAGE_DIR moves that folder. So ANUS keeps a small
+ * "front" folder: our package.json (name anus, folder ~/.anus) plus links
+ * to the engine's files. The engine then runs as `anus` everywhere: title,
+ * help, config folder ~/.anus/agent, env ANUS_CODING_AGENT_DIR.
+ */
+import {
+ cpSync,
+ existsSync,
+ lstatSync,
+ mkdirSync,
+ readFileSync,
+ readdirSync,
+ rmSync,
+ symlinkSync,
+ writeFileSync,
+} from 'node:fs';
+import { dirname, join } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+/** The engine's package folder and version. */
+export function findEngine() {
+ let dir = dirname(fileURLToPath(import.meta.resolve('@earendil-works/pi-coding-agent')));
+ for (;;) {
+ const pkgPath = join(dir, 'package.json');
+ if (existsSync(pkgPath)) {
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
+ if (pkg.name === '@earendil-works/pi-coding-agent')
+ return { dir, version: pkg.version, cli: join(dir, pkg.bin.pi) };
+ }
+ const up = dirname(dir);
+ if (up === dir) throw new Error('ANUS: engine package @earendil-works/pi-coding-agent not found');
+ dir = up;
+ }
+}
+
+/** Build (or refresh) the front folder and return its path. */
+export function prepareFront({ home, engine, version, root }) {
+ const front = join(home, 'engine', `${engine.version}-${version}`);
+ const pkg = {
+ name: '@anus-dev/anus',
+ version,
+ description: 'ANUS, a free coding agent',
+ piConfig: { name: 'anus', configDir: '.anus' },
+ engine: { name: '@earendil-works/pi-coding-agent', version: engine.version },
+ };
+ const text = JSON.stringify(pkg, null, 2) + '\n';
+ mkdirSync(front, { recursive: true });
+ const pkgPath = join(front, 'package.json');
+ if (!existsSync(pkgPath) || readFileSync(pkgPath, 'utf8') !== text) writeFileSync(pkgPath, text);
+ // The engine shows "what's new" from CHANGELOG.md next to package.json: ours.
+ rmSync(join(front, 'CHANGELOG.md'), { force: true }); // never write through an old link into the engine
+ cpSync(join(root, 'CHANGELOG.md'), join(front, 'CHANGELOG.md'));
+ for (const name of readdirSync(engine.dir)) {
+ if (name === 'package.json' || name === 'node_modules' || name === 'CHANGELOG.md') continue;
+ const target = join(engine.dir, name);
+ const link = join(front, name);
+ if (existsSync(link)) continue;
+ try {
+ lstatSync(link);
+ rmSync(link, { force: true, recursive: true }); // a dangling link from an old engine
+ } catch {}
+ try {
+ symlinkSync(target, link, process.platform === 'win32' ? 'junction' : undefined);
+ } catch {
+ cpSync(target, link, { recursive: true });
+ }
+ }
+ return front;
+}
+
+/**
+ * The "anus" color themes: the engine's dark and light themes with the colors
+ * of ANUS CLI 0.1.0 (lib/look.mjs). Built from the engine's own files, so a
+ * newer engine with new color keys still gets complete themes. Your message
+ * sits in a box, not on a gray bar: its background is the terminal's own.
+ */
+const THEMES = {
+ anus: {
+ base: 'dark.json',
+ vars: { accent: '#C8A6F1', cyan: '#A9E4FC', red: '#EF867E', yellow: '#F2A447', text: '#F2F2F7', gray: '#8E8E93', userMsgBg: '' },
+ colors: { border: '#C6C6C6', mdHeading: '#F2A447', mdLink: '#EC5AF7' },
+ },
+ 'anus-light': {
+ base: 'light.json',
+ vars: { accent: '#8E44C9', teal: '#1F7FA6', red: '#C0463C', yellow: '#B4661B', userMsgBg: '' },
+ colors: { border: '#8E8E93', mdHeading: '#C0621B', mdLink: '#B03AC0' },
+ },
+};
+
+export function prepareTheme({ agentDir, engine }) {
+ const dir = join(agentDir, 'themes');
+ for (const [name, spec] of Object.entries(THEMES)) {
+ try {
+ const base = JSON.parse(readFileSync(join(engine.dir, 'dist', 'modes', 'interactive', 'theme', spec.base), 'utf8'));
+ const theme = {
+ ...base,
+ name,
+ vars: { ...base.vars, ...spec.vars },
+ colors: { ...base.colors, borderAccent: 'accent', mdListBullet: 'accent', customMessageLabel: 'accent', ...spec.colors },
+ };
+ delete theme.$schema;
+ const path = join(dir, `${name}.json`);
+ const text = JSON.stringify(theme, null, 2) + '\n';
+ mkdirSync(dir, { recursive: true });
+ if (!existsSync(path) || readFileSync(path, 'utf8') !== text) writeFileSync(path, text);
+ } catch {
+ // no theme file: the engine falls back to its own theme
+ }
+ }
+}
diff --git a/lib/free-models.json b/lib/free-models.json
new file mode 100644
index 0000000..77f1910
--- /dev/null
+++ b/lib/free-models.json
@@ -0,0 +1,114 @@
+{
+ "updated": "2026-09-29",
+ "about": "Free models ANUS routes between, best first. iq: Artificial Analysis index; ping: answered a \"hi\" in the last hours (ok, flaky, down).",
+ "models": [
+ {
+ "provider": "google",
+ "id": "gemini-flash-latest"
+ },
+ {
+ "provider": "cerebras",
+ "id": "qwen-3.8-27b",
+ "iq": 26.1
+ },
+ {
+ "provider": "groq",
+ "id": "qwen/qwen3.8-27b",
+ "iq": 26.1
+ },
+ {
+ "provider": "openrouter",
+ "id": "qwen/qwen3.8-27b:free",
+ "iq": 26.1,
+ "ping": "flaky"
+ },
+ {
+ "provider": "mistral",
+ "id": "devstral-latest"
+ },
+ {
+ "provider": "openrouter",
+ "id": "thinkingmachines/inkling:free",
+ "iq": 24,
+ "ping": "down"
+ },
+ {
+ "provider": "openrouter",
+ "id": "thinkingmachines/inkling-small:free",
+ "iq": 25.3,
+ "ping": "down"
+ },
+ {
+ "provider": "openrouter",
+ "id": "nvidia/nemotron-3-ultra-550b-a55b:free",
+ "iq": 20.4,
+ "ping": "ok"
+ },
+ {
+ "provider": "mistral",
+ "id": "mistral-medium-latest"
+ },
+ {
+ "provider": "cerebras",
+ "id": "gpt-oss-120b"
+ },
+ {
+ "provider": "groq",
+ "id": "openai/gpt-oss-120b"
+ },
+ {
+ "provider": "google",
+ "id": "gemma-4-31b-it",
+ "iq": 22.7
+ },
+ {
+ "provider": "openrouter",
+ "id": "google/gemma-4-31b-it:free",
+ "iq": 22.7,
+ "ping": "flaky"
+ },
+ {
+ "provider": "openrouter",
+ "id": "cohere/north-mini-code:free",
+ "iq": 19.6,
+ "ping": "ok"
+ },
+ {
+ "provider": "openrouter",
+ "id": "poolside/laguna-s-2.1:free",
+ "ping": "flaky"
+ },
+ {
+ "provider": "openrouter",
+ "id": "nvidia/nemotron-3-super-120b-a12b:free",
+ "iq": 18.1,
+ "ping": "ok"
+ },
+ {
+ "provider": "openrouter",
+ "id": "nvidia/nemotron-3.5-lightning:free",
+ "iq": 16.3,
+ "ping": "ok"
+ },
+ {
+ "provider": "openrouter",
+ "id": "dots-studio/dots-3-note-preview:free",
+ "ping": "ok"
+ },
+ {
+ "provider": "openrouter",
+ "id": "poolside/laguna-xs-2.1:free",
+ "ping": "flaky"
+ },
+ {
+ "provider": "openrouter",
+ "id": "google/gemma-4-26b-a4b-it:free",
+ "ping": "flaky"
+ },
+ {
+ "provider": "openrouter",
+ "id": "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free",
+ "ping": "flaky"
+ }
+ ]
+}
diff --git a/lib/look.mjs b/lib/look.mjs
new file mode 100644
index 0000000..bca469e
--- /dev/null
+++ b/lib/look.mjs
@@ -0,0 +1,196 @@
+/**
+ * The look of ANUS CLI 0.1.0 (github.com/anus-dev/anus, docs/assets/anus-screenshot.png):
+ * the block logo in a warm gradient, the welcome line, the tips, the rounded
+ * input box, the "✦ Generating thrust... (esc to cancel, 3s)" line and the
+ * footer. Colors are sampled from that screenshot. Pure functions: every line
+ * is cut to the terminal width here, because the engine crashes on a line
+ * wider than the terminal (test/look.test.mjs checks every width).
+ */
+import { readFileSync } from 'node:fs';
+
+export const PALETTE = {
+ gradient: ['#F2A447', '#EE8541', '#EE857E', '#EB5A81', '#EB5BD3', '#EC5AF7'],
+ purple: '#C8A6F1',
+ text: null, // the terminal's own text color: readable on dark and light
+ gray: '#C6C6C6',
+ dim: '#8E8E93',
+ cyan: '#A9E4FC',
+ salmon: '#EF867E',
+};
+
+// The same look on a light terminal: darker shades of the same colors.
+const LIGHT = { purple: '#8E44C9', gray: '#6C6C6C', dim: '#767676', cyan: '#1F7FA6', salmon: '#C0463C' };
+const DARK = { purple: PALETTE.purple, gray: PALETTE.gray, dim: PALETTE.dim, cyan: PALETTE.cyan, salmon: PALETTE.salmon };
+
+/** Switch the palette when the engine picked the light theme. */
+export function useLightPalette(light) {
+ Object.assign(PALETTE, light ? LIGHT : DARK);
+}
+
+// longAsciiLogo of ANUS CLI 0.1.0 (packages/cli/src/ui/components/AsciiArt.ts)
+export const LOGO = [
+ ' █████╗ ███╗ ██╗██╗ ██╗███████╗',
+ '██╔══██╗████╗ ██║██║ ██║██╔════╝',
+ '███████║██╔██╗ ██║██║ ██║███████╗',
+ '██╔══██║██║╚██╗██║██║ ██║╚════██║',
+ '██║ ██║██║ ╚████║╚██████╔╝███████║',
+ '╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝',
+];
+
+export const PHRASES = JSON.parse(readFileSync(new URL('./phrases.json', import.meta.url), 'utf8')).phrases;
+
+const truecolor = /truecolor|24bit/i.test(process.env.COLORTERM ?? '');
+
+const rgb = (hex) => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));
+
+/** Nearest color of the 256-color palette (for terminals without 24-bit color). */
+function to256([r, g, b]) {
+ const level = (v) => (v < 48 ? 0 : v < 115 ? 1 : Math.floor((v - 35) / 40));
+ const cube = 16 + 36 * level(r) + 6 * level(g) + level(b);
+ const gray = Math.round(((r + g + b) / 3 - 8) / 10);
+ const grayIndex = 232 + Math.max(0, Math.min(23, gray));
+ const cubeRgb = [level(r), level(g), level(b)].map((l) => (l ? 55 + l * 40 : 0));
+ const grayRgb = 8 + (grayIndex - 232) * 10;
+ const dist = (a) => a.reduce((s, v, i) => s + (v - [r, g, b][i]) ** 2, 0);
+ return dist(cubeRgb) <= dist([grayRgb, grayRgb, grayRgb]) ? cube : grayIndex;
+}
+
+export function fg(hex, text) {
+ if (!hex) return text;
+ const c = rgb(hex);
+ const open = truecolor ? `\x1b[38;2;${c[0]};${c[1]};${c[2]}m` : `\x1b[38;5;${to256(c)}m`;
+ return `${open}${text}\x1b[39m`;
+}
+export const bold = (text) => `\x1b[1m${text}\x1b[22m`;
+export const italic = (text) => `\x1b[3m${text}\x1b[23m`;
+
+/** Color at position t (0..1) along the gradient. */
+export function gradientAt(t, stops = PALETTE.gradient) {
+ const x = Math.max(0, Math.min(1, t)) * (stops.length - 1);
+ const i = Math.min(stops.length - 2, Math.floor(x));
+ const a = rgb(stops[i]);
+ const b = rgb(stops[i + 1]);
+ const f = x - i;
+ return `#${a.map((v, k) => Math.round(v + (b[k] - v) * f).toString(16).padStart(2, '0')).join('')}`;
+}
+
+/** A line painted left to right in the logo gradient, one color per column. */
+export function paintGradient(line, width = line.length) {
+ return [...line].map((ch, i) => (ch === ' ' ? ch : fg(gradientAt(i / Math.max(1, width - 1)), ch))).join('');
+}
+
+const ANSI = /\x1b\[[0-9;]*m/g;
+
+export const visibleWidth = (s) => [...s.replace(ANSI, '')].length;
+
+/** Cut a colored line to `width` visible characters, keeping its color codes. */
+export function fit(line, width) {
+ if (visibleWidth(line) <= width) return line;
+ let out = '';
+ let seen = 0;
+ for (const part of line.split(/(\x1b\[[0-9;]*m)/)) {
+ if (part.startsWith('\x1b[')) {
+ out += part;
+ continue;
+ }
+ for (const ch of part) {
+ if (seen >= width) break;
+ out += ch;
+ seen += 1;
+ }
+ }
+ return `${out}\x1b[0m`;
+}
+
+/** The start screen: logo, welcome, tagline, tips. */
+export function headerLines(width) {
+ const logoWidth = Math.max(...LOGO.map((l) => [...l].length));
+ const logo =
+ width >= logoWidth ? LOGO.map((l) => paintGradient(l, logoWidth)) : [bold(paintGradient('ANUS', 4))];
+ const p = PALETTE;
+ return [
+ '',
+ ...logo,
+ '',
+ bold(fg(p.purple, 'Welcome to ANUS - Autonomous Networked Utility System')),
+ '',
+ italic(fg(p.gray, 'Powered by the smartest free models • Born from AI, built by AI')),
+ '',
+ fg(p.text, 'Tips for getting started:'),
+ fg(p.text, '1. Ask questions, edit files, or run commands.'),
+ fg(p.text, '2. Be specific for the best results.'),
+ `${fg(p.text, '3. ')}${bold(fg(p.purple, '/help'))}${fg(p.text, ' for more information.')}`,
+ '',
+ ].map((line) => fit(line, Math.max(1, width)));
+}
+
+/** "✦ Generating thrust... (esc to cancel, 3s)" */
+export function workingLine(phrase, seconds, width) {
+ const time = seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
+ const line = `${fg(PALETTE.purple, '✦')} ${fg(PALETTE.cyan, phrase)} ${fg(PALETTE.dim, `(esc to cancel, ${time})`)}`;
+ return fit(line, Math.max(1, width));
+}
+
+/**
+ * "✦ " before an answer, like 0.1.0. Only before a plain first paragraph:
+ * a heading, list, quote, table or code block keeps its own first line.
+ */
+export function starAnswer(markdown) {
+ const text = String(markdown ?? '');
+ if (!text.trim() || /^\s*(#|```|~~~|[-*+] |\d+[.)] |>|\|)/.test(text)) return text;
+ return `${fg(PALETTE.purple, '✦')} ${text.replace(/^\s+/, '')}`;
+}
+
+/**
+ * Your message in the transcript: "> is this a joke?" in a rounded box, like
+ * the 0.1.0 screenshot. Markdown line breaks keep the box lines apart. A long,
+ * multi-line or markdown-marked message keeps just the "> ".
+ */
+export function boxUserMessage(markdown, width) {
+ const text = String(markdown ?? '').trim();
+ const border = (s) => fg(PALETTE.gray, s);
+ const inner = [...text].length + 2; // "> " + text
+ if (!text || text.includes('\n') || /[*_`[\]<>#|~\\]/.test(text) || inner + 4 > width)
+ return `${border('>')} ${text}`;
+ const w = Math.max(20, inner);
+ return [
+ border(`╭${'─'.repeat(w + 2)}╮`),
+ `${border('│')} ${border('>')} ${fg(PALETTE.gray, text)}${' '.repeat(w - inner)} ${border('│')}`,
+ border(`╰${'─'.repeat(w + 2)}╯`),
+ ].join(' \n');
+}
+
+/** Rounded box around the input, "> " on the first line, like 0.1.0. */
+export function boxLines(inner, width) {
+ const border = (s) => fg(PALETTE.gray, s);
+ const w = Math.max(6, width);
+ return [
+ border(`╭${'─'.repeat(w - 2)}╮`),
+ ...inner.map((line, i) => `${border('│')} ${fg(PALETTE.gray, i === 0 ? '>' : ' ')} ${line} ${border('│')}`),
+ border(`╰${'─'.repeat(w - 2)}╯`),
+ ];
+}
+
+/**
+ * Footer: "~/path no sandbox (see /help) model (97% context left)".
+ * Short on room, it drops "(see /help)" first, then shortens the model name,
+ * and keeps the left part only as the last resort.
+ */
+export function footerLine({ cwd, model, contextLeft }, width) {
+ const home = process.env.HOME;
+ const path = home && cwd.startsWith(home) ? `~${cwd.slice(home.length)}` : cwd;
+ const ctx = contextLeft != null ? ` (${contextLeft}% context left)` : '';
+ const short = String(model).split('/').pop().replace(/:free$/, '');
+ const tries = [
+ [`${fg(PALETTE.gray, path)} ${fg(PALETTE.salmon, 'no sandbox')} ${fg(PALETTE.dim, '(see /help)')}`, `${model}${ctx}`],
+ [`${fg(PALETTE.gray, path)} ${fg(PALETTE.salmon, 'no sandbox')}`, `${model}${ctx}`],
+ [`${fg(PALETTE.gray, path)} ${fg(PALETTE.salmon, 'no sandbox')}`, `${short}${ctx}`],
+ [`${fg(PALETTE.gray, path)} ${fg(PALETTE.salmon, 'no sandbox')}`, short],
+ ];
+ for (const [left, r] of tries) {
+ const right = fg(PALETTE.gray, r);
+ const gap = width - visibleWidth(left) - visibleWidth(right);
+ if (gap >= 3) return `${left}${' '.repeat(gap)}${right}`;
+ }
+ return fit(tries[0][0], Math.max(1, width));
+}
diff --git a/lib/main.mjs b/lib/main.mjs
new file mode 100644
index 0000000..e88a070
--- /dev/null
+++ b/lib/main.mjs
@@ -0,0 +1,68 @@
+/**
+ * `anus`: set the stage, then hand the terminal to the engine.
+ *
+ * anus start in the current folder (first run asks for free keys)
+ * anus setup add or change free keys
+ * anus update update ANUS itself
+ * anus the engine's own flags and commands (anus --help)
+ */
+import { spawnSync } from 'node:child_process';
+import { readFileSync } from 'node:fs';
+import { homedir } from 'node:os';
+import { dirname, join } from 'node:path';
+import { fileURLToPath, pathToFileURL } from 'node:url';
+import { findEngine, prepareFront, prepareTheme } from './engine.mjs';
+import { PROVIDERS } from './providers.mjs';
+import { ensureSettings, keyedProviders, runSetup } from './setup.mjs';
+
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
+const VERSION = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8')).version;
+
+// Engine commands that manage packages and credentials: no router needed.
+const ENGINE_COMMANDS = new Set(['install', 'remove', 'uninstall', 'list', 'config', 'auth']);
+const ONE_SHOT = new Set(['-p', '--print', '--mode', '--export']);
+const INFO = new Set(['-h', '--help', '-v', '--version', '--list-models']);
+
+export async function main(argv) {
+ const home = process.env.ANUS_HOME ?? join(homedir(), '.anus');
+ const agentDir = process.env.ANUS_CODING_AGENT_DIR ?? join(home, 'agent');
+ process.env.ANUS_CODING_AGENT_DIR = agentDir;
+ process.env.ANUS_ROOT = ROOT;
+ // ANUS uses only the keys given to it in `anus setup`: a paid key that
+ // happens to sit in the shell must not be spent by a "free" agent.
+ if (process.env.ANUS_ENV_KEYS !== '1') for (const p of PROVIDERS) delete process.env[p.env];
+
+ if (argv[0] === 'setup') {
+ process.exitCode = (await runSetup(agentDir)) ? 0 : 1;
+ return;
+ }
+ if (argv[0] === 'update') {
+ const res = spawnSync('npm', ['install', '-g', '@anus-dev/anus@latest'], { stdio: 'inherit', shell: process.platform === 'win32' });
+ process.exitCode = res.status ?? 1;
+ return;
+ }
+
+ ensureSettings(agentDir);
+ const command = ENGINE_COMMANDS.has(argv[0]);
+ const info = argv.some((a) => INFO.has(a));
+ const interactive = process.stdin.isTTY && process.stdout.isTTY && !argv.some((a) => ONE_SHOT.has(a));
+ if (!command && !info && !keyedProviders(agentDir).length) {
+ if (!interactive) {
+ console.error('ANUS has no free keys yet. Run `anus setup` first.');
+ process.exitCode = 1;
+ return;
+ }
+ if (!(await runSetup(agentDir))) {
+ process.exitCode = 1;
+ return;
+ }
+ }
+
+ const engine = findEngine();
+ prepareTheme({ agentDir, engine });
+ process.env.PI_PACKAGE_DIR = prepareFront({ home, engine, version: VERSION, root: ROOT });
+ process.env.PI_SKIP_VERSION_CHECK = '1'; // the engine would compare its own releases with ANUS's version
+ const args = command ? argv : ['-e', join(ROOT, 'extension', 'anus.mjs'), ...argv];
+ process.argv = [process.argv[0], engine.cli, ...args];
+ await import(pathToFileURL(engine.cli).href);
+}
diff --git a/lib/phrases.json b/lib/phrases.json
new file mode 100644
index 0000000..089a1a5
--- /dev/null
+++ b/lib/phrases.json
@@ -0,0 +1,144 @@
+{
+ "source": "ANUS CLI 0.1.0 (github.com/anus-dev/anus, Apache-2.0), packages/cli/src/ui/hooks/usePhraseCycler.ts",
+ "phrases": [
+ "Getting to the bottom of this...",
+ "Processing from the rear...",
+ "Working our way through the backend...",
+ "Pushing through the pipeline...",
+ "Squeezing out some results...",
+ "Getting behind your request...",
+ "Diving deep into the core...",
+ "Probing the system depths...",
+ "Entering through the back door...",
+ "Flushing the cache...",
+ "Clearing the pipes...",
+ "Working from behind the scenes...",
+ "Pushing data through the tunnel...",
+ "Processing your rear-quest... I mean request...",
+ "Getting cheeky with the algorithms...",
+ "Bottoms up! Processing...",
+ "Analyzing from all angles...",
+ "Taking the scenic route through the backend...",
+ "Clenching those computational muscles...",
+ "Squeezing maximum performance...",
+ "Working our assets off...",
+ "Getting to the core of the matter...",
+ "Pushing through tight constraints...",
+ "Navigating narrow channels...",
+ "Processing deep in the stack...",
+ "Working the lower layers...",
+ "Diving into the depths of computation...",
+ "Exploring the dark corners of the codebase...",
+ "Venturing where the sun don't shine (in the server room)...",
+ "Getting our hands dirty in the backend...",
+ "Plumbing the depths of logic...",
+ "Unclogging the data flow...",
+ "Releasing the pressure valve...",
+ "Opening up new channels...",
+ "Expanding our capacity...",
+ "Stretching our computational limits...",
+ "Flexing those processing muscles...",
+ "Tightening up the algorithms...",
+ "Loosening up the bottlenecks...",
+ "Working out the kinks...",
+ "Smoothing out the rough edges...",
+ "Polishing the backend...",
+ "Buffing up the performance...",
+ "Getting down and dirty with the data...",
+ "Rolling up our sleeves for deep work...",
+ "Penetrating the problem space...",
+ "Breaking through barriers...",
+ "Pushing past resistance...",
+ "Sliding into your DMs... I mean, database management...",
+ "Backing up your data... way up...",
+ "Getting intimate with the infrastructure...",
+ "Exploring hidden passages in the code...",
+ "Finding the secret tunnel to success...",
+ "Taking the express route through the backend...",
+ "Bypassing the usual channels...",
+ "Finding alternative entry points...",
+ "Accessing restricted areas...",
+ "Breaching the inner sanctum...",
+ "Infiltrating the core systems...",
+ "Penetrating security layers (legally)...",
+ "Getting past the gatekeepers...",
+ "Slipping through the cracks...",
+ "Finding the backdoor solution...",
+ "Exploiting system openings (ethically)...",
+ "Discovering hidden features...",
+ "Unlocking secret functionality...",
+ "Revealing concealed capabilities...",
+ "Exposing hidden depths...",
+ "Uncovering buried treasure...",
+ "Digging deep for answers...",
+ "Excavating solutions...",
+ "Mining the data veins...",
+ "Drilling down to the core...",
+ "Boring through the layers...",
+ "Tunneling to success...",
+ "Carving out a solution...",
+ "Sculpting the perfect response...",
+ "Molding data to fit...",
+ "Shaping up nicely...",
+ "Forming a coherent response...",
+ "Coming together nicely...",
+ "Converging on a solution...",
+ "Closing in on the answer...",
+ "Zeroing in on the target...",
+ "Homing in on success...",
+ "Approaching from behind...",
+ "Sneaking up on the solution...",
+ "Catching the problem off guard...",
+ "Ambushing inefficiency...",
+ "Surprising you with excellence...",
+ "Delivering unexpected pleasure... I mean, results...",
+ "Exceeding expectations from behind...",
+ "Coming from an unexpected angle...",
+ "Taking the scenic route...",
+ "Enjoying the journey through the backend...",
+ "Savoring the computational process...",
+ "Relishing the challenge...",
+ "Embracing the complexity...",
+ "Getting comfortable with discomfort...",
+ "Settling into the groove...",
+ "Finding our rhythm...",
+ "Hitting our stride...",
+ "Getting into position...",
+ "Assuming the position... for optimal processing...",
+ "Preparing for insertion... of data...",
+ "Ready for deep integration...",
+ "Warming up the engines...",
+ "Revving up for action...",
+ "Building up pressure...",
+ "Generating thrust...",
+ "Achieving liftoff...",
+ "Breaking through the atmosphere...",
+ "Entering new territory...",
+ "Exploring uncharted regions...",
+ "Venturing into the unknown...",
+ "Boldly going where no code has gone before...",
+ "Making first contact... with the solution...",
+ "Establishing a connection...",
+ "Forming a tight bond...",
+ "Creating intimate relationships... between data structures...",
+ "Bringing systems together...",
+ "Unifying disparate elements...",
+ "Merging streams...",
+ "Converging flows...",
+ "Coming together as one...",
+ "Achieving unity...",
+ "Reaching completion...",
+ "Approaching climax... of the computation...",
+ "Building to a crescendo...",
+ "Reaching peak performance...",
+ "Hitting the sweet spot...",
+ "Finding the G-spot... the Good spot in the code...",
+ "Touching all the right places... in memory...",
+ "Stimulating the processors...",
+ "Exciting the electrons...",
+ "Getting the bits all worked up...",
+ "Making the hardware sweat...",
+ "Working up a computational lather...",
+ "Getting hot and heavy... with the CPU..."
+ ]
+}
diff --git a/lib/providers.mjs b/lib/providers.mjs
new file mode 100644
index 0000000..4bd050b
--- /dev/null
+++ b/lib/providers.mjs
@@ -0,0 +1,45 @@
+/**
+ * The free services ANUS can run on. `id` is the engine's provider id; the
+ * key a person pastes in `anus setup` is stored under it in auth.json.
+ * Only models of these providers, and only the ones in free-models.json,
+ * are ever picked by the router.
+ */
+export const PROVIDERS = [
+ {
+ id: 'openrouter',
+ name: 'OpenRouter',
+ env: 'OPENROUTER_API_KEY',
+ keyUrl: 'https://openrouter.ai/keys',
+ note: 'one key, about fifteen free models; 50 requests a day, 1000 after a one-time $10 top-up',
+ },
+ {
+ id: 'google',
+ name: 'Google Gemini',
+ env: 'GEMINI_API_KEY',
+ keyUrl: 'https://aistudio.google.com/apikey',
+ note: 'free tier while billing is off on the project',
+ },
+ {
+ id: 'groq',
+ name: 'Groq',
+ env: 'GROQ_API_KEY',
+ keyUrl: 'https://console.groq.com/keys',
+ note: 'free tier, very fast',
+ },
+ {
+ id: 'cerebras',
+ name: 'Cerebras',
+ env: 'CEREBRAS_API_KEY',
+ keyUrl: 'https://cloud.cerebras.ai',
+ note: 'free tier, very fast',
+ },
+ {
+ id: 'mistral',
+ name: 'Mistral',
+ env: 'MISTRAL_API_KEY',
+ keyUrl: 'https://console.mistral.ai/api-keys',
+ note: 'free Experiment plan',
+ },
+];
+
+export const PROVIDER_IDS = PROVIDERS.map((p) => p.id);
diff --git a/lib/route.mjs b/lib/route.mjs
new file mode 100644
index 0000000..2f3fe8c
--- /dev/null
+++ b/lib/route.mjs
@@ -0,0 +1,95 @@
+/**
+ * Which free model gets the next request. Pure functions: no network, no
+ * engine objects, so the rules are tested on their own (test/route.test.mjs).
+ *
+ * The list (free-models.json) is ANUS's order of preference. A model that
+ * failed rests for a while; a model that did not answer the daily ping sinks
+ * to the end; a model whose context window is too small for the conversation
+ * is skipped.
+ */
+
+export const keyOf = (m) => `${m.provider}/${m.id}`;
+
+const PING_RANK = { ok: 0, flaky: 1, down: 3 };
+const pingRank = (m) => PING_RANK[m.ping] ?? 0;
+
+/** Keep the list's order, but let models that failed today's ping sink. */
+export function orderModels(models) {
+ return models
+ .map((m, i) => ({ m, i }))
+ .sort((a, b) => pingRank(a.m) - pingRank(b.m) || a.i - b.i)
+ .map(({ m }) => m);
+}
+
+/** Milliseconds until the next 00:00 UTC, when daily free quotas reset. */
+export function untilUtcMidnight(now) {
+ const d = new Date(now);
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() + 1) - now;
+}
+
+/**
+ * How long a model (or its whole provider) rests after a failed request.
+ * scope 'provider' means the key itself failed, so every model behind it rests.
+ */
+export function restFor(errorMessage, now) {
+ const e = String(errorMessage ?? '');
+ if (/\b401\b|unauthori[sz]ed|invalid.{0,20}(api.?)?key|api.?key.{0,20}(invalid|not valid|missing)/i.test(e))
+ return { ms: 6 * 3_600_000, scope: 'provider' };
+ if (/per.?day|daily|free-models-per-day|quota|exhausted|insufficient|credits|billing|\b403\b|forbidden|only available/i.test(e))
+ return { ms: untilUtcMidnight(now), scope: 'model' };
+ if (/context.{0,30}(length|window|limit)|too long|maximum.{0,30}tokens|context_length_exceeded/i.test(e))
+ return { ms: 0, scope: 'model' };
+ if (/rate.?limit|too many requests|\b429\b/i.test(e)) return { ms: 60_000, scope: 'model' };
+ return { ms: 5 * 60_000, scope: 'model' };
+}
+
+// The engine (pi 0.87.1) retries a broken answer only when the error text
+// has words it knows ("provider returned error" is one), and never when the
+// text mentions quota or billing. Kept in step by test/route.test.mjs.
+const ENGINE_NO_RETRY =
+ /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/gi;
+
+/**
+ * A model broke off after it had started answering, so the request cannot
+ * silently move on. This text makes the engine retry the turn; the model
+ * that broke rests, so the retry goes to the next free model.
+ */
+export function midStreamError(errorMessage, name) {
+ const cause = String(errorMessage ?? 'unknown error')
+ .replace(ENGINE_NO_RETRY, '(limit)')
+ .slice(0, 300);
+ return `${name} broke off mid-answer (provider returned error: ${cause}); ANUS retries on the next free model`;
+}
+
+/** Rough token count of a conversation: four characters per token. */
+export function estimateTokens(messages) {
+ let chars = 0;
+ for (const m of messages ?? []) chars += JSON.stringify(m?.content ?? '').length;
+ return Math.ceil(chars / 4);
+}
+
+/**
+ * The models to try for one request, best first.
+ *
+ * models — ordered list entries { provider, id, ... }
+ * usable — entry => engine model with a key, or null
+ * rest — Map of 'provider/id' or 'provider' => resting until (ms)
+ * tokens — estimated conversation size
+ *
+ * Awake models come first in list order; resting ones follow, soonest to
+ * wake first, so a request still has somewhere to go when everything rests.
+ */
+export function plan({ models, usable, rest, now, tokens = 0 }) {
+ const awake = [];
+ const resting = [];
+ for (const entry of orderModels(models)) {
+ const model = usable(entry);
+ if (!model) continue;
+ if (model.contextWindow && model.contextWindow < tokens * 1.15 + 4096) continue;
+ const until = Math.max(rest.get(keyOf(entry)) ?? 0, rest.get(entry.provider) ?? 0);
+ if (until <= now) awake.push({ entry, model });
+ else resting.push({ entry, model, until });
+ }
+ resting.sort((a, b) => a.until - b.until);
+ return [...awake, ...resting];
+}
diff --git a/lib/setup.mjs b/lib/setup.mjs
new file mode 100644
index 0000000..c71eedd
--- /dev/null
+++ b/lib/setup.mjs
@@ -0,0 +1,119 @@
+/**
+ * First run and `anus setup`: free keys and default settings.
+ *
+ * Keys go to /auth.json in the engine's own format, so the
+ * engine's /login and /logout see them too. Nothing leaves the machine.
+ */
+import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
+import { join } from 'node:path';
+import { createInterface } from 'node:readline';
+import { PROVIDERS } from './providers.mjs';
+
+const readJson = (path) => {
+ try {
+ return JSON.parse(readFileSync(path, 'utf8'));
+ } catch {
+ return {};
+ }
+};
+
+/** Providers from our list that already have a key (auth.json or, if allowed, env). */
+export function keyedProviders(agentDir, env = process.env) {
+ const auth = readJson(join(agentDir, 'auth.json'));
+ return PROVIDERS.filter((p) => auth[p.id]?.key || auth[p.id]?.type === 'oauth' || env[p.env]);
+}
+
+// light terminal → anus-light, dark → anus: the engine picks by the terminal background
+export const ANUS_THEME = 'anus-light/anus';
+
+/** Defaults ANUS needs, written only where the person has not set their own. */
+export function ensureSettings(agentDir) {
+ mkdirSync(agentDir, { recursive: true });
+ const path = join(agentDir, 'settings.json');
+ const settings = readJson(path);
+ const defaults = {
+ defaultProvider: 'anus',
+ defaultModel: 'free',
+ defaultThinkingLevel: 'medium',
+ theme: ANUS_THEME,
+ quietStartup: true, // no engine resource list under the ANUS start screen
+ hideThinkingBlock: true, // 0.1.0 showed answers, not the model's thinking
+ enableInstallTelemetry: false,
+ };
+ let changed = !existsSync(path);
+ // The engine writes "light" or "dark" on its first start (it asks the
+ // terminal for its background). ANUS replaces that once with its own pair;
+ // a theme the person picks later is kept.
+ if (!settings.anusLook) {
+ if (settings.theme === 'light' || settings.theme === 'dark' || settings.theme === 'anus') delete settings.theme;
+ settings.anusLook = 1;
+ changed = true;
+ }
+ for (const [k, v] of Object.entries(defaults)) {
+ if (settings[k] === undefined) {
+ settings[k] = v;
+ changed = true;
+ }
+ }
+ if (changed) writeFileSync(path, JSON.stringify(settings, null, 2) + '\n');
+}
+
+function saveKey(agentDir, providerId, key) {
+ mkdirSync(agentDir, { recursive: true });
+ const path = join(agentDir, 'auth.json');
+ const auth = readJson(path);
+ auth[providerId] = { type: 'api_key', key };
+ writeFileSync(path, JSON.stringify(auth, null, 2) + '\n', { mode: 0o600 });
+ chmodSync(path, 0o600);
+ rmSync(join(agentDir, 'anus-rest.json'), { force: true }); // a new key: forget old refusals
+}
+
+/** Ask for a line without echoing it (keys should not sit in the scrollback). */
+function askHidden(question) {
+ return new Promise((resolve) => {
+ const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
+ rl._writeToOutput = (s) => {
+ if (s.startsWith(question)) rl.output.write(question);
+ else if (s.includes('\n') || s.includes('\r')) rl.output.write('\n');
+ else rl.output.write('*'.repeat(s.length));
+ };
+ rl.question(question, (answer) => {
+ rl.close();
+ resolve(answer.trim());
+ });
+ });
+}
+
+const bold = (s) => (process.stdout.isTTY ? `\x1b[1m${s}\x1b[22m` : s);
+const dim = (s) => (process.stdout.isTTY ? `\x1b[2m${s}\x1b[22m` : s);
+
+export async function runSetup(agentDir) {
+ const have = new Set(keyedProviders(agentDir).map((p) => p.id));
+ console.log(
+ [
+ '',
+ bold('ANUS runs on free models.') + ' It needs at least one free key. Keys stay on this machine.',
+ 'Each service below gives a free key in a minute. More keys = more free requests a day.',
+ '',
+ ...PROVIDERS.map((p, i) => ` ${i + 1}. ${bold(p.name.padEnd(14))} ${p.keyUrl}\n ${dim(p.note)}${have.has(p.id) ? ' (key saved)' : ''}`),
+ '',
+ 'Start with OpenRouter: one key opens about fifteen free models.',
+ 'Paste a key and press Enter. Enter on an empty line skips.',
+ '',
+ ].join('\n'),
+ );
+ for (const p of PROVIDERS) {
+ const key = await askHidden(`${p.name} key${have.has(p.id) ? ' (Enter keeps the saved one)' : ''}: `);
+ if (!key) continue;
+ saveKey(agentDir, p.id, key);
+ have.add(p.id);
+ }
+ ensureSettings(agentDir);
+ if (!have.size) {
+ console.log('\nNo keys yet. Run `anus setup` when you have one.');
+ return false;
+ }
+ console.log(`\nSaved. ANUS will use: ${PROVIDERS.filter((p) => have.has(p.id)).map((p) => p.name).join(', ')}.`);
+ console.log('Run `anus` in a project folder to start. Inside, /free shows which free models are awake.\n');
+ return true;
+}
diff --git a/package-lock.json b/package-lock.json
index 9571bf8..750fa3f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,372 +1,522 @@
{
"name": "@anus-dev/anus",
- "version": "0.1.0",
+ "version": "0.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@anus-dev/anus",
- "version": "0.1.0",
- "license": "Apache-2.0",
- "workspaces": [
- "packages/*"
- ],
+ "version": "0.2.0",
+ "dependencies": {
+ "@earendil-works/pi-coding-agent": "^0.87.1"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent": {
+ "version": "0.87.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.87.1.tgz",
+ "integrity": "sha512-m8ArJUtVcQMSe1lLE/Ei7vX/JV7O39sWmWBsXV2NOU70F0qCp8GubA24pT3LnwTmM6LL2xV80/h6sQg85n69ew==",
+ "hasShrinkwrap": true,
+ "license": "MIT",
+ "dependencies": {
+ "@earendil-works/chord": "^0.87.1",
+ "@earendil-works/pi-agent-core": "^0.87.1",
+ "@earendil-works/pi-ai": "^0.87.1",
+ "@earendil-works/pi-tui": "^0.87.1",
+ "@silvia-odwyer/photon-node": "0.3.4",
+ "chalk": "6.0.0",
+ "cross-spawn": "7.0.6",
+ "diff": "8.0.4",
+ "grok-mermaid": "0.2.3",
+ "highlight.js": "10.7.3",
+ "hosted-git-info": "9.0.3",
+ "ignore": "7.0.8",
+ "jiti": "2.7.0",
+ "minimatch": "10.2.6",
+ "proper-lockfile": "4.1.2",
+ "semver": "7.8.5",
+ "typebox": "1.3.27",
+ "undici": "8.10.2",
+ "yaml": "2.9.0"
+ },
+ "bin": {
+ "pi": "dist/bundle/cli.js"
+ },
+ "engines": {
+ "node": ">=22.19.0"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@anthropic-ai/sdk": {
+ "version": "0.124.0",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.124.0.tgz",
+ "integrity": "sha512-cN5O8i9UVxHeOQAzj/XjshWXG8KiibJDw9OGpH2Z/eR3n/RBxdoLxDJOcfqAJWvjaMDFfHTBADU04hWRJVkDyA==",
+ "license": "MIT",
"dependencies": {
- "eventsource": "^4.0.0",
- "eventsource-parser": "^3.0.5",
- "fast-uri": "^3.1.0",
- "pkce-challenge": "^5.0.0"
+ "json-schema-to-ts": "^3.1.1",
+ "standardwebhooks": "^1.0.0"
},
"bin": {
- "anus": "bundle/anus.js"
+ "anthropic-ai-sdk": "bin/cli"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
},
- "devDependencies": {
- "@types/marked": "^5.0.2",
- "@types/micromatch": "^4.0.9",
- "@types/mime-types": "^3.0.1",
- "@types/minimatch": "^5.1.2",
- "@types/mock-fs": "^4.13.4",
- "@types/shell-quote": "^1.7.5",
- "@vitest/coverage-v8": "^3.1.1",
- "concurrently": "^9.2.0",
- "cross-env": "^7.0.3",
- "esbuild": "^0.25.0",
- "eslint": "^9.24.0",
- "eslint-config-prettier": "^10.1.2",
- "eslint-plugin-import": "^2.31.0",
- "eslint-plugin-react": "^7.37.5",
- "eslint-plugin-react-hooks": "^5.2.0",
- "glob": "^10.4.5",
- "globals": "^16.0.0",
- "json": "^11.0.0",
- "lodash": "^4.17.21",
- "memfs": "^4.17.2",
- "mnemonist": "^0.40.3",
- "mock-fs": "^5.5.0",
- "msw": "^2.10.4",
- "prettier": "^3.5.3",
- "react-devtools-core": "^4.28.5",
- "tsx": "^4.20.3",
- "typescript-eslint": "^8.30.1",
- "vitest": "^3.2.4",
- "yargs": "^17.7.2"
+ "peerDependenciesMeta": {
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/client-bedrock-runtime": {
+ "version": "3.1127.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1127.0.tgz",
+ "integrity": "sha512-IDl/lrPb90aH+pZFHGNDmgH9nAUQj5PlZH1sJ3w7RikctyjHSnY3oNjZhrLoaBoQn/rNK0zsP6OHEqEhj2tdLA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/credential-provider-node": "^3.972.82",
+ "@aws-sdk/eventstream-handler-node": "^3.972.34",
+ "@aws-sdk/middleware-eventstream": "^3.972.29",
+ "@aws-sdk/middleware-websocket": "^3.972.52",
+ "@aws-sdk/token-providers": "3.1127.0",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
- "node_modules/@alcalzone/ansi-tokenize": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.0.tgz",
- "integrity": "sha512-qI/5TaaaCZE4yeSZ83lu0+xi1r88JSxUjnH4OP/iZF7+KKZ75u3ee5isd0LxX+6N8U0npL61YrpbthILHB6BnA==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/core": {
+ "version": "3.977.9",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.9.tgz",
+ "integrity": "sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA==",
+ "license": "Apache-2.0",
"dependencies": {
- "ansi-styles": "^6.2.1",
- "is-fullwidth-code-point": "^5.0.0"
+ "@aws-sdk/types": "^3.974.5",
+ "@aws-sdk/xml-builder": "^3.972.40",
+ "@aws/lambda-invoke-store": "^0.3.0",
+ "@smithy/core": "^3.33.3",
+ "@smithy/signature-v4": "^5.6.12",
+ "@smithy/types": "^4.17.2",
+ "bowser": "^2.11.0",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": ">=18"
+ "node": ">=20.0.0"
}
},
- "node_modules/@alcalzone/ansi-tokenize/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-env": {
+ "version": "3.972.70",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.70.tgz",
+ "integrity": "sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
"engines": {
- "node": ">=12"
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-http": {
+ "version": "3.972.72",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.72.tgz",
+ "integrity": "sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "engines": {
+ "node": ">=20.0.0"
}
},
- "node_modules/@alcalzone/ansi-tokenize/node_modules/is-fullwidth-code-point": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
- "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-ini": {
+ "version": "3.973.15",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.15.tgz",
+ "integrity": "sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg==",
+ "license": "Apache-2.0",
"dependencies": {
- "get-east-asian-width": "^1.3.1"
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/credential-provider-env": "^3.972.70",
+ "@aws-sdk/credential-provider-http": "^3.972.72",
+ "@aws-sdk/credential-provider-login": "^3.972.77",
+ "@aws-sdk/credential-provider-process": "^3.972.70",
+ "@aws-sdk/credential-provider-sso": "^3.973.14",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.76",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/credential-provider-imds": "^4.4.16",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": ">=18"
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-login": {
+ "version": "3.972.77",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.77.tgz",
+ "integrity": "sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">=20.0.0"
}
},
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-node": {
+ "version": "3.972.82",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.82.tgz",
+ "integrity": "sha512-znDkEOGXB8W3kG1LJUKP3foBZY/9qLM0eil/DxWXSp37XsdsRLQHE/d/OaCGGVgKpA6znR38h/+INk8do1FjiA==",
"license": "Apache-2.0",
"dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
+ "@aws-sdk/credential-provider-env": "^3.972.70",
+ "@aws-sdk/credential-provider-http": "^3.972.72",
+ "@aws-sdk/credential-provider-ini": "^3.973.15",
+ "@aws-sdk/credential-provider-process": "^3.972.70",
+ "@aws-sdk/credential-provider-sso": "^3.973.14",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.76",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/credential-provider-imds": "^4.4.16",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": ">=6.0.0"
+ "node": ">=20.0.0"
}
},
- "node_modules/@anus-dev/anus": {
- "resolved": "packages/cli",
- "link": true
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-process": {
+ "version": "3.972.70",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.70.tgz",
+ "integrity": "sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
},
- "node_modules/@anus-dev/anus-core": {
- "resolved": "packages/core",
- "link": true
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-sso": {
+ "version": "3.973.14",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.14.tgz",
+ "integrity": "sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/token-providers": "3.1116.0",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
},
- "node_modules/@anus-dev/anus-test-utils": {
- "resolved": "packages/test-utils",
- "link": true
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": {
+ "version": "3.1116.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1116.0.tgz",
+ "integrity": "sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
},
- "node_modules/@asamuzakjp/css-color": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
- "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
- "dev": true,
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-web-identity": {
+ "version": "3.972.76",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.76.tgz",
+ "integrity": "sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA==",
+ "license": "Apache-2.0",
"dependencies": {
- "@csstools/css-calc": "^2.1.3",
- "@csstools/css-color-parser": "^3.0.9",
- "@csstools/css-parser-algorithms": "^3.0.4",
- "@csstools/css-tokenizer": "^3.0.3",
- "lru-cache": "^10.4.3"
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
}
},
- "node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/eventstream-handler-node": {
+ "version": "3.972.34",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.34.tgz",
+ "integrity": "sha512-cTeVzpu1xEAkryTZBYhGwnQ6gOGyp8ZYZvmn0Sg/nI/ABmy/CRHHxPDJDUi9PxwxUtGGaatvfRUB3FCgT/rSWw==",
+ "license": "Apache-2.0",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
- "js-tokens": "^4.0.0",
- "picocolors": "^1.1.1"
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": ">=6.9.0"
+ "node": ">=20.0.0"
}
},
- "node_modules/@babel/code-frame/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==",
- "license": "MIT"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/middleware-eventstream": {
+ "version": "3.972.29",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.29.tgz",
+ "integrity": "sha512-dlRzHCgyB8W6hLuDC5pcT5q+ziPt00n4QGgGBE17ucLVU4zMa6lsbuUdQ2Pm75Z5VA8GF+R/+SgrRcaTdIzSIQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
},
- "node_modules/@babel/helper-string-parser": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
- "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
- "dev": true,
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/middleware-websocket": {
+ "version": "3.972.52",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.52.tgz",
+ "integrity": "sha512-vsPPM+nMbKJlUCFU+eoGZbdxdxDIAX9LbpjSXaR5Ufpmqgp8TdYQnoExhLu4T3umW/JIIPny1ydbhWidZZYokQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/signature-v4": "^5.6.12",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
"engines": {
- "node": ">=6.9.0"
+ "node": ">= 14.0.0"
}
},
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
- "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/nested-clients": {
+ "version": "3.997.44",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.44.tgz",
+ "integrity": "sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.46",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/fetch-http-handler": "^5.7.2",
+ "@smithy/node-http-handler": "^4.11.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
"engines": {
- "node": ">=6.9.0"
+ "node": ">=20.0.0"
}
},
- "node_modules/@babel/parser": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
- "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
- "dev": true,
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/signature-v4-multi-region": {
+ "version": "3.996.46",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.46.tgz",
+ "integrity": "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==",
+ "license": "Apache-2.0",
"dependencies": {
- "@babel/types": "^7.28.2"
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/signature-v4": "^5.6.12",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
- "bin": {
- "parser": "bin/babel-parser.js"
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/token-providers": {
+ "version": "3.1127.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1127.0.tgz",
+ "integrity": "sha512-Dv2TMWBshJ+tF6ahs2Sy5bh4Iabsd4GAQqVvE9XZmYmnoaVbpS2QKIKE/HRacc7bTtbjEEvP+laGzHvHlf1CiQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.977.9",
+ "@aws-sdk/nested-clients": "^3.997.44",
+ "@aws-sdk/types": "^3.974.5",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": ">=6.0.0"
+ "node": ">=20.0.0"
}
},
- "node_modules/@babel/runtime": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
- "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
- "dev": true,
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/types": {
+ "version": "3.974.5",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.5.tgz",
+ "integrity": "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
"engines": {
- "node": ">=6.9.0"
+ "node": ">=20.0.0"
}
},
- "node_modules/@babel/types": {
- "version": "7.28.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
- "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
- "dev": true,
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/xml-builder": {
+ "version": "3.972.40",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.40.tgz",
+ "integrity": "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==",
+ "license": "Apache-2.0",
"dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": ">=6.9.0"
+ "node": ">=20.0.0"
}
},
- "node_modules/@bcoe/v8-coverage": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",
- "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws/lambda-invoke-store": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz",
+ "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@babel/runtime": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
"license": "MIT",
"engines": {
- "node": ">=18"
+ "node": ">=6.9.0"
}
},
- "node_modules/@bundled-es-modules/cookie": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz",
- "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==",
- "dev": true,
- "license": "ISC",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/chord": {
+ "version": "0.87.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/chord/-/chord-0.87.1.tgz",
+ "license": "MIT",
"dependencies": {
- "cookie": "^0.7.2"
+ "esbuild": "0.28.2"
+ },
+ "engines": {
+ "node": ">=22.19.0"
}
},
- "node_modules/@bundled-es-modules/statuses": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz",
- "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==",
- "dev": true,
- "license": "ISC",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": {
+ "version": "0.87.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.87.1.tgz",
+ "license": "MIT",
"dependencies": {
- "statuses": "^2.0.1"
+ "@earendil-works/chord": "^0.87.1",
+ "@earendil-works/pi-ai": "^0.87.1",
+ "@earendil-works/pi-telemetry": "^0.87.1",
+ "diff": "8.0.4",
+ "ignore": "7.0.8",
+ "typebox": "1.3.27",
+ "yaml": "2.9.0"
+ },
+ "engines": {
+ "node": ">=22.19.0"
}
},
- "node_modules/@csstools/color-helpers": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
- "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
- "license": "MIT-0",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": {
+ "version": "0.87.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.87.1.tgz",
+ "license": "MIT",
+ "dependencies": {
+ "@anthropic-ai/sdk": "0.124.0",
+ "@aws-sdk/client-bedrock-runtime": "3.1127.0",
+ "@earendil-works/pi-telemetry": "^0.87.1",
+ "@google/genai": "2.21.0",
+ "@smithy/node-http-handler": "4.12.1",
+ "http-proxy-agent": "9.1.0",
+ "https-proxy-agent": "9.1.0",
+ "openai": "6.40.0",
+ "partial-json": "0.1.7",
+ "typebox": "1.3.27"
+ },
+ "bin": {
+ "pi-ai": "dist/cli.js"
+ },
"engines": {
- "node": ">=18"
+ "node": ">=22.19.0"
}
},
- "node_modules/@csstools/css-calc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
- "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai/node_modules/agent-base": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz",
+ "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==",
"license": "MIT",
"engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@csstools/css-parser-algorithms": "^3.0.5",
- "@csstools/css-tokenizer": "^3.0.4"
+ "node": ">= 20"
}
},
- "node_modules/@csstools/css-color-parser": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
- "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai/node_modules/https-proxy-agent": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz",
+ "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==",
"license": "MIT",
"dependencies": {
- "@csstools/color-helpers": "^5.1.0",
- "@csstools/css-calc": "^2.1.4"
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "proxy-agent-negotiate": "1.1.0"
},
"engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@csstools/css-parser-algorithms": "^3.0.5",
- "@csstools/css-tokenizer": "^3.0.4"
+ "node": ">= 20"
}
},
- "node_modules/@csstools/css-parser-algorithms": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
- "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-telemetry": {
+ "version": "0.87.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-telemetry/-/pi-telemetry-0.87.1.tgz",
"license": "MIT",
"engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@csstools/css-tokenizer": "^3.0.4"
+ "node": ">=22.19.0"
}
},
- "node_modules/@csstools/css-tokenizer": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
- "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": {
+ "version": "0.87.1",
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.87.1.tgz",
"license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "1.6.0",
+ "marked": "18.0.11"
+ },
"engines": {
- "node": ">=18"
+ "node": ">=22.19.0"
}
},
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
- "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
"cpu": [
"ppc64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -376,14 +526,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/android-arm": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
- "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/android-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -393,14 +542,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/android-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
- "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/android-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -410,14 +558,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/android-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
- "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/android-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -427,14 +574,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
- "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -444,14 +590,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
- "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -461,14 +606,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
- "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -478,14 +622,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
- "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -495,14 +638,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-arm": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
- "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -512,14 +654,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
- "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -529,14 +670,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
- "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
"cpu": [
"ia32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -546,14 +686,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
- "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
"cpu": [
"loong64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -563,14 +702,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
- "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
"cpu": [
"mips64el"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -580,14 +718,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
- "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
"cpu": [
"ppc64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -597,14 +734,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
- "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
"cpu": [
"riscv64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -614,14 +750,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
- "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
"cpu": [
"s390x"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -631,14 +766,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/linux-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
- "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/linux-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -648,14 +782,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
- "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -665,14 +798,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
- "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -682,14 +814,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
- "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -699,14 +830,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
- "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -716,14 +846,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
- "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -733,14 +862,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
- "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -750,14 +878,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
- "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -767,14 +894,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
- "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
"cpu": [
"ia32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -784,14 +910,13 @@
"node": ">=18"
}
},
- "node_modules/@esbuild/win32-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
- "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@esbuild/win32-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -801,11680 +926,960 @@
"node": ">=18"
}
},
- "node_modules/@eslint-community/eslint-utils": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz",
- "integrity": "sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==",
- "dev": true,
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@google/genai": {
+ "version": "2.21.0",
+ "resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.21.0.tgz",
+ "integrity": "sha512-+PDtco2/Z0ONdzCGekCoCT+O1VJS9xJQNN4XzQpXG/t3El/SWWMkCWlFRO1KmivOHPa4Q0VjUYu1HBKCZ/v33Q==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
"dependencies": {
- "eslint-visitor-keys": "^3.4.3"
+ "google-auth-library": "^10.3.0",
+ "p-retry": "^4.6.2",
+ "protobufjs": "^7.5.4",
+ "ws": "^8.18.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "node": ">=20.0.0"
},
"peerDependencies": {
- "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
- }
- },
- "node_modules/@eslint-community/eslint-utils/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"
+ "@modelcontextprotocol/sdk": "^1.25.2"
},
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "peerDependenciesMeta": {
+ "@modelcontextprotocol/sdk": {
+ "optional": true
+ }
}
},
- "node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/codegen": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz",
+ "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz",
+ "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/fetch": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz",
+ "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1"
}
},
- "node_modules/@eslint/config-array": {
- "version": "0.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
- "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/utf8": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz",
+ "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@silvia-odwyer/photon-node": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz",
+ "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/core": {
+ "version": "3.33.3",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.33.3.tgz",
+ "integrity": "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==",
"license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.6",
- "debug": "^4.3.1",
- "minimatch": "^3.1.2"
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": ">=18.0.0"
}
},
- "node_modules/@eslint/config-helpers": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
- "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/credential-provider-imds": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz",
+ "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==",
"license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.33.2",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
+ },
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": ">=18.0.0"
}
},
- "node_modules/@eslint/core": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
- "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/fetch-http-handler": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz",
+ "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==",
"license": "Apache-2.0",
"dependencies": {
- "@types/json-schema": "^7.0.15"
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.18.0",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": ">=18.0.0"
}
},
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
- "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.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": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/js": {
- "version": "9.34.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz",
- "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- }
- },
- "node_modules/@eslint/object-schema": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
- "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/plugin-kit": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
- "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^0.15.2",
- "levn": "^0.4.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@google/genai": {
- "version": "1.13.0",
- "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.13.0.tgz",
- "integrity": "sha512-BxilXzE8cJ0zt5/lXk6KwuBcIT9P2Lbi2WXhwWMbxf1RNeC68/8DmYQqMrzQP333CieRMdbDXs0eNCphLoScWg==",
- "license": "Apache-2.0",
- "dependencies": {
- "google-auth-library": "^9.14.2",
- "ws": "^8.18.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@modelcontextprotocol/sdk": "^1.11.0"
- },
- "peerDependenciesMeta": {
- "@modelcontextprotocol/sdk": {
- "optional": true
- }
- }
- },
- "node_modules/@google/genai/node_modules/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
- "node_modules/@grpc/grpc-js": {
- "version": "1.13.4",
- "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz",
- "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@grpc/proto-loader": "^0.7.13",
- "@js-sdsl/ordered-map": "^4.4.2"
- },
- "engines": {
- "node": ">=12.10.0"
- }
- },
- "node_modules/@grpc/proto-loader": {
- "version": "0.7.15",
- "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
- "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "lodash.camelcase": "^4.3.0",
- "long": "^5.0.0",
- "protobufjs": "^7.2.5",
- "yargs": "^17.7.2"
- },
- "bin": {
- "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@humanfs/core": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
- "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/node": {
- "version": "0.16.7",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
- "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@humanfs/core": "^0.19.1",
- "@humanwhocodes/retry": "^0.4.0"
- },
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "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/retry": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
- "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@iarna/toml": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz",
- "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==",
- "license": "ISC"
- },
- "node_modules/@inquirer/confirm": {
- "version": "5.1.16",
- "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.16.tgz",
- "integrity": "sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/type": "^3.0.8"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/core": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz",
- "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/figures": "^1.0.13",
- "@inquirer/type": "^3.0.8",
- "ansi-escapes": "^4.3.2",
- "cli-width": "^4.1.0",
- "mute-stream": "^2.0.0",
- "signal-exit": "^4.1.0",
- "wrap-ansi": "^6.2.0",
- "yoctocolors-cjs": "^2.1.2"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/core/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/@inquirer/core/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/@inquirer/core/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/@inquirer/core/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/@inquirer/core/node_modules/wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@inquirer/figures": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz",
- "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@inquirer/type": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz",
- "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@isaacs/cliui": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
- "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^5.1.2",
- "string-width-cjs": "npm:string-width@^4.2.0",
- "strip-ansi": "^7.0.1",
- "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
- "wrap-ansi": "^8.1.0",
- "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@jest/schemas": {
- "version": "30.0.5",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz",
- "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@sinclair/typebox": "^0.34.0"
- },
- "engines": {
- "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.13",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
- "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.0",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.30",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
- "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@js-sdsl/ordered-map": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
- "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/js-sdsl"
- }
- },
- "node_modules/@jsonjoy.com/base64": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
- "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=10.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "2"
- }
- },
- "node_modules/@jsonjoy.com/buffers": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.0.0.tgz",
- "integrity": "sha512-NDigYR3PHqCnQLXYyoLbnEdzMMvzeiCWo1KOut7Q0CoIqg9tUAPKJ1iq/2nFhc5kZtexzutNY0LFjdwWL3Dw3Q==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=10.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "2"
- }
- },
- "node_modules/@jsonjoy.com/codegen": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz",
- "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=10.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "2"
- }
- },
- "node_modules/@jsonjoy.com/json-pack": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.11.0.tgz",
- "integrity": "sha512-nLqSTAYwpk+5ZQIoVp7pfd/oSKNWlEdvTq2LzVA4r2wtWZg6v+5u0VgBOaDJuUfNOuw/4Ysq6glN5QKSrOCgrA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@jsonjoy.com/base64": "^1.1.2",
- "@jsonjoy.com/buffers": "^1.0.0",
- "@jsonjoy.com/codegen": "^1.0.0",
- "@jsonjoy.com/json-pointer": "^1.0.1",
- "@jsonjoy.com/util": "^1.9.0",
- "hyperdyperid": "^1.2.0",
- "thingies": "^2.5.0"
- },
- "engines": {
- "node": ">=10.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "2"
- }
- },
- "node_modules/@jsonjoy.com/json-pointer": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz",
- "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@jsonjoy.com/codegen": "^1.0.0",
- "@jsonjoy.com/util": "^1.9.0"
- },
- "engines": {
- "node": ">=10.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "2"
- }
- },
- "node_modules/@jsonjoy.com/util": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz",
- "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@jsonjoy.com/buffers": "^1.0.0",
- "@jsonjoy.com/codegen": "^1.0.0"
- },
- "engines": {
- "node": ">=10.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "2"
- }
- },
- "node_modules/@kwsites/file-exists": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
- "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
- "license": "MIT",
- "dependencies": {
- "debug": "^4.1.1"
- }
- },
- "node_modules/@kwsites/promise-deferred": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
- "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
- "license": "MIT"
- },
- "node_modules/@mswjs/interceptors": {
- "version": "0.39.6",
- "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.6.tgz",
- "integrity": "sha512-bndDP83naYYkfayr/qhBHMhk0YGwS1iv6vaEGcr0SQbO0IZtbOPqjKjds/WcG+bJA+1T5vCx6kprKOzn5Bg+Vw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@open-draft/deferred-promise": "^2.2.0",
- "@open-draft/logger": "^0.3.0",
- "@open-draft/until": "^2.0.0",
- "is-node-process": "^1.2.0",
- "outvariant": "^1.4.3",
- "strict-event-emitter": "^0.5.1"
- },
- "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/@open-draft/deferred-promise": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
- "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@open-draft/logger": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
- "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-node-process": "^1.2.0",
- "outvariant": "^1.4.0"
- }
- },
- "node_modules/@open-draft/until": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
- "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@opentelemetry/api": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
- "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/@opentelemetry/api-logs": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz",
- "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/api": "^1.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@opentelemetry/context-async-hooks": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz",
- "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.0.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/core": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz",
- "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/semantic-conventions": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.0.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/exporter-logs-otlp-grpc": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.52.1.tgz",
- "integrity": "sha512-sXgcp4fsL3zCo96A0LmFIGYOj2LSEDI6wD7nBYRhuDDxeRsk18NQgqRVlCf4VIyTBZzGu1M7yOtdFukQPgII1A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@grpc/grpc-js": "^1.7.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/otlp-grpc-exporter-base": "0.52.1",
- "@opentelemetry/otlp-transformer": "0.52.1",
- "@opentelemetry/sdk-logs": "0.52.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.0.0"
- }
- },
- "node_modules/@opentelemetry/exporter-logs-otlp-http": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.52.1.tgz",
- "integrity": "sha512-qKgywId2DbdowPZpOBXQKp0B8DfhfIArmSic15z13Nk/JAOccBUQdPwDjDnjsM5f0ckZFMVR2t/tijTUAqDZoA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/api-logs": "0.52.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/otlp-exporter-base": "0.52.1",
- "@opentelemetry/otlp-transformer": "0.52.1",
- "@opentelemetry/sdk-logs": "0.52.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.0.0"
- }
- },
- "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.52.1.tgz",
- "integrity": "sha512-CE0f1IEE1GQj8JWl/BxKvKwx9wBTLR09OpPQHaIs5LGBw3ODu8ek5kcbrHPNsFYh/pWh+pcjbZQoxq3CqvQVnA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@grpc/grpc-js": "^1.7.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/exporter-metrics-otlp-http": "0.52.1",
- "@opentelemetry/otlp-exporter-base": "0.52.1",
- "@opentelemetry/otlp-grpc-exporter-base": "0.52.1",
- "@opentelemetry/otlp-transformer": "0.52.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/sdk-metrics": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.3.0"
- }
- },
- "node_modules/@opentelemetry/exporter-metrics-otlp-http": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.52.1.tgz",
- "integrity": "sha512-oAHPOy1sZi58bwqXaucd19F/v7+qE2EuVslQOEeLQT94CDuZJJ4tbWzx8DpYBTrOSzKqqrMtx9+PMxkrcbxOyQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/otlp-exporter-base": "0.52.1",
- "@opentelemetry/otlp-transformer": "0.52.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/sdk-metrics": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.3.0"
- }
- },
- "node_modules/@opentelemetry/exporter-trace-otlp-grpc": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz",
- "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@grpc/grpc-js": "^1.7.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/otlp-grpc-exporter-base": "0.52.1",
- "@opentelemetry/otlp-transformer": "0.52.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/sdk-trace-base": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.0.0"
- }
- },
- "node_modules/@opentelemetry/exporter-trace-otlp-http": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz",
- "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/otlp-exporter-base": "0.52.1",
- "@opentelemetry/otlp-transformer": "0.52.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/sdk-trace-base": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.0.0"
- }
- },
- "node_modules/@opentelemetry/exporter-trace-otlp-proto": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz",
- "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/otlp-exporter-base": "0.52.1",
- "@opentelemetry/otlp-transformer": "0.52.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/sdk-trace-base": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.0.0"
- }
- },
- "node_modules/@opentelemetry/exporter-zipkin": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz",
- "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/sdk-trace-base": "1.25.1",
- "@opentelemetry/semantic-conventions": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.0.0"
- }
- },
- "node_modules/@opentelemetry/instrumentation": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz",
- "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/api-logs": "0.52.1",
- "@types/shimmer": "^1.0.2",
- "import-in-the-middle": "^1.8.1",
- "require-in-the-middle": "^7.1.1",
- "semver": "^7.5.2",
- "shimmer": "^1.2.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.3.0"
- }
- },
- "node_modules/@opentelemetry/instrumentation-http": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.52.1.tgz",
- "integrity": "sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/instrumentation": "0.52.1",
- "@opentelemetry/semantic-conventions": "1.25.1",
- "semver": "^7.5.2"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.3.0"
- }
- },
- "node_modules/@opentelemetry/instrumentation-http/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@opentelemetry/instrumentation/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@opentelemetry/otlp-exporter-base": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz",
- "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/otlp-transformer": "0.52.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.0.0"
- }
- },
- "node_modules/@opentelemetry/otlp-grpc-exporter-base": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz",
- "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@grpc/grpc-js": "^1.7.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/otlp-exporter-base": "0.52.1",
- "@opentelemetry/otlp-transformer": "0.52.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.0.0"
- }
- },
- "node_modules/@opentelemetry/otlp-transformer": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz",
- "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/api-logs": "0.52.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/sdk-logs": "0.52.1",
- "@opentelemetry/sdk-metrics": "1.25.1",
- "@opentelemetry/sdk-trace-base": "1.25.1",
- "protobufjs": "^7.3.0"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.3.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/propagator-b3": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz",
- "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.0.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/propagator-jaeger": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz",
- "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.0.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/resources": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz",
- "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/semantic-conventions": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.0.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/sdk-logs": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz",
- "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/api-logs": "0.52.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/resources": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.4.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/sdk-metrics": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz",
- "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/resources": "1.25.1",
- "lodash.merge": "^4.6.2"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.3.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/sdk-node": {
- "version": "0.52.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz",
- "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/api-logs": "0.52.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1",
- "@opentelemetry/exporter-trace-otlp-http": "0.52.1",
- "@opentelemetry/exporter-trace-otlp-proto": "0.52.1",
- "@opentelemetry/exporter-zipkin": "1.25.1",
- "@opentelemetry/instrumentation": "0.52.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/sdk-logs": "0.52.1",
- "@opentelemetry/sdk-metrics": "1.25.1",
- "@opentelemetry/sdk-trace-base": "1.25.1",
- "@opentelemetry/sdk-trace-node": "1.25.1",
- "@opentelemetry/semantic-conventions": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.3.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/sdk-trace-base": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz",
- "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/resources": "1.25.1",
- "@opentelemetry/semantic-conventions": "1.25.1"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.0.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/sdk-trace-node": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz",
- "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@opentelemetry/context-async-hooks": "1.25.1",
- "@opentelemetry/core": "1.25.1",
- "@opentelemetry/propagator-b3": "1.25.1",
- "@opentelemetry/propagator-jaeger": "1.25.1",
- "@opentelemetry/sdk-trace-base": "1.25.1",
- "semver": "^7.5.2"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@opentelemetry/api": ">=1.0.0 <1.10.0"
- }
- },
- "node_modules/@opentelemetry/sdk-trace-node/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@opentelemetry/semantic-conventions": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz",
- "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@pkgjs/parseargs": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
- "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@pnpm/config.env-replace": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
- "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
- "license": "MIT",
- "engines": {
- "node": ">=12.22.0"
- }
- },
- "node_modules/@pnpm/network.ca-file": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
- "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "4.2.10"
- },
- "engines": {
- "node": ">=12.22.0"
- }
- },
- "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
- "license": "ISC"
- },
- "node_modules/@pnpm/npm-conf": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz",
- "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==",
- "license": "MIT",
- "dependencies": {
- "@pnpm/config.env-replace": "^1.1.0",
- "@pnpm/network.ca-file": "^1.0.1",
- "config-chain": "^1.1.11"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@protobufjs/aspromise": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
- "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/base64": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
- "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/codegen": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
- "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/eventemitter": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
- "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/fetch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
- "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.1",
- "@protobufjs/inquire": "^1.1.0"
- }
- },
- "node_modules/@protobufjs/float": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
- "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/inquire": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
- "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/path": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
- "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/pool": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
- "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/utf8": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
- "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz",
- "integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz",
- "integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz",
- "integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz",
- "integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz",
- "integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz",
- "integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz",
- "integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz",
- "integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz",
- "integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz",
- "integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz",
- "integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz",
- "integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz",
- "integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz",
- "integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz",
- "integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz",
- "integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz",
- "integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz",
- "integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz",
- "integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz",
- "integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz",
- "integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rtsao/scc": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
- "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@selderee/plugin-htmlparser2": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
- "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
- "license": "MIT",
- "dependencies": {
- "domhandler": "^5.0.3",
- "selderee": "^0.11.0"
- },
- "funding": {
- "url": "https://ko-fi.com/killymxi"
- }
- },
- "node_modules/@sinclair/typebox": {
- "version": "0.34.41",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
- "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@testing-library/dom": {
- "version": "10.4.1",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
- "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@babel/code-frame": "^7.10.4",
- "@babel/runtime": "^7.12.5",
- "@types/aria-query": "^5.0.1",
- "aria-query": "5.3.0",
- "dom-accessibility-api": "^0.5.9",
- "lz-string": "^1.5.0",
- "picocolors": "1.1.1",
- "pretty-format": "^27.0.2"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@testing-library/dom/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",
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@testing-library/dom/node_modules/ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/@testing-library/dom/node_modules/pretty-format": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
- "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "ansi-regex": "^5.0.1",
- "ansi-styles": "^5.0.0",
- "react-is": "^17.0.1"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
- "node_modules/@testing-library/dom/node_modules/react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
- "node_modules/@testing-library/react": {
- "version": "16.3.0",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz",
- "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.12.5"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@testing-library/dom": "^10.0.0",
- "@types/react": "^18.0.0 || ^19.0.0",
- "@types/react-dom": "^18.0.0 || ^19.0.0",
- "react": "^18.0.0 || ^19.0.0",
- "react-dom": "^18.0.0 || ^19.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/@types/aria-query": {
- "version": "5.0.4",
- "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
- "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
- "node_modules/@types/body-parser": {
- "version": "1.19.6",
- "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
- "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/connect": "*",
- "@types/node": "*"
- }
- },
- "node_modules/@types/braces": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.5.tgz",
- "integrity": "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/chai": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz",
- "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/deep-eql": "*"
- }
- },
- "node_modules/@types/command-exists": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@types/command-exists/-/command-exists-1.2.3.tgz",
- "integrity": "sha512-PpbaE2XWLaWYboXD6k70TcXO/OdOyyRFq5TVpmlUELNxdkkmXU9fkImNosmXU1DtsNrqdUgWd/nJQYXgwmtdXQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/configstore": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/@types/configstore/-/configstore-6.0.2.tgz",
- "integrity": "sha512-OS//b51j9uyR3zvwD04Kfs5kHpve2qalQ18JhY/ho3voGYUTPLEG90/ocfKPI48hyHH8T04f7KEEbK6Ue60oZQ==",
- "license": "MIT"
- },
- "node_modules/@types/connect": {
- "version": "3.4.38",
- "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
- "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/cookie": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
- "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/cors": {
- "version": "2.8.19",
- "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
- "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/deep-eql": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
- "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/diff": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz",
- "integrity": "sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/dotenv": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
- "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/estree": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
- "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/express": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz",
- "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/body-parser": "*",
- "@types/express-serve-static-core": "^5.0.0",
- "@types/serve-static": "*"
- }
- },
- "node_modules/@types/express-serve-static-core": {
- "version": "5.0.7",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz",
- "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*",
- "@types/qs": "*",
- "@types/range-parser": "*",
- "@types/send": "*"
- }
- },
- "node_modules/@types/glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==",
- "license": "MIT",
- "dependencies": {
- "@types/minimatch": "^5.1.2",
- "@types/node": "*"
- }
- },
- "node_modules/@types/gradient-string": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/@types/gradient-string/-/gradient-string-1.1.6.tgz",
- "integrity": "sha512-LkaYxluY4G5wR1M4AKQUal2q61Di1yVVCw42ImFTuaIoQVgmV0WP1xUaLB8zwb47mp82vWTpePI9JmrjEnJ7nQ==",
- "license": "MIT",
- "dependencies": {
- "@types/tinycolor2": "*"
- }
- },
- "node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
- "node_modules/@types/html-to-text": {
- "version": "9.0.4",
- "resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-9.0.4.tgz",
- "integrity": "sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==",
- "license": "MIT"
- },
- "node_modules/@types/http-errors": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
- "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/json5": {
- "version": "0.0.29",
- "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
- "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/marked": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz",
- "integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/micromatch": {
- "version": "4.0.9",
- "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.9.tgz",
- "integrity": "sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/braces": "*"
- }
- },
- "node_modules/@types/mime": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
- "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/mime-types": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-3.0.1.tgz",
- "integrity": "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/minimatch": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
- "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
- "license": "MIT"
- },
- "node_modules/@types/mock-fs": {
- "version": "4.13.4",
- "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz",
- "integrity": "sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/node": {
- "version": "24.3.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
- "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
- "license": "MIT",
- "dependencies": {
- "undici-types": "~7.10.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==",
- "license": "MIT"
- },
- "node_modules/@types/picomatch": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz",
- "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/qs": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
- "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/range-parser": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
- "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/react": {
- "version": "19.1.12",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz",
- "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==",
- "devOptional": true,
- "license": "MIT",
- "dependencies": {
- "csstype": "^3.0.2"
- }
- },
- "node_modules/@types/react-dom": {
- "version": "19.1.9",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz",
- "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "@types/react": "^19.0.0"
- }
- },
- "node_modules/@types/semver": {
- "version": "7.7.1",
- "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz",
- "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/send": {
- "version": "0.17.5",
- "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
- "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/mime": "^1",
- "@types/node": "*"
- }
- },
- "node_modules/@types/serve-static": {
- "version": "1.15.8",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
- "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/http-errors": "*",
- "@types/node": "*",
- "@types/send": "*"
- }
- },
- "node_modules/@types/shell-quote": {
- "version": "1.7.5",
- "resolved": "https://registry.npmjs.org/@types/shell-quote/-/shell-quote-1.7.5.tgz",
- "integrity": "sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/shimmer": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
- "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==",
- "license": "MIT"
- },
- "node_modules/@types/statuses": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz",
- "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/tinycolor2": {
- "version": "1.4.6",
- "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.6.tgz",
- "integrity": "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==",
- "license": "MIT"
- },
- "node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
- "node_modules/@types/update-notifier": {
- "version": "6.0.8",
- "resolved": "https://registry.npmjs.org/@types/update-notifier/-/update-notifier-6.0.8.tgz",
- "integrity": "sha512-IlDFnfSVfYQD+cKIg63DEXn3RFmd7W1iYtKQsJodcHK9R1yr8aKbKaPKfBxzPpcHCq2DU8zUq4PIPmy19Thjfg==",
- "license": "MIT",
- "dependencies": {
- "@types/configstore": "*",
- "boxen": "^7.1.1"
- }
- },
- "node_modules/@types/vscode": {
- "version": "1.103.0",
- "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.103.0.tgz",
- "integrity": "sha512-o4hanZAQdNfsKecexq9L3eHICd0AAvdbLk6hA60UzGXbGH/q8b/9xv2RgR7vV3ZcHuyKVq7b37IGd/+gM4Tu+Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/ws": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
- "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/yargs": {
- "version": "17.0.33",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
- "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@types/yargs-parser": {
- "version": "21.0.3",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
- "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz",
- "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/type-utils": "8.42.0",
- "@typescript-eslint/utils": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
- "graphemer": "^1.4.0",
- "ignore": "^7.0.0",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.1.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.42.0",
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin/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/@typescript-eslint/parser": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz",
- "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
- "debug": "^4.3.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",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz",
- "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.42.0",
- "@typescript-eslint/types": "^8.42.0",
- "debug": "^4.3.4"
- },
- "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.0.0"
- }
- },
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz",
- "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.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/tsconfig-utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz",
- "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==",
- "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.0.0"
- }
- },
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz",
- "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0",
- "@typescript-eslint/utils": "8.42.0",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.1.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",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz",
- "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==",
- "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.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz",
- "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.42.0",
- "@typescript-eslint/tsconfig-utils": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.1.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.0.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz",
- "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.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",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz",
- "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.42.0",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@vitest/coverage-v8": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz",
- "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@ampproject/remapping": "^2.3.0",
- "@bcoe/v8-coverage": "^1.0.2",
- "ast-v8-to-istanbul": "^0.3.3",
- "debug": "^4.4.1",
- "istanbul-lib-coverage": "^3.2.2",
- "istanbul-lib-report": "^3.0.1",
- "istanbul-lib-source-maps": "^5.0.6",
- "istanbul-reports": "^3.1.7",
- "magic-string": "^0.30.17",
- "magicast": "^0.3.5",
- "std-env": "^3.9.0",
- "test-exclude": "^7.0.1",
- "tinyrainbow": "^2.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- },
- "peerDependencies": {
- "@vitest/browser": "3.2.4",
- "vitest": "3.2.4"
- },
- "peerDependenciesMeta": {
- "@vitest/browser": {
- "optional": true
- }
- }
- },
- "node_modules/@vitest/expect": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
- "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/chai": "^5.2.2",
- "@vitest/spy": "3.2.4",
- "@vitest/utils": "3.2.4",
- "chai": "^5.2.0",
- "tinyrainbow": "^2.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/mocker": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz",
- "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/spy": "3.2.4",
- "estree-walker": "^3.0.3",
- "magic-string": "^0.30.17"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- },
- "peerDependencies": {
- "msw": "^2.4.9",
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
- },
- "peerDependenciesMeta": {
- "msw": {
- "optional": true
- },
- "vite": {
- "optional": true
- }
- }
- },
- "node_modules/@vitest/pretty-format": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
- "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tinyrainbow": "^2.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/runner": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz",
- "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/utils": "3.2.4",
- "pathe": "^2.0.3",
- "strip-literal": "^3.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/snapshot": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz",
- "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/pretty-format": "3.2.4",
- "magic-string": "^0.30.17",
- "pathe": "^2.0.3"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/spy": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
- "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tinyspy": "^4.0.3"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/utils": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
- "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/pretty-format": "3.2.4",
- "loupe": "^3.1.4",
- "tinyrainbow": "^2.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/accepts": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
- "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
- "license": "MIT",
- "dependencies": {
- "mime-types": "^3.0.0",
- "negotiator": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
- "license": "MIT",
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-import-attributes": {
- "version": "1.9.5",
- "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
- "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
- "license": "MIT",
- "peerDependencies": {
- "acorn": "^8"
- }
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "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/ansi-align": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
- "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.1.0"
- }
- },
- "node_modules/ansi-align/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-align/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/ansi-align/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-align/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-escapes": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "type-fest": "^0.21.3"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ansi-escapes/node_modules/type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ansi-regex": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
- "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
- "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==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/anus-vscode-ide-companion": {
- "resolved": "packages/vscode-ide-companion",
- "link": true
- },
- "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/aria-query": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
- "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
- "dev": true,
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "dequal": "^2.0.3"
- }
- },
- "node_modules/array-buffer-byte-length": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
- "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "is-array-buffer": "^3.0.5"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array-includes": {
- "version": "3.1.9",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
- "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.24.0",
- "es-object-atoms": "^1.1.1",
- "get-intrinsic": "^1.3.0",
- "is-string": "^1.1.1",
- "math-intrinsics": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.findlast": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
- "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "es-shim-unscopables": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.findlastindex": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
- "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.9",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "es-shim-unscopables": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.flat": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
- "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-shim-unscopables": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.flatmap": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
- "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-shim-unscopables": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/array.prototype.tosorted": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
- "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.3",
- "es-errors": "^1.3.0",
- "es-shim-unscopables": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/arraybuffer.prototype.slice": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
- "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-buffer-byte-length": "^1.0.1",
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6",
- "is-array-buffer": "^3.0.4"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/ast-v8-to-istanbul": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.5.tgz",
- "integrity": "sha512-9SdXjNheSiE8bALAQCQQuT6fgQaoxJh7IRYrRGZ8/9nv8WhJeC1aXAwN8TbaOssGOukUvyvnkgD9+Yuykvl1aA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.30",
- "estree-walker": "^3.0.3",
- "js-tokens": "^9.0.1"
- }
- },
- "node_modules/async-function": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
- "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/atomically": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.3.tgz",
- "integrity": "sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==",
- "dependencies": {
- "stubborn-fs": "^1.2.5",
- "when-exit": "^2.1.1"
- }
- },
- "node_modules/auto-bind": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz",
- "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==",
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/available-typed-arrays": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
- "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "possible-typed-array-names": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "license": "MIT"
- },
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/bignumber.js": {
- "version": "9.3.1",
- "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
- "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/body-parser": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
- "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
- "license": "MIT",
- "dependencies": {
- "bytes": "^3.1.2",
- "content-type": "^1.0.5",
- "debug": "^4.4.0",
- "http-errors": "^2.0.0",
- "iconv-lite": "^0.6.3",
- "on-finished": "^2.4.1",
- "qs": "^6.14.0",
- "raw-body": "^3.0.0",
- "type-is": "^2.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/boxen": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz",
- "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==",
- "license": "MIT",
- "dependencies": {
- "ansi-align": "^3.0.1",
- "camelcase": "^7.0.1",
- "chalk": "^5.2.0",
- "cli-boxes": "^3.0.0",
- "string-width": "^5.1.2",
- "type-fest": "^2.13.0",
- "widest-line": "^4.0.1",
- "wrap-ansi": "^8.1.0"
- },
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/boxen/node_modules/chalk": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
- "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
- "license": "MIT",
- "engines": {
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/boxen/node_modules/type-fest": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
- "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=12.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "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==",
- "license": "MIT",
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "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==",
- "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==",
- "license": "MIT",
- "dependencies": {
- "run-applescript": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/cac": {
- "version": "6.7.14",
- "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
- "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/call-bind": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
- "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.0",
- "es-define-property": "^1.0.0",
- "get-intrinsic": "^1.2.4",
- "set-function-length": "^1.2.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/call-bind-apply-helpers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/call-bound": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
- "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "get-intrinsic": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/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": "7.0.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
- "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==",
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cfonts": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/cfonts/-/cfonts-3.3.0.tgz",
- "integrity": "sha512-RlVxeEw2FXWI5Bs9LD0/Ef3bsQIc9m6lK/DINN20HIW0Y0YHUO2jjy88cot9YKZITiRTCdWzTfLmTyx47HeSLA==",
- "license": "GPL-3.0-or-later",
- "dependencies": {
- "supports-color": "^8",
- "window-size": "^1"
- },
- "bin": {
- "cfonts": "bin/index.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/chai": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
- "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assertion-error": "^2.0.1",
- "check-error": "^2.1.1",
- "deep-eql": "^5.0.1",
- "loupe": "^3.1.0",
- "pathval": "^2.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "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/chalk/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==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/chardet": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz",
- "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==",
- "license": "MIT"
- },
- "node_modules/check-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 16"
- }
- },
- "node_modules/cjs-module-lexer": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
- "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
- "license": "MIT"
- },
- "node_modules/cli-boxes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
- "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cli-cursor": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
- "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
- "license": "MIT",
- "dependencies": {
- "restore-cursor": "^4.0.0"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cli-truncate": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
- "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
- "license": "MIT",
- "dependencies": {
- "slice-ansi": "^5.0.0",
- "string-width": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cli-truncate/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/cli-truncate/node_modules/emoji-regex": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz",
- "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==",
- "license": "MIT"
- },
- "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
- "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cli-truncate/node_modules/slice-ansi": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
- "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.0.0",
- "is-fullwidth-code-point": "^4.0.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/slice-ansi?sponsor=1"
- }
- },
- "node_modules/cli-truncate/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==",
- "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/cli-width": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
- "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cliui/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/cliui/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "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==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cliui/node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "license": "MIT",
- "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/code-excerpt": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz",
- "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==",
- "license": "MIT",
- "dependencies": {
- "convert-to-spaces": "^2.0.1"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "license": "MIT"
- },
- "node_modules/command-exists": {
- "version": "1.2.9",
- "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
- "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==",
- "license": "MIT"
- },
- "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/concurrently": {
- "version": "9.2.1",
- "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
- "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chalk": "4.1.2",
- "rxjs": "7.8.2",
- "shell-quote": "1.8.3",
- "supports-color": "8.1.1",
- "tree-kill": "1.2.2",
- "yargs": "17.7.2"
- },
- "bin": {
- "conc": "dist/bin/concurrently.js",
- "concurrently": "dist/bin/concurrently.js"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
- }
- },
- "node_modules/config-chain": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
- "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
- "license": "MIT",
- "dependencies": {
- "ini": "^1.3.4",
- "proto-list": "~1.2.1"
- }
- },
- "node_modules/config-chain/node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "license": "ISC"
- },
- "node_modules/configstore": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.0.0.tgz",
- "integrity": "sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "atomically": "^2.0.3",
- "dot-prop": "^9.0.0",
- "graceful-fs": "^4.2.11",
- "xdg-basedir": "^5.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/yeoman/configstore?sponsor=1"
- }
- },
- "node_modules/content-disposition": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
- "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "5.2.1"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/content-type": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/convert-to-spaces": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz",
- "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==",
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- }
- },
- "node_modules/cookie": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
- "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/cookie-signature": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
- "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
- "license": "MIT",
- "engines": {
- "node": ">=6.6.0"
- }
- },
- "node_modules/cors": {
- "version": "2.8.5",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
- "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
- "license": "MIT",
- "dependencies": {
- "object-assign": "^4",
- "vary": "^1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/cross-env": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
- "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cross-spawn": "^7.0.1"
- },
- "bin": {
- "cross-env": "src/bin/cross-env.js",
- "cross-env-shell": "src/bin/cross-env-shell.js"
- },
- "engines": {
- "node": ">=10.14",
- "npm": ">=6",
- "yarn": ">=1"
- }
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/cssstyle": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
- "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@asamuzakjp/css-color": "^3.2.0",
- "rrweb-cssom": "^0.8.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "devOptional": true,
- "license": "MIT"
- },
- "node_modules/data-urls": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
- "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "whatwg-mimetype": "^4.0.0",
- "whatwg-url": "^14.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/data-urls/node_modules/tr46": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
- "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "punycode": "^2.3.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/data-urls/node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/data-urls/node_modules/whatwg-url": {
- "version": "14.2.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
- "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tr46": "^5.1.0",
- "webidl-conversions": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/data-view-buffer": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
- "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/data-view-byte-length": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
- "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/inspect-js"
- }
- },
- "node_modules/data-view-byte-offset": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
- "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "is-data-view": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/decimal.js": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
- "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/deep-eql": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/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/deepmerge": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/default-browser": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz",
- "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==",
- "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.0",
- "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz",
- "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/define-data-property": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/define-properties": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
- "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.0.1",
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/dequal": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/devlop": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
- "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
- "license": "MIT",
- "dependencies": {
- "dequal": "^2.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/diff": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
- "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/doctrine": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "esutils": "^2.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/dom-accessibility-api": {
- "version": "0.5.16",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
- "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
- "node_modules/dom-serializer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
- "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.2",
- "entities": "^4.2.0"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
- }
- },
- "node_modules/domelementtype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "license": "BSD-2-Clause"
- },
- "node_modules/domhandler": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
- "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "domelementtype": "^2.3.0"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "node_modules/domutils": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
- "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "dom-serializer": "^2.0.0",
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3"
- },
- "funding": {
- "url": "https://github.com/fb55/domutils?sponsor=1"
- }
- },
- "node_modules/dot-prop": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
- "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==",
- "license": "MIT",
- "dependencies": {
- "type-fest": "^4.18.2"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/dotenv": {
- "version": "17.2.2",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz",
- "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://dotenvx.com"
- }
- },
- "node_modules/dunder-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "es-errors": "^1.3.0",
- "gopd": "^1.2.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "license": "MIT"
- },
- "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==",
- "license": "Apache-2.0",
- "dependencies": {
- "safe-buffer": "^5.0.1"
- }
- },
- "node_modules/ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
- "license": "MIT"
- },
- "node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "license": "MIT"
- },
- "node_modules/encodeurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
- "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/environment": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
- "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
- "node_modules/es-abstract": {
- "version": "1.24.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
- "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-buffer-byte-length": "^1.0.2",
- "arraybuffer.prototype.slice": "^1.0.4",
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "data-view-buffer": "^1.0.2",
- "data-view-byte-length": "^1.0.2",
- "data-view-byte-offset": "^1.0.1",
- "es-define-property": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "es-set-tostringtag": "^2.1.0",
- "es-to-primitive": "^1.3.0",
- "function.prototype.name": "^1.1.8",
- "get-intrinsic": "^1.3.0",
- "get-proto": "^1.0.1",
- "get-symbol-description": "^1.1.0",
- "globalthis": "^1.0.4",
- "gopd": "^1.2.0",
- "has-property-descriptors": "^1.0.2",
- "has-proto": "^1.2.0",
- "has-symbols": "^1.1.0",
- "hasown": "^2.0.2",
- "internal-slot": "^1.1.0",
- "is-array-buffer": "^3.0.5",
- "is-callable": "^1.2.7",
- "is-data-view": "^1.0.2",
- "is-negative-zero": "^2.0.3",
- "is-regex": "^1.2.1",
- "is-set": "^2.0.3",
- "is-shared-array-buffer": "^1.0.4",
- "is-string": "^1.1.1",
- "is-typed-array": "^1.1.15",
- "is-weakref": "^1.1.1",
- "math-intrinsics": "^1.1.0",
- "object-inspect": "^1.13.4",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.7",
- "own-keys": "^1.0.1",
- "regexp.prototype.flags": "^1.5.4",
- "safe-array-concat": "^1.1.3",
- "safe-push-apply": "^1.0.0",
- "safe-regex-test": "^1.1.0",
- "set-proto": "^1.0.0",
- "stop-iteration-iterator": "^1.1.0",
- "string.prototype.trim": "^1.2.10",
- "string.prototype.trimend": "^1.0.9",
- "string.prototype.trimstart": "^1.0.8",
- "typed-array-buffer": "^1.0.3",
- "typed-array-byte-length": "^1.0.3",
- "typed-array-byte-offset": "^1.0.4",
- "typed-array-length": "^1.0.7",
- "unbox-primitive": "^1.1.0",
- "which-typed-array": "^1.1.19"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/es-define-property": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-iterator-helpers": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
- "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.6",
- "es-errors": "^1.3.0",
- "es-set-tostringtag": "^2.0.3",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.6",
- "globalthis": "^1.0.4",
- "gopd": "^1.2.0",
- "has-property-descriptors": "^1.0.2",
- "has-proto": "^1.2.0",
- "has-symbols": "^1.1.0",
- "internal-slot": "^1.1.0",
- "iterator.prototype": "^1.1.4",
- "safe-array-concat": "^1.1.3"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-module-lexer": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/es-object-atoms": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/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/es-shim-unscopables": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
- "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-to-primitive": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
- "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-callable": "^1.2.7",
- "is-date-object": "^1.0.5",
- "is-symbol": "^1.0.4"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/es-toolkit": {
- "version": "1.39.10",
- "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz",
- "integrity": "sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==",
- "license": "MIT",
- "workspaces": [
- "docs",
- "benchmarks"
- ]
- },
- "node_modules/esbuild": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
- "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.9",
- "@esbuild/android-arm": "0.25.9",
- "@esbuild/android-arm64": "0.25.9",
- "@esbuild/android-x64": "0.25.9",
- "@esbuild/darwin-arm64": "0.25.9",
- "@esbuild/darwin-x64": "0.25.9",
- "@esbuild/freebsd-arm64": "0.25.9",
- "@esbuild/freebsd-x64": "0.25.9",
- "@esbuild/linux-arm": "0.25.9",
- "@esbuild/linux-arm64": "0.25.9",
- "@esbuild/linux-ia32": "0.25.9",
- "@esbuild/linux-loong64": "0.25.9",
- "@esbuild/linux-mips64el": "0.25.9",
- "@esbuild/linux-ppc64": "0.25.9",
- "@esbuild/linux-riscv64": "0.25.9",
- "@esbuild/linux-s390x": "0.25.9",
- "@esbuild/linux-x64": "0.25.9",
- "@esbuild/netbsd-arm64": "0.25.9",
- "@esbuild/netbsd-x64": "0.25.9",
- "@esbuild/openbsd-arm64": "0.25.9",
- "@esbuild/openbsd-x64": "0.25.9",
- "@esbuild/openharmony-arm64": "0.25.9",
- "@esbuild/sunos-x64": "0.25.9",
- "@esbuild/win32-arm64": "0.25.9",
- "@esbuild/win32-ia32": "0.25.9",
- "@esbuild/win32-x64": "0.25.9"
- }
- },
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-goat": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
- "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
- "license": "MIT"
- },
- "node_modules/escape-string-regexp": {
- "version": "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": "9.34.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz",
- "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.1",
- "@eslint/core": "^0.15.2",
- "@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.34.0",
- "@eslint/plugin-kit": "^0.3.5",
- "@humanfs/node": "^0.16.6",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.2",
- "@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.6",
- "debug": "^4.3.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.4.0",
- "eslint-visitor-keys": "^4.2.1",
- "espree": "^10.4.0",
- "esquery": "^1.5.0",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^8.0.0",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- },
- "peerDependencies": {
- "jiti": "*"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-config-prettier": {
- "version": "10.1.8",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
- "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "eslint-config-prettier": "bin/cli.js"
- },
- "funding": {
- "url": "https://opencollective.com/eslint-config-prettier"
- },
- "peerDependencies": {
- "eslint": ">=7.0.0"
- }
- },
- "node_modules/eslint-import-resolver-node": {
- "version": "0.3.9",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
- "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^3.2.7",
- "is-core-module": "^2.13.0",
- "resolve": "^1.22.4"
- }
- },
- "node_modules/eslint-import-resolver-node/node_modules/debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.1"
- }
- },
- "node_modules/eslint-module-utils": {
- "version": "2.12.1",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
- "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^3.2.7"
- },
- "engines": {
- "node": ">=4"
- },
- "peerDependenciesMeta": {
- "eslint": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-module-utils/node_modules/debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.1"
- }
- },
- "node_modules/eslint-plugin-import": {
- "version": "2.32.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
- "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@rtsao/scc": "^1.1.0",
- "array-includes": "^3.1.9",
- "array.prototype.findlastindex": "^1.2.6",
- "array.prototype.flat": "^1.3.3",
- "array.prototype.flatmap": "^1.3.3",
- "debug": "^3.2.7",
- "doctrine": "^2.1.0",
- "eslint-import-resolver-node": "^0.3.9",
- "eslint-module-utils": "^2.12.1",
- "hasown": "^2.0.2",
- "is-core-module": "^2.16.1",
- "is-glob": "^4.0.3",
- "minimatch": "^3.1.2",
- "object.fromentries": "^2.0.8",
- "object.groupby": "^1.0.3",
- "object.values": "^1.2.1",
- "semver": "^6.3.1",
- "string.prototype.trimend": "^1.0.9",
- "tsconfig-paths": "^3.15.0"
- },
- "engines": {
- "node": ">=4"
- },
- "peerDependencies": {
- "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
- }
- },
- "node_modules/eslint-plugin-import/node_modules/debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.1"
- }
- },
- "node_modules/eslint-plugin-react": {
- "version": "7.37.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
- "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-includes": "^3.1.8",
- "array.prototype.findlast": "^1.2.5",
- "array.prototype.flatmap": "^1.3.3",
- "array.prototype.tosorted": "^1.1.4",
- "doctrine": "^2.1.0",
- "es-iterator-helpers": "^1.2.1",
- "estraverse": "^5.3.0",
- "hasown": "^2.0.2",
- "jsx-ast-utils": "^2.4.1 || ^3.0.0",
- "minimatch": "^3.1.2",
- "object.entries": "^1.1.9",
- "object.fromentries": "^2.0.8",
- "object.values": "^1.2.1",
- "prop-types": "^15.8.1",
- "resolve": "^2.0.0-next.5",
- "semver": "^6.3.1",
- "string.prototype.matchall": "^4.0.12",
- "string.prototype.repeat": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- },
- "peerDependencies": {
- "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
- }
- },
- "node_modules/eslint-plugin-react-hooks": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
- "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
- }
- },
- "node_modules/eslint-plugin-react/node_modules/resolve": {
- "version": "2.0.0-next.5",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
- "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/eslint-scope": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
- "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/espree": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
- "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.15.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
- "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/estree-walker": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "^1.0.0"
- }
- },
- "node_modules/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/etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/eventsource": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.0.0.tgz",
- "integrity": "sha512-fvIkb9qZzdMxgZrEQDyll+9oJsyaVvY92I2Re+qK0qEJ+w5s0X3dtz+M0VAPOjP1gtU3iqWyjQ0G3nvd5CLZ2g==",
- "license": "MIT",
- "dependencies": {
- "eventsource-parser": "^3.0.1"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/eventsource-parser": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
- "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==",
- "license": "MIT",
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/expect-type": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz",
- "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/express": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
- "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
- "license": "MIT",
- "dependencies": {
- "accepts": "^2.0.0",
- "body-parser": "^2.2.0",
- "content-disposition": "^1.0.0",
- "content-type": "^1.0.5",
- "cookie": "^0.7.1",
- "cookie-signature": "^1.2.1",
- "debug": "^4.4.0",
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "etag": "^1.8.1",
- "finalhandler": "^2.1.0",
- "fresh": "^2.0.0",
- "http-errors": "^2.0.0",
- "merge-descriptors": "^2.0.0",
- "mime-types": "^3.0.0",
- "on-finished": "^2.4.1",
- "once": "^1.4.0",
- "parseurl": "^1.3.3",
- "proxy-addr": "^2.0.7",
- "qs": "^6.14.0",
- "range-parser": "^1.2.1",
- "router": "^2.2.0",
- "send": "^1.1.0",
- "serve-static": "^2.2.0",
- "statuses": "^2.0.1",
- "type-is": "^2.0.1",
- "vary": "^1.1.2"
- },
- "engines": {
- "node": ">= 18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
- "license": "MIT"
- },
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "license": "MIT"
- },
- "node_modules/fast-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==",
- "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.0",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
- "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fastify"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/fastify"
- }
- ],
- "license": "BSD-3-Clause"
- },
- "node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
- "node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/figures": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
- "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==",
- "license": "MIT",
- "dependencies": {
- "is-unicode-supported": "^2.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/file-entry-cache": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
- "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "flat-cache": "^4.0.0"
- },
- "engines": {
- "node": ">=16.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==",
- "license": "MIT",
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/finalhandler": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
- "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
- "license": "MIT",
- "dependencies": {
- "debug": "^4.4.0",
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "on-finished": "^2.4.1",
- "parseurl": "^1.3.3",
- "statuses": "^2.0.1"
- },
- "engines": {
- "node": ">= 0.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/find-up-simple": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz",
- "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/flat-cache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
- "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.4"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/flatted": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
- "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/for-each": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
- "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-callable": "^1.2.7"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "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==",
- "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/forwarded": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/fresh": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
- "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/function.prototype.name": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
- "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "functions-have-names": "^1.2.3",
- "hasown": "^2.0.2",
- "is-callable": "^1.2.7"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/functions-have-names": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
- "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/fzf": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz",
- "integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/gaxios": {
- "version": "6.7.1",
- "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
- "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "extend": "^3.0.2",
- "https-proxy-agent": "^7.0.1",
- "is-stream": "^2.0.0",
- "node-fetch": "^2.6.9",
- "uuid": "^9.0.1"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/gcp-metadata": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
- "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
- "license": "Apache-2.0",
- "dependencies": {
- "gaxios": "^6.1.1",
- "google-logging-utils": "^0.0.2",
- "json-bigint": "^1.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "license": "ISC",
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/get-east-asian-width": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.1.tgz",
- "integrity": "sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==",
- "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==",
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "es-define-property": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "function-bind": "^1.1.2",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "has-symbols": "^1.1.0",
- "hasown": "^2.0.2",
- "math-intrinsics": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/get-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
- "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/get-symbol-description": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
- "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/get-tsconfig": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
- "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "resolve-pkg-maps": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
- }
- },
- "node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "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/glob-to-regex.js": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.0.1.tgz",
- "integrity": "sha512-CG/iEvgQqfzoVsMUbxSJcwbG2JwyZ3naEqPkeltwl0BSS8Bp83k3xlGms+0QdWFUAwV+uvo80wNswKF6FWEkKg==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=10.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "2"
- }
- },
- "node_modules/glob/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/glob/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/global-directory": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
- "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
- "license": "MIT",
- "dependencies": {
- "ini": "4.1.1"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/globals": {
- "version": "16.3.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
- "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/globalthis": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
- "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-properties": "^1.2.1",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/google-auth-library": {
- "version": "9.15.1",
- "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
- "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
- "license": "Apache-2.0",
- "dependencies": {
- "base64-js": "^1.3.0",
- "ecdsa-sig-formatter": "^1.0.11",
- "gaxios": "^6.1.1",
- "gcp-metadata": "^6.1.0",
- "gtoken": "^7.0.0",
- "jws": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/google-logging-utils": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
- "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/gopd": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/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==",
- "license": "ISC"
- },
- "node_modules/gradient-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/gradient-string/-/gradient-string-2.0.2.tgz",
- "integrity": "sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==",
- "license": "MIT",
- "dependencies": {
- "chalk": "^4.1.2",
- "tinygradient": "^1.1.5"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "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/graphql": {
- "version": "16.11.0",
- "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz",
- "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
- }
- },
- "node_modules/gtoken": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
- "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
- "license": "MIT",
- "dependencies": {
- "gaxios": "^6.0.0",
- "jws": "^4.0.0"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/has-bigints": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
- "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/has-property-descriptors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
- "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-proto": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
- "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-symbols": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/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.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "license": "MIT",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/headers-polyfill": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz",
- "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/highlight.js": {
- "version": "11.11.1",
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
- "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/hosted-git-info": {
- "version": "2.8.9",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/html-encoding-sniffer": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
- "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "whatwg-encoding": "^3.1.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/html-to-text": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
- "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
- "license": "MIT",
- "dependencies": {
- "@selderee/plugin-htmlparser2": "^0.11.0",
- "deepmerge": "^4.3.1",
- "dom-serializer": "^2.0.0",
- "htmlparser2": "^8.0.2",
- "selderee": "^0.11.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/htmlparser2": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
- "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
- "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.0.1",
- "entities": "^4.4.0"
- }
- },
- "node_modules/http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "license": "MIT",
- "dependencies": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/http-errors/node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "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==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/hyperdyperid": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
- "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10.18"
- }
- },
- "node_modules/iconv-lite": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/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/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/import-in-the-middle": {
- "version": "1.14.2",
- "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.2.tgz",
- "integrity": "sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==",
- "license": "Apache-2.0",
- "dependencies": {
- "acorn": "^8.14.0",
- "acorn-import-attributes": "^1.9.5",
- "cjs-module-lexer": "^1.2.2",
- "module-details-from-path": "^1.0.3"
- }
- },
- "node_modules/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/indent-string": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
- "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/index-to-position": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz",
- "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "license": "ISC"
- },
- "node_modules/ini": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
- "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
- "license": "ISC",
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
- "node_modules/ink": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/ink/-/ink-6.2.3.tgz",
- "integrity": "sha512-fQkfEJjKbLXIcVWEE3MvpYSnwtbbmRsmeNDNz1pIuOFlwE+UF2gsy228J36OXKZGWJWZJKUigphBSqCNMcARtg==",
- "license": "MIT",
- "dependencies": {
- "@alcalzone/ansi-tokenize": "^0.2.0",
- "ansi-escapes": "^7.0.0",
- "ansi-styles": "^6.2.1",
- "auto-bind": "^5.0.1",
- "chalk": "^5.6.0",
- "cli-boxes": "^3.0.0",
- "cli-cursor": "^4.0.0",
- "cli-truncate": "^4.0.0",
- "code-excerpt": "^4.0.0",
- "es-toolkit": "^1.39.10",
- "indent-string": "^5.0.0",
- "is-in-ci": "^2.0.0",
- "patch-console": "^2.0.0",
- "react-reconciler": "^0.32.0",
- "signal-exit": "^3.0.7",
- "slice-ansi": "^7.1.0",
- "stack-utils": "^2.0.6",
- "string-width": "^7.2.0",
- "type-fest": "^4.27.0",
- "widest-line": "^5.0.0",
- "wrap-ansi": "^9.0.0",
- "ws": "^8.18.0",
- "yoga-layout": "~3.2.1"
- },
- "engines": {
- "node": ">=20"
- },
- "peerDependencies": {
- "@types/react": ">=19.0.0",
- "react": ">=19.0.0",
- "react-devtools-core": "^4.19.1"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "react-devtools-core": {
- "optional": true
- }
- }
- },
- "node_modules/ink-big-text": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ink-big-text/-/ink-big-text-2.0.0.tgz",
- "integrity": "sha512-Juzqv+rIOLGuhMJiE50VtS6dg6olWfzFdL7wsU/EARSL5Eaa5JNXMogMBm9AkjgzO2Y3UwWCOh87jbhSn8aNdw==",
- "license": "MIT",
- "dependencies": {
- "cfonts": "^3.1.1",
- "prop-types": "^15.8.1"
- },
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- },
- "peerDependencies": {
- "ink": ">=4",
- "react": ">=18"
- }
- },
- "node_modules/ink-gradient": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ink-gradient/-/ink-gradient-3.0.0.tgz",
- "integrity": "sha512-OVyPBovBxE1tFcBhSamb+P1puqDP6pG3xFe2W9NiLgwUZd9RbcjBeR7twLbliUT9navrUstEf1ZcPKKvx71BsQ==",
- "license": "MIT",
- "dependencies": {
- "@types/gradient-string": "^1.1.2",
- "gradient-string": "^2.0.2",
- "prop-types": "^15.8.1",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- },
- "peerDependencies": {
- "ink": ">=4"
- }
- },
- "node_modules/ink-link": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ink-link/-/ink-link-4.1.0.tgz",
- "integrity": "sha512-3nNyJXum0FJIKAXBK8qat2jEOM41nJ1J60NRivwgK9Xh92R5UMN/k4vbz0A9xFzhJVrlf4BQEmmxMgXkCE1Jeg==",
- "license": "MIT",
- "dependencies": {
- "prop-types": "^15.8.1",
- "terminal-link": "^3.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- },
- "peerDependencies": {
- "ink": ">=4"
- }
- },
- "node_modules/ink-select-input": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/ink-select-input/-/ink-select-input-6.2.0.tgz",
- "integrity": "sha512-304fZXxkpYxJ9si5lxRCaX01GNlmPBgOZumXXRnPYbHW/iI31cgQynqk2tRypGLOF1cMIwPUzL2LSm6q4I5rQQ==",
- "license": "MIT",
- "dependencies": {
- "figures": "^6.1.0",
- "to-rotated": "^1.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "ink": ">=5.0.0",
- "react": ">=18.0.0"
- }
- },
- "node_modules/ink-spinner": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ink-spinner/-/ink-spinner-5.0.0.tgz",
- "integrity": "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==",
- "license": "MIT",
- "dependencies": {
- "cli-spinners": "^2.7.0"
- },
- "engines": {
- "node": ">=14.16"
- },
- "peerDependencies": {
- "ink": ">=4.0.0",
- "react": ">=18.0.0"
- }
- },
- "node_modules/ink-testing-library": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/ink-testing-library/-/ink-testing-library-4.0.0.tgz",
- "integrity": "sha512-yF92kj3pmBvk7oKbSq5vEALO//o7Z9Ck/OaLNlkzXNeYdwfpxMQkSowGTFUCS5MSu9bWfSZMewGpp7bFc66D7Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/react": ">=18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/ink/node_modules/ansi-escapes": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
- "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
- "license": "MIT",
- "dependencies": {
- "environment": "^1.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ink/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/ink/node_modules/chalk": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
- "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
- "license": "MIT",
- "engines": {
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/ink/node_modules/emoji-regex": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz",
- "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==",
- "license": "MIT"
- },
- "node_modules/ink/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "license": "ISC"
- },
- "node_modules/ink/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==",
- "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/ink/node_modules/widest-line": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
- "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==",
- "license": "MIT",
- "dependencies": {
- "string-width": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ink/node_modules/wrap-ansi": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
- "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/ink/node_modules/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
- "node_modules/internal-slot": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
- "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "hasown": "^2.0.2",
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/is-accessor-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz",
- "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==",
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/is-array-buffer": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
- "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/is-async-function": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
- "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "async-function": "^1.0.0",
- "call-bound": "^1.0.3",
- "get-proto": "^1.0.1",
- "has-tostringtag": "^1.0.2",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-bigint": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
- "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-bigints": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-boolean-object": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
- "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
- "license": "MIT"
- },
- "node_modules/is-callable": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
- "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-core-module": {
- "version": "2.16.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
- "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-data-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz",
- "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==",
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/is-data-view": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
- "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "get-intrinsic": "^1.2.6",
- "is-typed-array": "^1.1.13"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-date-object": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
- "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-descriptor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
- "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "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==",
- "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-finalizationregistry": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
- "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-generator-function": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
- "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "get-proto": "^1.0.0",
- "has-tostringtag": "^1.0.2",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "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-in-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-2.0.0.tgz",
- "integrity": "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==",
- "license": "MIT",
- "bin": {
- "is-in-ci": "cli.js"
- },
- "engines": {
- "node": ">=20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "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==",
- "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-installed-globally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz",
- "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==",
- "license": "MIT",
- "dependencies": {
- "global-directory": "^4.0.1",
- "is-path-inside": "^4.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-map": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
- "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-negative-zero": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
- "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-node-process": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz",
- "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/is-npm": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz",
- "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==",
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-number-object": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
- "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-path-inside": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz",
- "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-potential-custom-element-name": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
- "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/is-promise": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
- "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
- "license": "MIT"
- },
- "node_modules/is-regex": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
- "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "gopd": "^1.2.0",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-set": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
- "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-shared-array-buffer": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
- "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-string": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
- "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-tostringtag": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-symbol": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
- "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "has-symbols": "^1.1.0",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-typed-array": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
- "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "which-typed-array": "^1.1.16"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-weakmap": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
- "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-weakref": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
- "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-weakset": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
- "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-wsl": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
- "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
- "license": "MIT",
- "dependencies": {
- "is-inside-container": "^1.0.0"
- },
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
- "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==",
- "license": "ISC"
- },
- "node_modules/istanbul-lib-coverage": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
- "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-report": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
- "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^4.0.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-report/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/istanbul-lib-source-maps": {
- "version": "5.0.6",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
- "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.23",
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-reports": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
- "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/iterator.prototype": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
- "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.6",
- "get-proto": "^1.0.0",
- "has-symbols": "^1.1.0",
- "set-function-name": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/jackspeak": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
- "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/cliui": "^8.0.2"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- },
- "optionalDependencies": {
- "@pkgjs/parseargs": "^0.11.0"
- }
- },
- "node_modules/js-tokens": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
- "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/jsdom": {
- "version": "26.1.0",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
- "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cssstyle": "^4.2.1",
- "data-urls": "^5.0.0",
- "decimal.js": "^10.5.0",
- "html-encoding-sniffer": "^4.0.0",
- "http-proxy-agent": "^7.0.2",
- "https-proxy-agent": "^7.0.6",
- "is-potential-custom-element-name": "^1.0.1",
- "nwsapi": "^2.2.16",
- "parse5": "^7.2.1",
- "rrweb-cssom": "^0.8.0",
- "saxes": "^6.0.0",
- "symbol-tree": "^3.2.4",
- "tough-cookie": "^5.1.1",
- "w3c-xmlserializer": "^5.0.0",
- "webidl-conversions": "^7.0.0",
- "whatwg-encoding": "^3.1.1",
- "whatwg-mimetype": "^4.0.0",
- "whatwg-url": "^14.1.1",
- "ws": "^8.18.0",
- "xml-name-validator": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "canvas": "^3.0.0"
- },
- "peerDependenciesMeta": {
- "canvas": {
- "optional": true
- }
- }
- },
- "node_modules/jsdom/node_modules/tldts": {
- "version": "6.1.86",
- "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
- "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tldts-core": "^6.1.86"
- },
- "bin": {
- "tldts": "bin/cli.js"
- }
- },
- "node_modules/jsdom/node_modules/tldts-core": {
- "version": "6.1.86",
- "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
- "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/jsdom/node_modules/tough-cookie": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
- "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "tldts": "^6.1.32"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/jsdom/node_modules/tr46": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
- "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "punycode": "^2.3.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/jsdom/node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/jsdom/node_modules/whatwg-url": {
- "version": "14.2.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
- "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tr46": "^5.1.0",
- "webidl-conversions": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/jsdom/node_modules/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
- "node_modules/json": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/json/-/json-11.0.0.tgz",
- "integrity": "sha512-N/ITv3Yw9Za8cGxuQqSqrq6RHnlaHWZkAFavcfpH/R52522c26EbihMxnY7A1chxfXJ4d+cEFIsyTgfi9GihrA==",
- "dev": true,
- "bin": {
- "json": "lib/json.js"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/json-bigint": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
- "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
- "license": "MIT",
- "dependencies": {
- "bignumber.js": "^9.0.0"
- }
- },
- "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-parse-better-errors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
- "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
- "dev": true,
- "license": "MIT"
- },
- "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==",
- "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": "1.0.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
- "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.0"
- },
- "bin": {
- "json5": "lib/cli.js"
- }
- },
- "node_modules/jsx-ast-utils": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
- "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-includes": "^3.1.6",
- "array.prototype.flat": "^1.3.1",
- "object.assign": "^4.1.4",
- "object.values": "^1.1.6"
- },
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/jwa": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
- "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
- "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.0",
- "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
- "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
- "license": "MIT",
- "dependencies": {
- "jwa": "^2.0.0",
- "safe-buffer": "^5.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/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ky": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ky/-/ky-1.9.1.tgz",
- "integrity": "sha512-WGzpBn57klhxsqRTEABAqF4tqTtqCuxoTIv9m6nIZtMMFTVcrHp7bRDWblzFIfqkb47+OhTztOgHn6A4xItmqg==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/ky?sponsor=1"
- }
- },
- "node_modules/latest-version": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-9.0.0.tgz",
- "integrity": "sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==",
- "license": "MIT",
- "dependencies": {
- "package-json": "^10.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/leac": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
- "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==",
- "license": "MIT",
- "funding": {
- "url": "https://ko-fi.com/killymxi"
- }
- },
- "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/load-json-file": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
- "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^4.0.0",
- "pify": "^3.0.0",
- "strip-bom": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "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.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lodash.camelcase": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
- "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
- "license": "MIT"
- },
- "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==",
- "license": "MIT"
- },
- "node_modules/long": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
- "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
- "license": "Apache-2.0"
- },
- "node_modules/loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "license": "MIT",
- "dependencies": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- },
- "bin": {
- "loose-envify": "cli.js"
- }
- },
- "node_modules/loose-envify/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==",
- "license": "MIT"
- },
- "node_modules/loupe": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
- "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lowlight": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz",
- "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==",
- "license": "MIT",
- "dependencies": {
- "@types/hast": "^3.0.0",
- "devlop": "^1.0.0",
- "highlight.js": "~11.11.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "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==",
- "license": "ISC"
- },
- "node_modules/lz-string": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
- "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "bin": {
- "lz-string": "bin/bin.js"
- }
- },
- "node_modules/magic-string": {
- "version": "0.30.18",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
- "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.5"
- }
- },
- "node_modules/magicast": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz",
- "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.25.4",
- "@babel/types": "^7.25.4",
- "source-map-js": "^1.2.0"
- }
- },
- "node_modules/make-dir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
- "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.3"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-dir/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/marked": {
- "version": "15.0.12",
- "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
- "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
- "license": "MIT",
- "bin": {
- "marked": "bin/marked.js"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/math-intrinsics": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/media-typer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
- "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/memfs": {
- "version": "4.38.2",
- "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.38.2.tgz",
- "integrity": "sha512-FpWsVHpAkoSh/LfY1BgAl72BVd374ooMRtDi2VqzBycX4XEfvC0XKACCe0C9VRZoYq5viuoyTv6lYXZ/Q7TrLQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@jsonjoy.com/json-pack": "^1.11.0",
- "@jsonjoy.com/util": "^1.9.0",
- "glob-to-regex.js": "^1.0.1",
- "thingies": "^2.5.0",
- "tree-dump": "^1.0.3",
- "tslib": "^2.0.0"
- },
- "engines": {
- "node": ">= 4.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- }
- },
- "node_modules/memorystream": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
- "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
- "dev": true,
- "engines": {
- "node": ">= 0.10.0"
- }
- },
- "node_modules/merge-descriptors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
- "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/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==",
- "license": "MIT",
- "dependencies": {
- "braces": "^3.0.3",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/micromatch/node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/mime-db": {
- "version": "1.54.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
- "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
- "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
- "license": "MIT",
- "dependencies": {
- "mime-db": "^1.54.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/mnemonist": {
- "version": "0.40.3",
- "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.40.3.tgz",
- "integrity": "sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==",
- "license": "MIT",
- "dependencies": {
- "obliterator": "^2.0.4"
- }
- },
- "node_modules/mock-fs": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz",
- "integrity": "sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/module-details-from-path": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz",
- "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
- "license": "MIT"
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
- "node_modules/msw": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/msw/-/msw-2.11.1.tgz",
- "integrity": "sha512-dGSRx0AJmQVQfpGXTsAAq4JFdwdhOBdJ6sJS/jnN0ac3s0NZB6daacHF1z5Pefx+IejmvuiLWw260RlyQOf3sQ==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "@bundled-es-modules/cookie": "^2.0.1",
- "@bundled-es-modules/statuses": "^1.0.1",
- "@inquirer/confirm": "^5.0.0",
- "@mswjs/interceptors": "^0.39.1",
- "@open-draft/deferred-promise": "^2.2.0",
- "@open-draft/until": "^2.1.0",
- "@types/cookie": "^0.6.0",
- "@types/statuses": "^2.0.4",
- "graphql": "^16.8.1",
- "headers-polyfill": "^4.0.2",
- "is-node-process": "^1.2.0",
- "outvariant": "^1.4.3",
- "path-to-regexp": "^6.3.0",
- "picocolors": "^1.1.1",
- "strict-event-emitter": "^0.5.1",
- "tough-cookie": "^6.0.0",
- "type-fest": "^4.26.1",
- "yargs": "^17.7.2"
- },
- "bin": {
- "msw": "cli/index.js"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/mswjs"
- },
- "peerDependencies": {
- "typescript": ">= 4.8.x"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/mute-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
- "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/nanoid": {
- "version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/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/negotiator": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
- "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "license": "MIT",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/normalize-package-data": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
- "node_modules/normalize-package-data/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/npm-run-all": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
- "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "chalk": "^2.4.1",
- "cross-spawn": "^6.0.5",
- "memorystream": "^0.3.1",
- "minimatch": "^3.0.4",
- "pidtree": "^0.3.0",
- "read-pkg": "^3.0.0",
- "shell-quote": "^1.6.1",
- "string.prototype.padend": "^3.0.0"
- },
- "bin": {
- "npm-run-all": "bin/npm-run-all/index.js",
- "run-p": "bin/run-p/index.js",
- "run-s": "bin/run-s/index.js"
- },
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/npm-run-all/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/npm-run-all/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/npm-run-all/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/npm-run-all/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/npm-run-all/node_modules/cross-spawn": {
- "version": "6.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
- "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- },
- "engines": {
- "node": ">=4.8"
- }
- },
- "node_modules/npm-run-all/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/npm-run-all/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/npm-run-all/node_modules/path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/npm-run-all/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/npm-run-all/node_modules/shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/npm-run-all/node_modules/shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/npm-run-all/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/npm-run-all/node_modules/which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "which": "bin/which"
- }
- },
- "node_modules/nwsapi": {
- "version": "2.2.21",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz",
- "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-inspect": {
- "version": "1.13.4",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
- "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object.assign": {
- "version": "4.1.7",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
- "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0",
- "has-symbols": "^1.1.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/object.entries": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
- "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object.fromentries": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
- "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/object.groupby": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
- "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object.values": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
- "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/obliterator": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz",
- "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==",
- "license": "MIT"
- },
- "node_modules/on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "license": "MIT",
- "dependencies": {
- "ee-first": "1.1.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "license": "MIT",
- "dependencies": {
- "mimic-fn": "^2.1.0"
- },
- "engines": {
- "node": ">=6"
- },
- "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==",
- "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/outvariant": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz",
- "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/own-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
- "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-intrinsic": "^1.2.6",
- "object-keys": "^1.1.1",
- "safe-push-apply": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "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/package-json": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/package-json/-/package-json-10.0.1.tgz",
- "integrity": "sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==",
- "license": "MIT",
- "dependencies": {
- "ky": "^1.2.0",
- "registry-auth-token": "^5.0.2",
- "registry-url": "^6.0.1",
- "semver": "^7.6.0"
- },
- "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==",
- "license": "BlueOak-1.0.0"
- },
- "node_modules/package-json/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "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": "4.0.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
- "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "error-ex": "^1.3.1",
- "json-parse-better-errors": "^1.0.1"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "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/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/parseley": {
- "version": "0.12.1",
- "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz",
- "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==",
- "license": "MIT",
- "dependencies": {
- "leac": "^0.6.0",
- "peberminta": "^0.9.0"
- },
- "funding": {
- "url": "https://ko-fi.com/killymxi"
- }
- },
- "node_modules/parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/patch-console": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz",
- "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==",
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- }
- },
- "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-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "license": "MIT"
- },
- "node_modules/path-scurry": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
- "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "lru-cache": "^10.2.0",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
- },
- "engines": {
- "node": ">=16 || 14 >=14.18"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/path-to-regexp": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
- "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/path-type": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
- "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pify": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/pathe": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
- "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/pathval": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
- "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14.16"
- }
- },
- "node_modules/peberminta": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
- "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==",
- "license": "MIT",
- "funding": {
- "url": "https://ko-fi.com/killymxi"
- }
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "license": "ISC"
- },
- "node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/pidtree": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
- "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "pidtree": "bin/pidtree.js"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/pkce-challenge": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
- "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
- "license": "MIT",
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/possible-typed-array-names": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
- "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.11",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/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/prettier": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
- "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "prettier": "bin/prettier.cjs"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/pretty-format": {
- "version": "30.0.5",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz",
- "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jest/schemas": "30.0.5",
- "ansi-styles": "^5.2.0",
- "react-is": "^18.3.1"
- },
- "engines": {
- "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
- }
- },
- "node_modules/pretty-format/node_modules/ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/pretty-format/node_modules/react-is": {
- "version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
- "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "license": "MIT",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
- "node_modules/proto-list": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
- "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
- "license": "ISC"
- },
- "node_modules/protobufjs": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
- "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
- "hasInstallScript": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.2",
- "@protobufjs/base64": "^1.1.2",
- "@protobufjs/codegen": "^2.0.4",
- "@protobufjs/eventemitter": "^1.1.0",
- "@protobufjs/fetch": "^1.1.0",
- "@protobufjs/float": "^1.0.2",
- "@protobufjs/inquire": "^1.1.0",
- "@protobufjs/path": "^1.1.2",
- "@protobufjs/pool": "^1.1.0",
- "@protobufjs/utf8": "^1.1.0",
- "@types/node": ">=13.7.0",
- "long": "^5.0.0"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/proxy-addr": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
- "license": "MIT",
- "dependencies": {
- "forwarded": "0.2.0",
- "ipaddr.js": "1.9.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/pupa": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz",
- "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==",
- "license": "MIT",
- "dependencies": {
- "escape-goat": "^4.0.0"
- },
- "engines": {
- "node": ">=12.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/qs": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
- "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">=0.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/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/range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/raw-body": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz",
- "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==",
- "license": "MIT",
- "dependencies": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.7.0",
- "unpipe": "1.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/raw-body/node_modules/iconv-lite": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
- "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
- "dependencies": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "bin": {
- "rc": "cli.js"
- }
- },
- "node_modules/rc/node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "license": "ISC"
- },
- "node_modules/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==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/react": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
- "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/react-devtools-core": {
- "version": "4.28.5",
- "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.28.5.tgz",
- "integrity": "sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==",
- "devOptional": true,
- "license": "MIT",
- "dependencies": {
- "shell-quote": "^1.6.1",
- "ws": "^7"
- }
- },
- "node_modules/react-dom": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
- "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "scheduler": "^0.26.0"
- },
- "peerDependencies": {
- "react": "^19.1.1"
- }
- },
- "node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "license": "MIT"
- },
- "node_modules/react-reconciler": {
- "version": "0.32.0",
- "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.32.0.tgz",
- "integrity": "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==",
- "license": "MIT",
- "dependencies": {
- "scheduler": "^0.26.0"
- },
- "engines": {
- "node": ">=0.10.0"
- },
- "peerDependencies": {
- "react": "^19.1.0"
- }
- },
- "node_modules/read-package-up": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
- "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
- "license": "MIT",
- "dependencies": {
- "find-up-simple": "^1.0.0",
- "read-pkg": "^9.0.0",
- "type-fest": "^4.6.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/read-package-up/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==",
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^10.0.1"
- },
- "engines": {
- "node": "^16.14.0 || >=18.0.0"
- }
- },
- "node_modules/read-package-up/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==",
- "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/read-package-up/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==",
- "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/read-package-up/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==",
- "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-package-up/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/read-pkg": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
- "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "load-json-file": "^4.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/reflect.getprototypeof": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
- "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.9",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.7",
- "get-proto": "^1.0.1",
- "which-builtin-type": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/regexp.prototype.flags": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
- "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "define-properties": "^1.2.1",
- "es-errors": "^1.3.0",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "set-function-name": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/registry-auth-token": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz",
- "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==",
- "license": "MIT",
- "dependencies": {
- "@pnpm/npm-conf": "^2.1.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/registry-url": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz",
- "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==",
- "license": "MIT",
- "dependencies": {
- "rc": "1.2.8"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/require-in-the-middle": {
- "version": "7.5.2",
- "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz",
- "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==",
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.5",
- "module-details-from-path": "^1.0.3",
- "resolve": "^1.22.8"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/resolve": {
- "version": "1.22.10",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
- "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.16.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/resolve-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/resolve-pkg-maps": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
- "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
- }
- },
- "node_modules/restore-cursor": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
- "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
- "license": "MIT",
- "dependencies": {
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/restore-cursor/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "license": "ISC"
- },
- "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/rollup": {
- "version": "4.50.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz",
- "integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "1.0.8"
- },
- "bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
- },
- "optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.50.0",
- "@rollup/rollup-android-arm64": "4.50.0",
- "@rollup/rollup-darwin-arm64": "4.50.0",
- "@rollup/rollup-darwin-x64": "4.50.0",
- "@rollup/rollup-freebsd-arm64": "4.50.0",
- "@rollup/rollup-freebsd-x64": "4.50.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.50.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.50.0",
- "@rollup/rollup-linux-arm64-gnu": "4.50.0",
- "@rollup/rollup-linux-arm64-musl": "4.50.0",
- "@rollup/rollup-linux-loongarch64-gnu": "4.50.0",
- "@rollup/rollup-linux-ppc64-gnu": "4.50.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.50.0",
- "@rollup/rollup-linux-riscv64-musl": "4.50.0",
- "@rollup/rollup-linux-s390x-gnu": "4.50.0",
- "@rollup/rollup-linux-x64-gnu": "4.50.0",
- "@rollup/rollup-linux-x64-musl": "4.50.0",
- "@rollup/rollup-openharmony-arm64": "4.50.0",
- "@rollup/rollup-win32-arm64-msvc": "4.50.0",
- "@rollup/rollup-win32-ia32-msvc": "4.50.0",
- "@rollup/rollup-win32-x64-msvc": "4.50.0",
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/router": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
- "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
- "license": "MIT",
- "dependencies": {
- "debug": "^4.4.0",
- "depd": "^2.0.0",
- "is-promise": "^4.0.0",
- "parseurl": "^1.3.3",
- "path-to-regexp": "^8.0.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/router/node_modules/path-to-regexp": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
- "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/rrweb-cssom": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
- "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/run-applescript": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
- "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==",
- "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/rxjs": {
- "version": "7.8.2",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
- "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.1.0"
- }
- },
- "node_modules/safe-array-concat": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
- "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "get-intrinsic": "^1.2.6",
- "has-symbols": "^1.1.0",
- "isarray": "^2.0.5"
- },
- "engines": {
- "node": ">=0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/safe-push-apply": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
- "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "isarray": "^2.0.5"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/safe-regex-test": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
- "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "is-regex": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "license": "MIT"
- },
- "node_modules/saxes": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
- "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "xmlchars": "^2.2.0"
- },
- "engines": {
- "node": ">=v12.22.7"
- }
- },
- "node_modules/scheduler": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
- "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
- "license": "MIT"
- },
- "node_modules/selderee": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
- "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
- "license": "MIT",
- "dependencies": {
- "parseley": "^0.12.0"
- },
- "funding": {
- "url": "https://ko-fi.com/killymxi"
- }
- },
- "node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/send": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
- "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.5",
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "etag": "^1.8.1",
- "fresh": "^2.0.0",
- "http-errors": "^2.0.0",
- "mime-types": "^3.0.1",
- "ms": "^2.1.3",
- "on-finished": "^2.4.1",
- "range-parser": "^1.2.1",
- "statuses": "^2.0.1"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/serve-static": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
- "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
- "license": "MIT",
- "dependencies": {
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "parseurl": "^1.3.3",
- "send": "^1.2.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/set-function-length": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
- "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/set-function-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
- "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "functions-have-names": "^1.2.3",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/set-proto": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
- "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/setprototypeof": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
- "license": "ISC"
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shell-quote": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
- "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/shimmer": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
- "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==",
- "license": "BSD-2-Clause"
- },
- "node_modules/side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
- "side-channel-map": "^1.0.1",
- "side-channel-weakmap": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-list": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-weakmap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3",
- "side-channel-map": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/siginfo": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
- "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/simple-git": {
- "version": "3.28.0",
- "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz",
- "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==",
- "license": "MIT",
- "dependencies": {
- "@kwsites/file-exists": "^1.1.1",
- "@kwsites/promise-deferred": "^1.1.1",
- "debug": "^4.4.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/steveukx/git-js?sponsor=1"
- }
- },
- "node_modules/slice-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
- "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "is-fullwidth-code-point": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/slice-ansi?sponsor=1"
- }
- },
- "node_modules/slice-ansi/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
- "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
- "license": "MIT",
- "dependencies": {
- "get-east-asian-width": "^1.3.1"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/spdx-correct": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
- "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
- "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==",
- "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==",
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-license-ids": {
- "version": "3.0.22",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz",
- "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==",
- "license": "CC0-1.0"
- },
- "node_modules/stack-utils": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
- "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
- "license": "MIT",
- "dependencies": {
- "escape-string-regexp": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/stack-utils/node_modules/escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/stackback": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
- "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/statuses": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
- "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/std-env": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
- "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/stop-iteration-iterator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
- "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "internal-slot": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/strict-event-emitter": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
- "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "license": "MIT",
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/string-width-cjs": {
- "name": "string-width",
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "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-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/string-width-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string.prototype.matchall": {
- "version": "4.0.12",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
- "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.6",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.6",
- "gopd": "^1.2.0",
- "has-symbols": "^1.1.0",
- "internal-slot": "^1.1.0",
- "regexp.prototype.flags": "^1.5.3",
- "set-function-name": "^2.0.2",
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.padend": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz",
- "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.repeat": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
- "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
- }
- },
- "node_modules/string.prototype.trim": {
- "version": "1.2.10",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
- "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "define-data-property": "^1.1.4",
- "define-properties": "^1.2.1",
- "es-abstract": "^1.23.5",
- "es-object-atoms": "^1.0.0",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.trimend": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
- "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.2",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/string.prototype.trimstart": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
- "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/strip-ansi-cjs": {
- "name": "strip-ansi",
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-bom": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/strip-literal": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz",
- "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "js-tokens": "^9.0.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/antfu"
- }
- },
- "node_modules/stubborn-fs": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.5.tgz",
- "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g=="
- },
- "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==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
- "node_modules/supports-hyperlinks": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
- "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0",
- "supports-color": "^7.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/supports-hyperlinks/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==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/symbol-tree": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
- "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/terminal-link": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-3.0.0.tgz",
- "integrity": "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==",
- "license": "MIT",
- "dependencies": {
- "ansi-escapes": "^5.0.0",
- "supports-hyperlinks": "^2.2.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/terminal-link/node_modules/ansi-escapes": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz",
- "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==",
- "license": "MIT",
- "dependencies": {
- "type-fest": "^1.0.2"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/terminal-link/node_modules/type-fest": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
- "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/test-exclude": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz",
- "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^10.4.1",
- "minimatch": "^9.0.4"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/test-exclude/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/test-exclude/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/thingies": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz",
- "integrity": "sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "^2"
- }
- },
- "node_modules/tinybench": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
- "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/tinycolor2": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
- "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
- "license": "MIT"
- },
- "node_modules/tinyexec": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
- "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/tinyglobby": {
- "version": "0.2.14",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
- "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/SuperchupuDev"
- }
- },
- "node_modules/tinygradient": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/tinygradient/-/tinygradient-1.1.5.tgz",
- "integrity": "sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==",
- "license": "MIT",
- "dependencies": {
- "@types/tinycolor2": "^1.4.0",
- "tinycolor2": "^1.0.0"
- }
- },
- "node_modules/tinypool": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
- "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- }
- },
- "node_modules/tinyrainbow": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
- "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/tinyspy": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz",
- "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/tldts": {
- "version": "7.0.12",
- "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.12.tgz",
- "integrity": "sha512-M9ZQBPp6FyqhMcl233vHYyYRkxXOA1SKGlnq13S0mJdUhRSwr2w6I8rlchPL73wBwRlyIZpFvpu2VcdSMWLYXw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tldts-core": "^7.0.12"
- },
- "bin": {
- "tldts": "bin/cli.js"
- }
- },
- "node_modules/tldts-core": {
- "version": "7.0.12",
- "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.12.tgz",
- "integrity": "sha512-3K76aXywJFduGRsOYoY5JzINLs/WMlOkeDwPL+8OCPq2Rh39gkSDtWAxdJQlWjpun/xF/LHf29yqCi6VC/rHDA==",
- "dev": true,
- "license": "MIT"
- },
- "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==",
- "license": "MIT",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/to-rotated": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-rotated/-/to-rotated-1.0.0.tgz",
- "integrity": "sha512-KsEID8AfgUy+pxVRLsWp0VzCa69wxzUDZnzGbyIST/bcgcrMvTYoFBX/QORH4YApoD89EDuUovx4BTdpOn319Q==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/toidentifier": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.6"
- }
- },
- "node_modules/tough-cookie": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz",
- "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "tldts": "^7.0.5"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "license": "MIT"
- },
- "node_modules/tree-dump": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz",
- "integrity": "sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=10.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/streamich"
- },
- "peerDependencies": {
- "tslib": "2"
- }
- },
- "node_modules/tree-kill": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
- "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "tree-kill": "cli.js"
- }
- },
- "node_modules/ts-api-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
- "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18.12"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4"
- }
- },
- "node_modules/tsconfig-paths": {
- "version": "3.15.0",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
- "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/json5": "^0.0.29",
- "json5": "^1.0.2",
- "minimist": "^1.2.6",
- "strip-bom": "^3.0.0"
- }
- },
- "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/tsx": {
- "version": "4.20.5",
- "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz",
- "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "esbuild": "~0.25.0",
- "get-tsconfig": "^4.7.5"
- },
- "bin": {
- "tsx": "dist/cli.mjs"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- }
- },
- "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": "4.41.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
- "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/type-is": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
- "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
- "license": "MIT",
- "dependencies": {
- "content-type": "^1.0.5",
- "media-typer": "^1.1.0",
- "mime-types": "^3.0.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/typed-array-buffer": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
- "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "is-typed-array": "^1.1.14"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/typed-array-byte-length": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
- "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.8",
- "for-each": "^0.3.3",
- "gopd": "^1.2.0",
- "has-proto": "^1.2.0",
- "is-typed-array": "^1.1.14"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/typed-array-byte-offset": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
- "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "for-each": "^0.3.3",
- "gopd": "^1.2.0",
- "has-proto": "^1.2.0",
- "is-typed-array": "^1.1.15",
- "reflect.getprototypeof": "^1.0.9"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/typed-array-length": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
- "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.7",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "is-typed-array": "^1.1.13",
- "possible-typed-array-names": "^1.0.0",
- "reflect.getprototypeof": "^1.0.6"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/typescript": {
- "version": "5.9.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
- "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/node-http-handler": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz",
+ "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==",
"license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/typescript-eslint": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.42.0.tgz",
- "integrity": "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/eslint-plugin": "8.42.0",
- "@typescript-eslint/parser": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0",
- "@typescript-eslint/utils": "8.42.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",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/unbox-primitive": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
- "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "has-bigints": "^1.0.2",
- "has-symbols": "^1.1.0",
- "which-boxed-primitive": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/undici-types": {
- "version": "7.10.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
- "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
- "license": "MIT"
- },
- "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==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/update-notifier": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.3.1.tgz",
- "integrity": "sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "boxen": "^8.0.1",
- "chalk": "^5.3.0",
- "configstore": "^7.0.0",
- "is-in-ci": "^1.0.0",
- "is-installed-globally": "^1.0.0",
- "is-npm": "^6.0.0",
- "latest-version": "^9.0.0",
- "pupa": "^3.1.0",
- "semver": "^7.6.3",
- "xdg-basedir": "^5.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/yeoman/update-notifier?sponsor=1"
- }
- },
- "node_modules/update-notifier/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/update-notifier/node_modules/boxen": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz",
- "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==",
- "license": "MIT",
- "dependencies": {
- "ansi-align": "^3.0.1",
- "camelcase": "^8.0.0",
- "chalk": "^5.3.0",
- "cli-boxes": "^3.0.0",
- "string-width": "^7.2.0",
- "type-fest": "^4.21.0",
- "widest-line": "^5.0.0",
- "wrap-ansi": "^9.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/update-notifier/node_modules/camelcase": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
- "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
- "license": "MIT",
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/update-notifier/node_modules/chalk": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
- "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
- "license": "MIT",
- "engines": {
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/update-notifier/node_modules/emoji-regex": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz",
- "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==",
- "license": "MIT"
- },
- "node_modules/update-notifier/node_modules/is-in-ci": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz",
- "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==",
- "license": "MIT",
- "bin": {
- "is-in-ci": "cli.js"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/update-notifier/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/update-notifier/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==",
- "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/update-notifier/node_modules/widest-line": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
- "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==",
- "license": "MIT",
- "dependencies": {
- "string-width": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/update-notifier/node_modules/wrap-ansi": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
- "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
- "license": "MIT",
"dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.18.0",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/uri-js": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
- "node_modules/uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
+ "node": ">=18.0.0"
}
},
- "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==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/signature-v4": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.3.tgz",
+ "integrity": "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==",
"license": "Apache-2.0",
"dependencies": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
- "node_modules/vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/vite": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.4.tgz",
- "integrity": "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "esbuild": "^0.25.0",
- "fdir": "^6.5.0",
- "picomatch": "^4.0.3",
- "postcss": "^8.5.6",
- "rollup": "^4.43.0",
- "tinyglobby": "^0.2.14"
- },
- "bin": {
- "vite": "bin/vite.js"
- },
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- },
- "funding": {
- "url": "https://github.com/vitejs/vite?sponsor=1"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- },
- "peerDependencies": {
- "@types/node": "^20.19.0 || >=22.12.0",
- "jiti": ">=1.21.0",
- "less": "^4.0.0",
- "lightningcss": "^1.21.0",
- "sass": "^1.70.0",
- "sass-embedded": "^1.70.0",
- "stylus": ">=0.54.8",
- "sugarss": "^5.0.0",
- "terser": "^5.16.0",
- "tsx": "^4.8.1",
- "yaml": "^2.4.2"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "jiti": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "sass-embedded": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- },
- "tsx": {
- "optional": true
- },
- "yaml": {
- "optional": true
- }
- }
- },
- "node_modules/vite-node": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
- "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cac": "^6.7.14",
- "debug": "^4.4.1",
- "es-module-lexer": "^1.7.0",
- "pathe": "^2.0.3",
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
- },
- "bin": {
- "vite-node": "vite-node.mjs"
- },
- "engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/vitest": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
- "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/chai": "^5.2.2",
- "@vitest/expect": "3.2.4",
- "@vitest/mocker": "3.2.4",
- "@vitest/pretty-format": "^3.2.4",
- "@vitest/runner": "3.2.4",
- "@vitest/snapshot": "3.2.4",
- "@vitest/spy": "3.2.4",
- "@vitest/utils": "3.2.4",
- "chai": "^5.2.0",
- "debug": "^4.4.1",
- "expect-type": "^1.2.1",
- "magic-string": "^0.30.17",
- "pathe": "^2.0.3",
- "picomatch": "^4.0.2",
- "std-env": "^3.9.0",
- "tinybench": "^2.9.0",
- "tinyexec": "^0.3.2",
- "tinyglobby": "^0.2.14",
- "tinypool": "^1.1.1",
- "tinyrainbow": "^2.0.0",
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
- "vite-node": "3.2.4",
- "why-is-node-running": "^2.3.0"
- },
- "bin": {
- "vitest": "vitest.mjs"
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.17.2",
+ "tslib": "^2.6.2"
},
"engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- },
- "peerDependencies": {
- "@edge-runtime/vm": "*",
- "@types/debug": "^4.1.12",
- "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
- "@vitest/browser": "3.2.4",
- "@vitest/ui": "3.2.4",
- "happy-dom": "*",
- "jsdom": "*"
- },
- "peerDependenciesMeta": {
- "@edge-runtime/vm": {
- "optional": true
- },
- "@types/debug": {
- "optional": true
- },
- "@types/node": {
- "optional": true
- },
- "@vitest/browser": {
- "optional": true
- },
- "@vitest/ui": {
- "optional": true
- },
- "happy-dom": {
- "optional": true
- },
- "jsdom": {
- "optional": true
- }
+ "node": ">=18.0.0"
}
},
- "node_modules/w3c-xmlserializer": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
- "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
- "dev": true,
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/types": {
+ "version": "4.18.0",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.18.0.tgz",
+ "integrity": "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A==",
+ "license": "Apache-2.0",
"dependencies": {
- "xml-name-validator": "^5.0.0"
+ "tslib": "^2.6.2"
},
"engines": {
- "node": ">=18"
+ "node": ">=18.0.0"
}
},
- "node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "license": "BSD-2-Clause"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@stablelib/base64": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
+ "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
+ "license": "MIT"
},
- "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==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/@types/node": {
+ "version": "22.19.19",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz",
+ "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==",
"license": "MIT",
"dependencies": {
- "iconv-lite": "0.6.3"
- },
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
"engines": {
- "node": ">=18"
+ "node": ">= 14"
}
},
- "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,
+ "node_modules/@earendil-works/pi-coding-agent/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==",
"license": "MIT",
"engines": {
- "node": ">=18"
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
"license": "MIT",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
+ "engines": {
+ "node": "*"
}
},
- "node_modules/when-exit": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.4.tgz",
- "integrity": "sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/bowser": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz",
+ "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==",
"license": "MIT"
},
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "license": "ISC",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
+ "license": "MIT",
"dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
+ "balanced-match": "^4.0.2"
},
"engines": {
- "node": ">= 8"
+ "node": "20 || >=22"
}
},
- "node_modules/which-boxed-primitive": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
- "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/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==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/chalk": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-6.0.0.tgz",
+ "integrity": "sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==",
"license": "MIT",
- "dependencies": {
- "is-bigint": "^1.1.0",
- "is-boolean-object": "^1.2.1",
- "is-number-object": "^1.1.1",
- "is-string": "^1.1.1",
- "is-symbol": "^1.1.1"
- },
"engines": {
- "node": ">= 0.4"
+ "node": ">=22"
},
"funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/which-builtin-type": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
- "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.2",
- "function.prototype.name": "^1.1.6",
- "has-tostringtag": "^1.0.2",
- "is-async-function": "^2.0.0",
- "is-date-object": "^1.1.0",
- "is-finalizationregistry": "^1.1.0",
- "is-generator-function": "^1.0.10",
- "is-regex": "^1.2.1",
- "is-weakref": "^1.0.2",
- "isarray": "^2.0.5",
- "which-boxed-primitive": "^1.1.0",
- "which-collection": "^1.0.2",
- "which-typed-array": "^1.1.16"
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
},
"engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "node": ">= 8"
}
},
- "node_modules/which-collection": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
- "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"license": "MIT",
- "dependencies": {
- "is-map": "^2.0.3",
- "is-set": "^2.0.3",
- "is-weakmap": "^2.0.2",
- "is-weakset": "^2.0.3"
- },
"engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "node": ">= 12"
}
},
- "node_modules/which-typed-array": {
- "version": "1.1.19",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
- "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
- "available-typed-arrays": "^1.0.7",
- "call-bind": "^1.0.8",
- "call-bound": "^1.0.4",
- "for-each": "^0.3.5",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "has-tostringtag": "^1.0.2"
+ "ms": "^2.1.3"
},
"engines": {
- "node": ">= 0.4"
+ "node": ">=6.0"
},
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
- "node_modules/why-is-node-running": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
- "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
- "dev": true,
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/diff": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz",
+ "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/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==",
+ "license": "Apache-2.0",
"dependencies": {
- "siginfo": "^2.0.0",
- "stackback": "0.0.2"
- },
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/esbuild": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+ "hasInstallScript": true,
+ "license": "MIT",
"bin": {
- "why-is-node-running": "cli.js"
+ "esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=8"
- }
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.2",
+ "@esbuild/android-arm": "0.28.2",
+ "@esbuild/android-arm64": "0.28.2",
+ "@esbuild/android-x64": "0.28.2",
+ "@esbuild/darwin-arm64": "0.28.2",
+ "@esbuild/darwin-x64": "0.28.2",
+ "@esbuild/freebsd-arm64": "0.28.2",
+ "@esbuild/freebsd-x64": "0.28.2",
+ "@esbuild/linux-arm": "0.28.2",
+ "@esbuild/linux-arm64": "0.28.2",
+ "@esbuild/linux-ia32": "0.28.2",
+ "@esbuild/linux-loong64": "0.28.2",
+ "@esbuild/linux-mips64el": "0.28.2",
+ "@esbuild/linux-ppc64": "0.28.2",
+ "@esbuild/linux-riscv64": "0.28.2",
+ "@esbuild/linux-s390x": "0.28.2",
+ "@esbuild/linux-x64": "0.28.2",
+ "@esbuild/netbsd-arm64": "0.28.2",
+ "@esbuild/netbsd-x64": "0.28.2",
+ "@esbuild/openbsd-arm64": "0.28.2",
+ "@esbuild/openbsd-x64": "0.28.2",
+ "@esbuild/openharmony-arm64": "0.28.2",
+ "@esbuild/sunos-x64": "0.28.2",
+ "@esbuild/win32-arm64": "0.28.2",
+ "@esbuild/win32-ia32": "0.28.2",
+ "@esbuild/win32-x64": "0.28.2"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
},
- "node_modules/widest-line": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
- "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/fast-sha256": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
+ "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
+ "license": "Unlicense"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
"license": "MIT",
"dependencies": {
- "string-width": "^5.0.1"
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
},
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": "^12.20 || >= 14.13"
}
},
- "node_modules/window-size": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/window-size/-/window-size-1.1.1.tgz",
- "integrity": "sha512-5D/9vujkmVQ7pSmc0SCBmHXbkv6eaHwXEx65MywhmUMsI8sGqJ972APq1lotfcwMKPFLuCFfL8xGHLIp7jaBmA==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"license": "MIT",
"dependencies": {
- "define-property": "^1.0.0",
- "is-number": "^3.0.0"
- },
- "bin": {
- "window-size": "cli.js"
+ "fetch-blob": "^3.1.2"
},
"engines": {
- "node": ">= 0.10.0"
+ "node": ">=12.20.0"
}
},
- "node_modules/window-size/node_modules/is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/gaxios": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz",
+ "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==",
+ "license": "Apache-2.0",
"dependencies": {
- "kind-of": "^3.0.2"
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2"
},
"engines": {
- "node": ">=0.10.0"
+ "node": ">=18"
}
},
- "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",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/gcp-metadata": {
+ "version": "8.1.2",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
+ "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "google-logging-utils": "^1.0.0",
+ "json-bigint": "^1.0.0"
+ },
"engines": {
- "node": ">=0.10.0"
+ "node": ">=18"
}
},
- "node_modules/wrap-ansi": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
- "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "node_modules/@earendil-works/pi-coding-agent/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==",
"license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.1.0",
- "string-width": "^5.0.1",
- "strip-ansi": "^7.0.1"
- },
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/wrap-ansi-cjs": {
- "name": "wrap-ansi",
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/google-auth-library": {
+ "version": "10.6.2",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz",
+ "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==",
+ "license": "Apache-2.0",
"dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.1.4",
+ "gcp-metadata": "8.1.2",
+ "google-logging-utils": "1.1.3",
+ "jws": "^4.0.0"
},
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "node": ">=18"
}
},
- "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/google-logging-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+ "license": "Apache-2.0",
"engines": {
- "node": ">=8"
+ "node": ">=14"
}
},
- "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
+ "node_modules/@earendil-works/pi-coding-agent/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==",
+ "license": "ISC"
},
- "node_modules/wrap-ansi-cjs/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/grok-mermaid": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/grok-mermaid/-/grok-mermaid-0.2.3.tgz",
+ "integrity": "sha512-/4KopAbsjvuRP9MdPtlDjOHUmUVEohOX73JNcsWpzAtFxh+bq5+Dhb6gzvRieLDwIPQIR3/vy8V1NNTuz4Zsmg==",
+ "license": "Apache-2.0",
"engines": {
- "node": ">=8"
+ "node": ">=18"
}
},
- "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/highlight.js": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/hosted-git-info": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz",
+ "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==",
+ "license": "ISC",
"dependencies": {
- "ansi-regex": "^5.0.1"
+ "lru-cache": "^11.1.0"
},
"engines": {
- "node": ">=8"
+ "node": "^20.17.0 || >=22.9.0"
}
},
- "node_modules/wrap-ansi/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/http-proxy-agent": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz",
+ "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==",
"license": "MIT",
- "engines": {
- "node": ">=12"
+ "dependencies": {
+ "agent-base": "9.0.0",
+ "debug": "^4.3.4",
+ "proxy-agent-negotiate": "1.1.0"
},
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "engines": {
+ "node": ">= 20"
}
},
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "license": "ISC"
- },
- "node_modules/ws": {
- "version": "7.5.10",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
- "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
- "devOptional": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/http-proxy-agent/node_modules/agent-base": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz",
+ "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==",
"license": "MIT",
"engines": {
- "node": ">=8.3.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
+ "node": ">= 20"
}
},
- "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==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
- "is-wsl": "^3.1.0"
+ "agent-base": "^7.1.2",
+ "debug": "4"
},
"engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">= 14"
}
},
- "node_modules/xdg-basedir": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz",
- "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/ignore": {
+ "version": "7.0.8",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz",
+ "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==",
"license": "MIT",
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">= 4"
}
},
- "node_modules/xml-name-validator": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
- "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18"
- }
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
},
- "node_modules/xmlchars": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
- "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
- "dev": true,
- "license": "MIT"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/jiti": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
},
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "license": "ISC",
- "engines": {
- "node": ">=10"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bignumber.js": "^9.0.0"
}
},
- "node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/json-schema-to-ts": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz",
+ "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==",
"license": "MIT",
"dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
+ "@babel/runtime": "^7.18.3",
+ "ts-algebra": "^2.0.0"
},
"engines": {
- "node": ">=12"
+ "node": ">=16"
}
},
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "license": "ISC",
- "engines": {
- "node": ">=12"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
}
},
- "node_modules/yargs/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==",
+ "node_modules/@earendil-works/pi-coding-agent/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==",
"license": "MIT",
- "engines": {
- "node": ">=8"
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
}
},
- "node_modules/yargs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
},
- "node_modules/yargs/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/lru-cache": {
+ "version": "11.4.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
+ "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
+ "license": "BlueOak-1.0.0",
"engines": {
- "node": ">=8"
+ "node": "20 || >=22"
}
},
- "node_modules/yargs/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==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/marked": {
+ "version": "18.0.11",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.11.tgz",
+ "integrity": "sha512-HnslJfsZkRPBDJRHvVtAaWlZHEpSu7u8LgQuJCELjRKuWR+hpq4A7sLq3p8HaI9ypVoXDXxV34CsQJEe1+J5Aw==",
"license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
+ "bin": {
+ "marked": "bin/marked.js"
},
"engines": {
- "node": ">=8"
+ "node": ">= 20"
}
},
- "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",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/minimatch": {
+ "version": "10.2.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
+ "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.8"
+ },
"engines": {
- "node": ">=10"
+ "node": "18 || 20 || >=22"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/yoctocolors-cjs": {
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/ms": {
"version": "2.1.3",
- "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
- "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
- "dev": true,
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
"license": "MIT",
"engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">=10.5.0"
}
},
- "node_modules/yoga-layout": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
- "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
- "license": "MIT"
- },
- "node_modules/zod": {
- "version": "3.25.76",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
- "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
"funding": {
- "url": "https://github.com/sponsors/colinhacks"
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
}
},
- "node_modules/zod-to-json-schema": {
- "version": "3.24.6",
- "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
- "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
- "license": "ISC",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/openai": {
+ "version": "6.40.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-6.40.0.tgz",
+ "integrity": "sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA==",
+ "license": "Apache-2.0",
"peerDependencies": {
- "zod": "^3.24.1"
+ "ws": "^8.18.0",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "ws": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
}
},
- "packages/cli": {
- "name": "@anus-dev/anus",
- "version": "0.1.0",
- "license": "Apache-2.0",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/p-retry": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+ "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "license": "MIT",
"dependencies": {
- "@anus-dev/anus-core": "file:../core",
- "@google/genai": "1.13.0",
- "@iarna/toml": "^2.2.5",
- "@modelcontextprotocol/sdk": "^1.15.1",
- "@types/update-notifier": "^6.0.8",
- "command-exists": "^1.2.9",
- "diff": "^7.0.0",
- "dotenv": "^17.1.0",
- "glob": "^10.4.1",
- "highlight.js": "^11.11.1",
- "ink": "^6.1.1",
- "ink-big-text": "^2.0.0",
- "ink-gradient": "^3.0.0",
- "ink-link": "^4.1.0",
- "ink-select-input": "^6.2.0",
- "ink-spinner": "^5.0.0",
- "lowlight": "^3.3.0",
- "mime-types": "^3.0.1",
- "open": "^10.1.2",
- "react": "^19.1.0",
- "read-package-up": "^11.0.0",
- "shell-quote": "^1.8.3",
- "string-width": "^7.1.0",
- "strip-ansi": "^7.1.0",
- "strip-json-comments": "^3.1.1",
- "undici": "^7.10.0",
- "update-notifier": "^7.3.1",
- "yargs": "^17.7.2",
- "zod": "^3.23.8"
- },
- "bin": {
- "anus": "dist/index.js"
- },
- "devDependencies": {
- "@anus-dev/anus-test-utils": "file:../test-utils",
- "@babel/runtime": "^7.27.6",
- "@testing-library/react": "^16.3.0",
- "@types/command-exists": "^1.2.3",
- "@types/diff": "^7.0.2",
- "@types/dotenv": "^6.1.1",
- "@types/node": "^20.11.24",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
- "@types/semver": "^7.7.0",
- "@types/shell-quote": "^1.7.5",
- "@types/yargs": "^17.0.32",
- "ink-testing-library": "^4.0.0",
- "jsdom": "^26.1.0",
- "pretty-format": "^30.0.2",
- "react-dom": "^19.1.0",
- "typescript": "^5.3.3",
- "vitest": "^3.1.1"
+ "@types/retry": "0.12.0",
+ "retry": "^0.13.1"
},
"engines": {
- "node": ">=20"
+ "node": ">=8"
}
},
- "packages/cli/node_modules/@modelcontextprotocol/sdk": {
- "version": "1.17.4",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/p-retry/node_modules/@types/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "license": "MIT"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/partial-json": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz",
+ "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==",
+ "license": "MIT"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"license": "MIT",
- "dependencies": {
- "ajv": "^6.12.6",
- "content-type": "^1.0.5",
- "cors": "^2.8.5",
- "cross-spawn": "^7.0.5",
- "eventsource": "^3.0.2",
- "eventsource-parser": "^3.0.0",
- "express": "^5.0.1",
- "express-rate-limit": "^7.5.0",
- "pkce-challenge": "^5.0.0",
- "raw-body": "^3.0.0",
- "zod": "^3.23.8",
- "zod-to-json-schema": "^3.24.1"
- },
"engines": {
- "node": ">=18"
+ "node": ">=8"
}
},
- "packages/cli/node_modules/@types/node": {
- "version": "20.19.11",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/proper-lockfile": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
+ "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
"license": "MIT",
"dependencies": {
- "undici-types": "~6.21.0"
+ "graceful-fs": "^4.2.4",
+ "retry": "^0.12.0",
+ "signal-exit": "^3.0.2"
}
},
- "packages/cli/node_modules/emoji-regex": {
- "version": "10.5.0",
- "license": "MIT"
- },
- "packages/cli/node_modules/eventsource": {
- "version": "3.0.7",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/proper-lockfile/node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/protobufjs": {
+ "version": "7.6.6",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz",
+ "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
"dependencies": {
- "eventsource-parser": "^3.0.1"
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.5",
+ "@protobufjs/eventemitter": "^1.1.1",
+ "@protobufjs/fetch": "^1.1.1",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.1",
+ "@types/node": ">=13.7.0",
+ "long": "^5.3.2"
},
"engines": {
- "node": ">=18.0.0"
+ "node": ">=12.0.0"
}
},
- "packages/cli/node_modules/string-width": {
- "version": "7.2.0",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/proxy-agent-negotiate": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz",
+ "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==",
"license": "MIT",
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
"engines": {
- "node": ">=18"
+ "node": ">= 20"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "peerDependencies": {
+ "kerberos": "^2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "kerberos": {
+ "optional": true
+ }
}
},
- "packages/cli/node_modules/undici": {
- "version": "7.15.0",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
"license": "MIT",
"engines": {
- "node": ">=20.18.1"
+ "node": ">= 4"
}
},
- "packages/cli/node_modules/undici-types": {
- "version": "6.21.0",
- "dev": true,
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
"license": "MIT"
},
- "packages/core": {
- "name": "@anus-dev/anus-core",
- "version": "0.1.0",
- "license": "Apache-2.0",
- "dependencies": {
- "@google/genai": "1.13.0",
- "@modelcontextprotocol/sdk": "^1.11.0",
- "@opentelemetry/api": "^1.9.0",
- "@opentelemetry/exporter-logs-otlp-grpc": "^0.52.0",
- "@opentelemetry/exporter-logs-otlp-http": "^0.52.0",
- "@opentelemetry/exporter-metrics-otlp-grpc": "^0.52.0",
- "@opentelemetry/exporter-metrics-otlp-http": "^0.52.0",
- "@opentelemetry/exporter-trace-otlp-grpc": "^0.52.0",
- "@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
- "@opentelemetry/instrumentation-http": "^0.52.0",
- "@opentelemetry/sdk-node": "^0.52.0",
- "@types/glob": "^8.1.0",
- "@types/html-to-text": "^9.0.4",
- "ajv": "^8.17.1",
- "chardet": "^2.1.0",
- "diff": "^7.0.0",
- "dotenv": "^17.1.0",
- "fdir": "^6.4.6",
- "fzf": "^0.5.2",
- "glob": "^10.4.5",
- "google-auth-library": "^9.11.0",
- "html-to-text": "^9.0.5",
- "https-proxy-agent": "^7.0.6",
- "ignore": "^7.0.0",
- "marked": "^15.0.12",
- "micromatch": "^4.0.8",
- "mnemonist": "^0.40.3",
- "open": "^10.1.2",
- "openai": "^5.15.0",
- "picomatch": "^4.0.1",
- "shell-quote": "^1.8.3",
- "simple-git": "^3.28.0",
- "strip-ansi": "^7.1.0",
- "undici": "^7.10.0",
- "ws": "^8.18.0"
- },
- "devDependencies": {
- "@anus-dev/anus-test-utils": "file:../test-utils",
- "@types/diff": "^7.0.2",
- "@types/dotenv": "^6.1.1",
- "@types/micromatch": "^4.0.8",
- "@types/minimatch": "^5.1.2",
- "@types/picomatch": "^4.0.1",
- "@types/ws": "^8.5.10",
- "typescript": "^5.3.3",
- "vitest": "^3.1.1"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
},
"engines": {
- "node": ">=20"
+ "node": ">=10"
}
},
- "packages/core/node_modules/@modelcontextprotocol/sdk": {
- "version": "1.17.4",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"license": "MIT",
"dependencies": {
- "ajv": "^6.12.6",
- "content-type": "^1.0.5",
- "cors": "^2.8.5",
- "cross-spawn": "^7.0.5",
- "eventsource": "^3.0.2",
- "eventsource-parser": "^3.0.0",
- "express": "^5.0.1",
- "express-rate-limit": "^7.5.0",
- "pkce-challenge": "^5.0.0",
- "raw-body": "^3.0.0",
- "zod": "^3.23.8",
- "zod-to-json-schema": "^3.24.1"
+ "shebang-regex": "^3.0.0"
},
"engines": {
- "node": ">=18"
+ "node": ">=8"
}
},
- "packages/core/node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
- "version": "6.12.6",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"license": "MIT",
- "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"
+ "engines": {
+ "node": ">=8"
}
},
- "packages/core/node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "license": "MIT"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "license": "ISC"
},
- "packages/core/node_modules/ajv": {
- "version": "8.17.1",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/standardwebhooks": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz",
+ "integrity": "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==",
"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"
+ "@stablelib/base64": "^1.0.0",
+ "fast-sha256": "^1.3.0"
}
},
- "packages/core/node_modules/eventsource": {
- "version": "3.0.7",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/ts-algebra": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz",
+ "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==",
+ "license": "MIT"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/typebox": {
+ "version": "1.3.27",
+ "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.27.tgz",
+ "integrity": "sha512-zu+jc1pcy4UiNThxikUr36f0Rybk9PEeCg/NE6adeWr/SKsdNO4EzZHYRDlv2YCVAfj3Odq3dESSo/jNyoBXzA==",
+ "license": "MIT"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/undici": {
+ "version": "8.10.2",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.2.tgz",
+ "integrity": "sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==",
"license": "MIT",
- "dependencies": {
- "eventsource-parser": "^3.0.1"
- },
"engines": {
- "node": ">=18.0.0"
+ "node": ">=22.19.0"
}
},
- "packages/core/node_modules/ignore": {
- "version": "7.0.5",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "license": "MIT"
+ },
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"license": "MIT",
"engines": {
- "node": ">= 4"
+ "node": ">= 8"
}
},
- "packages/core/node_modules/json-schema-traverse": {
- "version": "1.0.0",
- "license": "MIT"
- },
- "packages/core/node_modules/openai": {
- "version": "5.19.1",
- "resolved": "https://registry.npmjs.org/openai/-/openai-5.19.1.tgz",
- "integrity": "sha512-zSqnUF7oR9ksmpusKkpUgkNrj8Sl57U+OyzO8jzc7LUjTMg4DRfR3uCm+EIMA6iw06sRPNp4t7ojp3sCpEUZRQ==",
- "license": "Apache-2.0",
- "bin": {
- "openai": "bin/cli"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
},
- "peerDependencies": {
- "ws": "^8.18.0",
- "zod": "^3.23.8"
+ "bin": {
+ "node-which": "bin/node-which"
},
- "peerDependenciesMeta": {
- "ws": {
- "optional": true
- },
- "zod": {
- "optional": true
- }
- }
- },
- "packages/core/node_modules/undici": {
- "version": "7.15.0",
- "license": "MIT",
"engines": {
- "node": ">=20.18.1"
+ "node": ">= 8"
}
},
- "packages/core/node_modules/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/ws": {
+ "version": "8.21.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
+ "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -12492,88 +1897,20 @@
}
}
},
- "packages/test-utils": {
- "name": "@anus-dev/anus-test-utils",
- "version": "0.1.0",
- "license": "Apache-2.0",
- "devDependencies": {
- "typescript": "^5.3.3"
- },
- "engines": {
- "node": ">=20"
- }
- },
- "packages/vscode-ide-companion": {
- "name": "anus-vscode-ide-companion",
- "version": "0.1.0",
- "license": "Apache-2.0",
- "dependencies": {
- "@modelcontextprotocol/sdk": "^1.15.1",
- "cors": "^2.8.5",
- "express": "^5.1.0",
- "zod": "^3.25.76",
- "zod-to-json-schema": "^3.24.6"
- },
- "devDependencies": {
- "@types/cors": "^2.8.19",
- "@types/express": "^5.0.3",
- "@types/node": "20.x",
- "@types/vscode": "^1.99.0",
- "@typescript-eslint/eslint-plugin": "^8.31.1",
- "@typescript-eslint/parser": "^8.31.1",
- "esbuild": "^0.25.3",
- "eslint": "^9.25.1",
- "npm-run-all": "^4.1.5",
- "typescript": "^5.8.3",
- "vitest": "^3.2.4"
- },
- "engines": {
- "vscode": "^1.99.0"
- }
- },
- "packages/vscode-ide-companion/node_modules/@modelcontextprotocol/sdk": {
- "version": "1.17.4",
- "license": "MIT",
- "dependencies": {
- "ajv": "^6.12.6",
- "content-type": "^1.0.5",
- "cors": "^2.8.5",
- "cross-spawn": "^7.0.5",
- "eventsource": "^3.0.2",
- "eventsource-parser": "^3.0.0",
- "express": "^5.0.1",
- "express-rate-limit": "^7.5.0",
- "pkce-challenge": "^5.0.0",
- "raw-body": "^3.0.0",
- "zod": "^3.23.8",
- "zod-to-json-schema": "^3.24.1"
+ "node_modules/@earendil-works/pi-coding-agent/node_modules/yaml": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
},
"engines": {
- "node": ">=18"
- }
- },
- "packages/vscode-ide-companion/node_modules/@types/node": {
- "version": "20.19.11",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "undici-types": "~6.21.0"
- }
- },
- "packages/vscode-ide-companion/node_modules/eventsource": {
- "version": "3.0.7",
- "license": "MIT",
- "dependencies": {
- "eventsource-parser": "^3.0.1"
+ "node": ">= 14.6"
},
- "engines": {
- "node": ">=18.0.0"
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
}
- },
- "packages/vscode-ide-companion/node_modules/undici-types": {
- "version": "6.21.0",
- "dev": true,
- "license": "MIT"
}
}
}
diff --git a/package.json b/package.json
index f1d0306..b2f7b52 100644
--- a/package.json
+++ b/package.json
@@ -1,109 +1,43 @@
{
"name": "@anus-dev/anus",
- "version": "0.1.0",
- "private": false,
- "license": "Apache-2.0",
- "publishConfig": {
- "access": "public",
- "registry": "https://registry.npmjs.org/"
- },
- "engines": {
- "node": ">=20.0.0"
- },
+ "version": "0.2.1",
+ "description": "ANUS: a free coding agent in your terminal. Every request goes to the smartest free model that is answering today.",
"type": "module",
- "workspaces": [
- "packages/*"
- ],
- "repository": {
- "type": "git",
- "url": "git+https://github.com/anus-dev/anus.git"
- },
- "config": {
- "sandboxImageUri": "us-docker.pkg.dev/anus-code-dev/anus-cli/sandbox:0.1.0"
- },
- "scripts": {
- "prepublishOnly": "npm run build && npm run typecheck && npm run test:bundle",
- "publish:private": "npm publish --access restricted",
- "publish:dry-run": "npm publish --dry-run",
- "test:bundle": "./scripts/test-before-publish.sh",
- "start": "node scripts/start.js",
- "debug": "cross-env DEBUG=1 node --inspect-brk scripts/start.js",
- "auth:npm": "npx google-artifactregistry-auth",
- "auth:docker": "gcloud auth configure-docker us-west1-docker.pkg.dev",
- "auth": "npm run auth:npm && npm run auth:docker",
- "generate": "node scripts/generate-git-commit-info.js",
- "build": "node scripts/build.js",
- "build-and-start": "npm run build && npm run start",
- "build:vscode": "node scripts/build_vscode_companion.js",
- "build:all": "npm run build && npm run build:sandbox && npm run build:vscode",
- "build:packages": "npm run build --workspaces",
- "build:sandbox": "node scripts/build_sandbox.js --skip-npm-install-build",
- "bundle": "npm run generate && node esbuild.config.js && node scripts/copy_bundle_assets.js",
- "test": "npm run test --workspaces --if-present",
- "test:ci": "npm run test:ci --workspaces --if-present && npm run test:scripts",
- "test:scripts": "vitest run --config ./scripts/tests/vitest.config.ts",
- "test:e2e": "cross-env VERBOSE=true KEEP_OUTPUT=true npm run test:integration:sandbox:none",
- "test:integration:all": "npm run test:integration:sandbox:none && npm run test:integration:sandbox:docker && npm run test:integration:sandbox:podman",
- "test:integration:sandbox:none": "ANUS_SANDBOX=false vitest run --root ./integration-tests",
- "test:integration:sandbox:docker": "npm run build:sandbox && ANUS_SANDBOX=docker vitest run --root ./integration-tests",
- "test:integration:sandbox:podman": "ANUS_SANDBOX=podman vitest run --root ./integration-tests",
- "lint": "eslint . --ext .ts,.tsx && eslint integration-tests",
- "lint:fix": "eslint . --fix && eslint integration-tests --fix",
- "lint:ci": "eslint . --ext .ts,.tsx --max-warnings 5 && eslint integration-tests --max-warnings 0",
- "format": "prettier --experimental-cli --write .",
- "typecheck": "npm run typecheck --workspace=@anus-dev/anus --workspace=@anus-dev/anus-core --workspace=@anus-dev/anus-test-utils --if-present",
- "preflight": "npm ci && npm run format && npm run lint:ci && npm run build && npm run typecheck && npm run test:ci",
- "preflight:full": "npm run clean && npm ci && npm run format && npm run lint:ci && npm run build && npm run typecheck && npm run test:ci",
- "prepare": "npm run bundle",
- "prepare:package": "node scripts/prepare-package.js",
- "release:version": "node scripts/version.js",
- "telemetry": "node scripts/telemetry.js",
- "clean": "node scripts/clean.js"
- },
"bin": {
- "anus": "bundle/anus.js"
+ "anus": "bin/anus.mjs"
},
"files": [
- "bundle/",
+ "bin",
+ "lib",
+ "extension",
"README.md",
+ "CHANGELOG.md",
"LICENSE",
"NOTICE"
],
- "devDependencies": {
- "@types/marked": "^5.0.2",
- "@types/micromatch": "^4.0.9",
- "@types/mime-types": "^3.0.1",
- "@types/minimatch": "^5.1.2",
- "@types/mock-fs": "^4.13.4",
- "@types/shell-quote": "^1.7.5",
- "@vitest/coverage-v8": "^3.1.1",
- "concurrently": "^9.2.0",
- "cross-env": "^7.0.3",
- "esbuild": "^0.25.0",
- "eslint": "^9.24.0",
- "eslint-config-prettier": "^10.1.2",
- "eslint-plugin-import": "^2.31.0",
- "eslint-plugin-react": "^7.37.5",
- "eslint-plugin-react-hooks": "^5.2.0",
- "glob": "^10.4.5",
- "globals": "^16.0.0",
- "json": "^11.0.0",
- "lodash": "^4.17.21",
- "memfs": "^4.17.2",
- "mnemonist": "^0.40.3",
- "mock-fs": "^5.5.0",
- "msw": "^2.10.4",
- "prettier": "^3.5.3",
- "react-devtools-core": "^4.28.5",
- "tsx": "^4.20.3",
- "typescript-eslint": "^8.30.1",
- "vitest": "^3.2.4",
- "yargs": "^17.7.2"
+ "scripts": {
+ "test": "node --test test/*.test.mjs"
+ },
+ "engines": {
+ "node": ">=22.19.0"
},
"dependencies": {
- "eventsource": "^4.0.0",
- "eventsource-parser": "^3.0.5",
- "fast-uri": "^3.1.0",
- "pkce-challenge": "^5.0.0"
- }
+ "@earendil-works/pi-coding-agent": "0.87.1"
+ },
+ "keywords": [
+ "anus",
+ "coding-agent",
+ "free",
+ "llm",
+ "openrouter",
+ "cli",
+ "terminal",
+ "pi"
+ ],
+ "homepage": "https://anus.dev",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/anus-dev/anus.git"
+ },
+ "license": "MIT"
}
diff --git a/packages/cli/index.ts b/packages/cli/index.ts
deleted file mode 100644
index ea44e0f..0000000
--- a/packages/cli/index.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import './src/anus.js';
-import { main } from './src/anus.js';
-
-// --- Global Entry Point ---
-main().catch((error) => {
- console.error('An unexpected critical error occurred:');
- if (error instanceof Error) {
- console.error(error.stack);
- } else {
- console.error(String(error));
- }
- process.exit(1);
-});
diff --git a/packages/cli/package.json b/packages/cli/package.json
deleted file mode 100644
index db59ad1..0000000
--- a/packages/cli/package.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
- "name": "@anus-dev/anus",
- "version": "0.1.0",
- "description": "ANUS",
- "license": "Apache-2.0",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/anus-dev/anus.git"
- },
- "type": "module",
- "main": "dist/index.js",
- "bin": {
- "anus": "dist/index.js"
- },
- "scripts": {
- "build": "node ../../scripts/build_package.js",
- "start": "node dist/index.js",
- "debug": "node --inspect-brk dist/index.js",
- "lint": "eslint . --ext .ts,.tsx",
- "format": "prettier --write .",
- "test": "vitest run",
- "test:ci": "vitest run --coverage",
- "typecheck": "tsc --noEmit"
- },
- "files": [
- "dist"
- ],
- "config": {
- "sandboxImageUri": "us-docker.pkg.dev/anus-code-dev/anus-cli/sandbox:0.1.0"
- },
- "dependencies": {
- "@anus-dev/anus-core": "file:../core",
- "@google/genai": "1.13.0",
- "@iarna/toml": "^2.2.5",
- "@modelcontextprotocol/sdk": "^1.15.1",
- "@types/update-notifier": "^6.0.8",
- "command-exists": "^1.2.9",
- "diff": "^7.0.0",
- "dotenv": "^17.1.0",
- "glob": "^10.4.1",
- "highlight.js": "^11.11.1",
- "ink": "^6.1.1",
- "ink-big-text": "^2.0.0",
- "ink-gradient": "^3.0.0",
- "ink-link": "^4.1.0",
- "ink-select-input": "^6.2.0",
- "ink-spinner": "^5.0.0",
- "lowlight": "^3.3.0",
- "mime-types": "^3.0.1",
- "open": "^10.1.2",
- "react": "^19.1.0",
- "read-package-up": "^11.0.0",
- "shell-quote": "^1.8.3",
- "string-width": "^7.1.0",
- "strip-ansi": "^7.1.0",
- "strip-json-comments": "^3.1.1",
- "undici": "^7.10.0",
- "update-notifier": "^7.3.1",
- "yargs": "^17.7.2",
- "zod": "^3.23.8"
- },
- "devDependencies": {
- "@babel/runtime": "^7.27.6",
- "@testing-library/react": "^16.3.0",
- "@types/command-exists": "^1.2.3",
- "@types/diff": "^7.0.2",
- "@types/dotenv": "^6.1.1",
- "@types/node": "^20.11.24",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
- "@types/semver": "^7.7.0",
- "@types/shell-quote": "^1.7.5",
- "@types/yargs": "^17.0.32",
- "ink-testing-library": "^4.0.0",
- "jsdom": "^26.1.0",
- "pretty-format": "^30.0.2",
- "react-dom": "^19.1.0",
- "typescript": "^5.3.3",
- "vitest": "^3.1.1",
- "@anus-dev/anus-test-utils": "file:../test-utils"
- },
- "engines": {
- "node": ">=20"
- }
-}
diff --git a/packages/cli/src/anus.test.tsx b/packages/cli/src/anus.test.tsx
deleted file mode 100644
index 52dfd35..0000000
--- a/packages/cli/src/anus.test.tsx
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import stripAnsi from 'strip-ansi';
-import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
-import {
- main,
- setupUnhandledRejectionHandler,
- validateDnsResolutionOrder,
-} from './anus.js';
-import {
- LoadedSettings,
- SettingsFile,
- loadSettings,
-} from './config/settings.js';
-import { appEvents, AppEvent } from './utils/events.js';
-
-// Custom error to identify mock process.exit calls
-class MockProcessExitError extends Error {
- constructor(readonly code?: string | number | null | undefined) {
- super('PROCESS_EXIT_MOCKED');
- this.name = 'MockProcessExitError';
- }
-}
-
-// Mock dependencies
-vi.mock('./config/settings.js', async (importOriginal) => {
- const actual = await importOriginal();
- return {
- ...actual,
- loadSettings: vi.fn(),
- };
-});
-
-vi.mock('./config/config.js', () => ({
- loadCliConfig: vi.fn().mockResolvedValue({
- config: {
- getSandbox: vi.fn(() => false),
- getQuestion: vi.fn(() => ''),
- },
- modelWasSwitched: false,
- originalModelBeforeSwitch: null,
- finalModel: 'test-model',
- }),
-}));
-
-vi.mock('read-package-up', () => ({
- readPackageUp: vi.fn().mockResolvedValue({
- packageJson: { name: 'test-pkg', version: 'test-version' },
- path: '/fake/path/package.json',
- }),
-}));
-
-vi.mock('update-notifier', () => ({
- default: vi.fn(() => ({
- notify: vi.fn(),
- })),
-}));
-
-vi.mock('./utils/events.js', async (importOriginal) => {
- const actual = await importOriginal();
- return {
- ...actual,
- appEvents: {
- emit: vi.fn(),
- },
- };
-});
-
-vi.mock('./utils/sandbox.js', () => ({
- sandbox_command: vi.fn(() => ''), // Default to no sandbox command
- start_sandbox: vi.fn(() => Promise.resolve()), // Mock as an async function that resolves
-}));
-
-describe('anus.tsx main function', () => {
- let consoleErrorSpy: ReturnType;
- let loadSettingsMock: ReturnType>;
- let originalEnvAnusSandbox: string | undefined;
- let originalEnvSandbox: string | undefined;
- let initialUnhandledRejectionListeners: NodeJS.UnhandledRejectionListener[] =
- [];
-
- const processExitSpy = vi
- .spyOn(process, 'exit')
- .mockImplementation((code) => {
- throw new MockProcessExitError(code);
- });
-
- beforeEach(() => {
- loadSettingsMock = vi.mocked(loadSettings);
-
- // Store and clear sandbox-related env variables to ensure a consistent test environment
- originalEnvAnusSandbox = process.env['ANUS_SANDBOX'];
- originalEnvSandbox = process.env['SANDBOX'];
- delete process.env['ANUS_SANDBOX'];
- delete process.env['SANDBOX'];
-
- consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
- initialUnhandledRejectionListeners =
- process.listeners('unhandledRejection');
- });
-
- afterEach(() => {
- // Restore original env variables
- if (originalEnvAnusSandbox !== undefined) {
- process.env['ANUS_SANDBOX'] = originalEnvAnusSandbox;
- } else {
- delete process.env['ANUS_SANDBOX'];
- }
- if (originalEnvSandbox !== undefined) {
- process.env['SANDBOX'] = originalEnvSandbox;
- } else {
- delete process.env['SANDBOX'];
- }
-
- const currentListeners = process.listeners('unhandledRejection');
- const addedListener = currentListeners.find(
- (listener) => !initialUnhandledRejectionListeners.includes(listener),
- );
-
- if (addedListener) {
- process.removeListener('unhandledRejection', addedListener);
- }
- vi.restoreAllMocks();
- });
-
- it('should call process.exit(1) if settings have errors', async () => {
- const settingsError = {
- message: 'Test settings error',
- path: '/test/settings.json',
- };
- const userSettingsFile: SettingsFile = {
- path: '/user/settings.json',
- settings: {},
- };
- const workspaceSettingsFile: SettingsFile = {
- path: '/workspace/.anus/settings.json',
- settings: {},
- };
- const systemSettingsFile: SettingsFile = {
- path: '/system/settings.json',
- settings: {},
- };
- const mockLoadedSettings = new LoadedSettings(
- systemSettingsFile,
- userSettingsFile,
- workspaceSettingsFile,
- [settingsError],
- );
-
- loadSettingsMock.mockReturnValue(mockLoadedSettings);
-
- try {
- await main();
- // If main completes without throwing, the test should fail because process.exit was expected
- expect.fail('main function did not exit as expected');
- } catch (error) {
- expect(error).toBeInstanceOf(MockProcessExitError);
- if (error instanceof MockProcessExitError) {
- expect(error.code).toBe(1);
- }
- }
-
- // Verify console.error was called with the error message
- expect(consoleErrorSpy).toHaveBeenCalledTimes(2);
- expect(stripAnsi(String(consoleErrorSpy.mock.calls[0][0]))).toBe(
- 'Error in /test/settings.json: Test settings error',
- );
- expect(stripAnsi(String(consoleErrorSpy.mock.calls[1][0]))).toBe(
- 'Please fix /test/settings.json and try again.',
- );
-
- // Verify process.exit was called.
- expect(processExitSpy).toHaveBeenCalledWith(1);
- });
-
- it('should log unhandled promise rejections and open debug console on first error', async () => {
- const appEventsMock = vi.mocked(appEvents);
- const rejectionError = new Error('Test unhandled rejection');
-
- setupUnhandledRejectionHandler();
- // Simulate an unhandled rejection.
- // We are not using Promise.reject here as vitest will catch it.
- // Instead we will dispatch the event manually.
- process.emit('unhandledRejection', rejectionError, Promise.resolve());
-
- // We need to wait for the rejection handler to be called.
- await new Promise(process.nextTick);
-
- expect(appEventsMock.emit).toHaveBeenCalledWith(AppEvent.OpenDebugConsole);
- expect(appEventsMock.emit).toHaveBeenCalledWith(
- AppEvent.LogError,
- expect.stringContaining('Unhandled Promise Rejection'),
- );
- expect(appEventsMock.emit).toHaveBeenCalledWith(
- AppEvent.LogError,
- expect.stringContaining('Please file a bug report using the /bug tool.'),
- );
-
- // Simulate a second rejection
- const secondRejectionError = new Error('Second test unhandled rejection');
- process.emit('unhandledRejection', secondRejectionError, Promise.resolve());
- await new Promise(process.nextTick);
-
- // Ensure emit was only called once for OpenDebugConsole
- const openDebugConsoleCalls = appEventsMock.emit.mock.calls.filter(
- (call) => call[0] === AppEvent.OpenDebugConsole,
- );
- expect(openDebugConsoleCalls.length).toBe(1);
-
- // Avoid the process.exit error from being thrown.
- processExitSpy.mockRestore();
- });
-});
-
-describe('validateDnsResolutionOrder', () => {
- let consoleWarnSpy: ReturnType;
-
- beforeEach(() => {
- consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
- });
-
- afterEach(() => {
- consoleWarnSpy.mockRestore();
- });
-
- it('should return "ipv4first" when the input is "ipv4first"', () => {
- expect(validateDnsResolutionOrder('ipv4first')).toBe('ipv4first');
- expect(consoleWarnSpy).not.toHaveBeenCalled();
- });
-
- it('should return "verbatim" when the input is "verbatim"', () => {
- expect(validateDnsResolutionOrder('verbatim')).toBe('verbatim');
- expect(consoleWarnSpy).not.toHaveBeenCalled();
- });
-
- it('should return the default "ipv4first" when the input is undefined', () => {
- expect(validateDnsResolutionOrder(undefined)).toBe('ipv4first');
- expect(consoleWarnSpy).not.toHaveBeenCalled();
- });
-
- it('should return the default "ipv4first" and log a warning for an invalid string', () => {
- expect(validateDnsResolutionOrder('invalid-value')).toBe('ipv4first');
- expect(consoleWarnSpy).toHaveBeenCalledOnce();
- expect(consoleWarnSpy).toHaveBeenCalledWith(
- 'Invalid value for dnsResolutionOrder in settings: "invalid-value". Using default "ipv4first".',
- );
- });
-});
diff --git a/packages/cli/src/anus.tsx b/packages/cli/src/anus.tsx
deleted file mode 100644
index 0cee13a..0000000
--- a/packages/cli/src/anus.tsx
+++ /dev/null
@@ -1,363 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React from 'react';
-import { render } from 'ink';
-import { AppWrapper } from './ui/App.js';
-import { loadCliConfig, parseArguments } from './config/config.js';
-import { readStdin } from './utils/readStdin.js';
-import { basename } from 'node:path';
-import v8 from 'node:v8';
-import os from 'node:os';
-import dns from 'node:dns';
-import { spawn } from 'node:child_process';
-import { start_sandbox } from './utils/sandbox.js';
-import {
- DnsResolutionOrder,
- LoadedSettings,
- loadSettings,
- SettingScope,
-} from './config/settings.js';
-import { themeManager } from './ui/themes/theme-manager.js';
-import { getStartupWarnings } from './utils/startupWarnings.js';
-import { getUserStartupWarnings } from './utils/userStartupWarnings.js';
-import { ConsolePatcher } from './ui/utils/ConsolePatcher.js';
-import { runNonInteractive } from './nonInteractiveCli.js';
-import { loadExtensions } from './config/extension.js';
-import { cleanupCheckpoints, registerCleanup } from './utils/cleanup.js';
-import { getCliVersion } from './utils/version.js';
-import {
- Config,
- sessionId,
- logUserPrompt,
- AuthType,
- getOauthClient,
- logIdeConnection,
- IdeConnectionEvent,
- IdeConnectionType,
-} from '@anus-dev/anus-core';
-import { validateAuthMethod } from './config/auth.js';
-import { setMaxSizedBoxDebugging } from './ui/components/shared/MaxSizedBox.js';
-import { validateNonInteractiveAuth } from './validateNonInterActiveAuth.js';
-import { detectAndEnableKittyProtocol } from './ui/utils/kittyProtocolDetector.js';
-import { checkForUpdates } from './ui/utils/updateCheck.js';
-import { handleAutoUpdate } from './utils/handleAutoUpdate.js';
-import { appEvents, AppEvent } from './utils/events.js';
-import { SettingsContext } from './ui/contexts/SettingsContext.js';
-
-export function validateDnsResolutionOrder(
- order: string | undefined,
-): DnsResolutionOrder {
- const defaultValue: DnsResolutionOrder = 'ipv4first';
- if (order === undefined) {
- return defaultValue;
- }
- if (order === 'ipv4first' || order === 'verbatim') {
- return order;
- }
- // We don't want to throw here, just warn and use the default.
- console.warn(
- `Invalid value for dnsResolutionOrder in settings: "${order}". Using default "${defaultValue}".`,
- );
- return defaultValue;
-}
-
-function getNodeMemoryArgs(config: Config): string[] {
- const totalMemoryMB = os.totalmem() / (1024 * 1024);
- const heapStats = v8.getHeapStatistics();
- const currentMaxOldSpaceSizeMb = Math.floor(
- heapStats.heap_size_limit / 1024 / 1024,
- );
-
- // Set target to 50% of total memory
- const targetMaxOldSpaceSizeInMB = Math.floor(totalMemoryMB * 0.5);
- if (config.getDebugMode()) {
- console.debug(
- `Current heap size ${currentMaxOldSpaceSizeMb.toFixed(2)} MB`,
- );
- }
-
- // ANUS_CLI_NO_RELAUNCH: For when you need to prevent recursive spawning
- // (The infinite poop loop must be avoided)
- if (process.env['ANUS_CLI_NO_RELAUNCH']) {
- return [];
- }
-
- if (targetMaxOldSpaceSizeInMB > currentMaxOldSpaceSizeMb) {
- if (config.getDebugMode()) {
- console.debug(
- `Need to relaunch with more memory: ${targetMaxOldSpaceSizeInMB.toFixed(2)} MB`,
- );
- }
- return [`--max-old-space-size=${targetMaxOldSpaceSizeInMB}`];
- }
-
- return [];
-}
-
-async function relaunchWithAdditionalArgs(additionalArgs: string[]) {
- const nodeArgs = [...additionalArgs, ...process.argv.slice(1)];
- const newEnv = { ...process.env, ANUS_CLI_NO_RELAUNCH: 'true' };
-
- const child = spawn(process.execPath, nodeArgs, {
- stdio: 'inherit',
- env: newEnv,
- });
-
- await new Promise((resolve) => child.on('close', resolve));
- process.exit(0);
-}
-import { runZedIntegration } from './zed-integration/zedIntegration.js';
-
-export function setupUnhandledRejectionHandler() {
- let unhandledRejectionOccurred = false;
- process.on('unhandledRejection', (reason, _promise) => {
- const errorMessage = `=========================================
-This is an unexpected error. Please file a bug report using the /bug tool.
-CRITICAL: Unhandled Promise Rejection!
-=========================================
-Reason: ${reason}${
- reason instanceof Error && reason.stack
- ? `
-Stack trace:
-${reason.stack}`
- : ''
- }`;
- appEvents.emit(AppEvent.LogError, errorMessage);
- if (!unhandledRejectionOccurred) {
- unhandledRejectionOccurred = true;
- appEvents.emit(AppEvent.OpenDebugConsole);
- }
- });
-}
-
-export async function main() {
- setupUnhandledRejectionHandler();
- const workspaceRoot = process.cwd();
- const settings = loadSettings(workspaceRoot);
-
- await cleanupCheckpoints();
- if (settings.errors.length > 0) {
- for (const error of settings.errors) {
- let errorMessage = `Error in ${error.path}: ${error.message}`;
- if (!process.env['NO_COLOR']) {
- errorMessage = `\x1b[31m${errorMessage}\x1b[0m`;
- }
- console.error(errorMessage);
- console.error(`Please fix ${error.path} and try again.`);
- }
- process.exit(1);
- }
-
- const argv = await parseArguments();
-
- const extensions = loadExtensions(workspaceRoot);
- const config = await loadCliConfig(
- settings.merged,
- extensions,
- sessionId,
- argv,
- );
-
- const consolePatcher = new ConsolePatcher({
- stderr: true,
- debugMode: config.getDebugMode(),
- });
- consolePatcher.patch();
- registerCleanup(consolePatcher.cleanup);
-
- dns.setDefaultResultOrder(
- validateDnsResolutionOrder(settings.merged.dnsResolutionOrder),
- );
-
- if (argv.promptInteractive && !process.stdin.isTTY) {
- console.error(
- 'Error: The --prompt-interactive flag is not supported when piping input from stdin.',
- );
- process.exit(1);
- }
-
- if (config.getListExtensions()) {
- console.log('Installed extensions:');
- for (const extension of extensions) {
- console.log(`- ${extension.config.name}`);
- }
- process.exit(0);
- }
-
- // Set a default auth type if one isn't set.
- if (!settings.merged.selectedAuthType) {
- if (process.env['CLOUD_SHELL'] === 'true') {
- settings.setValue(
- SettingScope.User,
- 'selectedAuthType',
- AuthType.CLOUD_SHELL,
- );
- }
- }
-
- setMaxSizedBoxDebugging(config.getDebugMode());
-
- await config.initialize();
-
- if (config.getIdeMode()) {
- await config.getIdeClient().connect();
- logIdeConnection(config, new IdeConnectionEvent(IdeConnectionType.START));
- }
-
- // Load custom themes from settings
- themeManager.loadCustomThemes(settings.merged.customThemes);
-
- if (settings.merged.theme) {
- if (!themeManager.setActiveTheme(settings.merged.theme)) {
- // If the theme is not found during initial load, log a warning and continue.
- // The useThemeCommand hook in App.tsx will handle opening the dialog.
- console.warn(`Warning: Theme "${settings.merged.theme}" not found.`);
- }
- }
-
- // hop into sandbox if we are outside and sandboxing is enabled
- if (!process.env['SANDBOX']) {
- const memoryArgs = settings.merged.autoConfigureMaxOldSpaceSize
- ? getNodeMemoryArgs(config)
- : [];
- const sandboxConfig = config.getSandbox();
- if (sandboxConfig) {
- if (
- settings.merged.selectedAuthType &&
- !settings.merged.useExternalAuth
- ) {
- // If a Grok key is present, skip legacy Google auth validation and let provider auto-select.
- const hasGrokKey = !!process.env['GROK_API_KEY'];
- try {
- if (!hasGrokKey) {
- // Validate authentication here because the sandbox will interfere with the Oauth2 web redirect.
- const err = validateAuthMethod(
- settings.merged.selectedAuthType,
- settings,
- );
- if (err) {
- throw new Error(err);
- }
- }
- await config.refreshAuth(settings.merged.selectedAuthType);
- } catch (err) {
- console.error('Error authenticating:', err);
- process.exit(1);
- }
- }
- await start_sandbox(sandboxConfig, memoryArgs, config);
- process.exit(0);
- } else {
- // Not in a sandbox and not entering one, so relaunch with additional
- // arguments to control memory usage if needed.
- if (memoryArgs.length > 0) {
- await relaunchWithAdditionalArgs(memoryArgs);
- process.exit(0);
- }
- }
- }
-
- if (
- settings.merged.selectedAuthType === AuthType.LOGIN_WITH_GOOGLE &&
- config.isBrowserLaunchSuppressed()
- ) {
- // Do oauth before app renders to make copying the link possible.
- await getOauthClient(settings.merged.selectedAuthType, config);
- }
-
- if (config.getExperimentalZedIntegration()) {
- return runZedIntegration(config, settings, extensions, argv);
- }
-
- let input = config.getQuestion();
- const startupWarnings = [
- ...(await getStartupWarnings()),
- ...(await getUserStartupWarnings(workspaceRoot)),
- ];
-
- // Render UI, passing necessary config values. Check that there is no command line question.
- if (config.isInteractive()) {
- const version = await getCliVersion();
- // Detect and enable Kitty keyboard protocol once at startup
- await detectAndEnableKittyProtocol();
- setWindowTitle(basename(workspaceRoot), settings);
- const instance = render(
-
-
-
-
- ,
- { exitOnCtrlC: false },
- );
-
- checkForUpdates()
- .then((info) => {
- handleAutoUpdate(info, settings, config.getProjectRoot());
- })
- .catch((err) => {
- // Silently ignore update check errors.
- if (config.getDebugMode()) {
- console.error('Update check failed:', err);
- }
- });
-
- registerCleanup(() => instance.unmount());
- return;
- }
- // If not a TTY, read from stdin
- // This is for cases where the user pipes input directly into the command
- if (!process.stdin.isTTY) {
- const stdinData = await readStdin();
- if (stdinData) {
- input = `${stdinData}\n\n${input}`;
- }
- }
- if (!input) {
- console.error('No input provided via stdin.');
- process.exit(1);
- }
-
- const prompt_id = Math.random().toString(16).slice(2);
- logUserPrompt(config, {
- 'event.name': 'user_prompt',
- 'event.timestamp': new Date().toISOString(),
- prompt: input,
- prompt_id,
- auth_type: config.getContentGeneratorConfig()?.authType,
- prompt_length: input.length,
- });
-
- const nonInteractiveConfig = await validateNonInteractiveAuth(
- settings.merged.selectedAuthType,
- settings.merged.useExternalAuth,
- config,
- );
-
- await runNonInteractive(nonInteractiveConfig, input, prompt_id);
- process.exit(0);
-}
-
-function setWindowTitle(title: string, settings: LoadedSettings) {
- if (!settings.merged.hideWindowTitle) {
- const windowTitle = (process.env['CLI_TITLE'] || `Anus - ${title}`).replace(
- // eslint-disable-next-line no-control-regex
- /[\x00-\x1F\x7F]/g,
- '',
- );
- process.stdout.write(`\x1b]2;${windowTitle}\x07`);
-
- process.on('exit', () => {
- process.stdout.write(`\x1b]2;\x07`);
- });
- }
-}
diff --git a/packages/cli/src/commands/mcp.test.ts b/packages/cli/src/commands/mcp.test.ts
deleted file mode 100644
index 296d896..0000000
--- a/packages/cli/src/commands/mcp.test.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect, vi } from 'vitest';
-import { mcpCommand } from './mcp.js';
-import { type Argv } from 'yargs';
-import yargs from 'yargs';
-
-describe('mcp command', () => {
- it('should have correct command definition', () => {
- expect(mcpCommand.command).toBe('mcp');
- expect(mcpCommand.describe).toBe('Manage MCP servers');
- expect(typeof mcpCommand.builder).toBe('function');
- expect(typeof mcpCommand.handler).toBe('function');
- });
-
- it('should have exactly one option (help flag)', () => {
- // Test to ensure that the global 'anus' flags are not added to the mcp command
- const yargsInstance = yargs();
- const builtYargs = mcpCommand.builder(yargsInstance);
- const options = builtYargs.getOptions();
-
- // Should have exactly 1 option (help flag)
- expect(Object.keys(options.key).length).toBe(1);
- expect(options.key).toHaveProperty('help');
- });
-
- it('should register add, remove, and list subcommands', () => {
- const mockYargs = {
- command: vi.fn().mockReturnThis(),
- demandCommand: vi.fn().mockReturnThis(),
- version: vi.fn().mockReturnThis(),
- };
-
- mcpCommand.builder(mockYargs as unknown as Argv);
-
- expect(mockYargs.command).toHaveBeenCalledTimes(3);
-
- // Verify that the specific subcommands are registered
- const commandCalls = mockYargs.command.mock.calls;
- const commandNames = commandCalls.map((call) => call[0].command);
-
- expect(commandNames).toContain('add [args...]');
- expect(commandNames).toContain('remove ');
- expect(commandNames).toContain('list');
-
- expect(mockYargs.demandCommand).toHaveBeenCalledWith(
- 1,
- 'You need at least one command before continuing.',
- );
- });
-});
diff --git a/packages/cli/src/commands/mcp.ts b/packages/cli/src/commands/mcp.ts
deleted file mode 100644
index 72b4c59..0000000
--- a/packages/cli/src/commands/mcp.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-// File for 'anus mcp' command
-import type { CommandModule, Argv } from 'yargs';
-import { addCommand } from './mcp/add.js';
-import { removeCommand } from './mcp/remove.js';
-import { listCommand } from './mcp/list.js';
-
-export const mcpCommand: CommandModule = {
- command: 'mcp',
- describe: 'Manage MCP servers',
- builder: (yargs: Argv) =>
- yargs
- .command(addCommand)
- .command(removeCommand)
- .command(listCommand)
- .demandCommand(1, 'You need at least one command before continuing.')
- .version(false),
- handler: () => {
- // yargs will automatically show help if no subcommand is provided
- // thanks to demandCommand(1) in the builder.
- },
-};
diff --git a/packages/cli/src/commands/mcp/add.test.ts b/packages/cli/src/commands/mcp/add.test.ts
deleted file mode 100644
index 05e5712..0000000
--- a/packages/cli/src/commands/mcp/add.test.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import yargs from 'yargs';
-import { addCommand } from './add.js';
-import { loadSettings, SettingScope } from '../../config/settings.js';
-
-vi.mock('fs/promises', () => ({
- readFile: vi.fn(),
- writeFile: vi.fn(),
-}));
-
-vi.mock('../../config/settings.js', async () => {
- const actual = await vi.importActual('../../config/settings.js');
- return {
- ...actual,
- loadSettings: vi.fn(),
- };
-});
-
-const mockedLoadSettings = loadSettings as vi.Mock;
-
-describe('mcp add command', () => {
- let parser: yargs.Argv;
- let mockSetValue: vi.Mock;
-
- beforeEach(() => {
- vi.resetAllMocks();
- const yargsInstance = yargs([]).command(addCommand);
- parser = yargsInstance;
- mockSetValue = vi.fn();
- mockedLoadSettings.mockReturnValue({
- forScope: () => ({ settings: {} }),
- setValue: mockSetValue,
- });
- });
-
- it('should add a stdio server to project settings', async () => {
- await parser.parseAsync(
- 'add my-server /path/to/server arg1 arg2 -e FOO=bar',
- );
-
- expect(mockSetValue).toHaveBeenCalledWith(
- SettingScope.Workspace,
- 'mcpServers',
- {
- 'my-server': {
- command: '/path/to/server',
- args: ['arg1', 'arg2'],
- env: { FOO: 'bar' },
- },
- },
- );
- });
-
- it('should add an sse server to user settings', async () => {
- await parser.parseAsync(
- 'add --transport sse sse-server https://example.com/sse-endpoint --scope user -H "X-API-Key: your-key"',
- );
-
- expect(mockSetValue).toHaveBeenCalledWith(SettingScope.User, 'mcpServers', {
- 'sse-server': {
- url: 'https://example.com/sse-endpoint',
- headers: { 'X-API-Key': 'your-key' },
- },
- });
- });
-
- it('should add an http server to project settings', async () => {
- await parser.parseAsync(
- 'add --transport http http-server https://example.com/mcp -H "Authorization: Bearer your-token"',
- );
-
- expect(mockSetValue).toHaveBeenCalledWith(
- SettingScope.Workspace,
- 'mcpServers',
- {
- 'http-server': {
- httpUrl: 'https://example.com/mcp',
- headers: { Authorization: 'Bearer your-token' },
- },
- },
- );
- });
-
- it('should handle MCP server args with -- separator', async () => {
- await parser.parseAsync(
- 'add my-server npx -- -y http://example.com/some-package',
- );
-
- expect(mockSetValue).toHaveBeenCalledWith(
- SettingScope.Workspace,
- 'mcpServers',
- {
- 'my-server': {
- command: 'npx',
- args: ['-y', 'http://example.com/some-package'],
- },
- },
- );
- });
-
- it('should handle unknown options as MCP server args', async () => {
- await parser.parseAsync(
- 'add test-server npx -y http://example.com/some-package',
- );
-
- expect(mockSetValue).toHaveBeenCalledWith(
- SettingScope.Workspace,
- 'mcpServers',
- {
- 'test-server': {
- command: 'npx',
- args: ['-y', 'http://example.com/some-package'],
- },
- },
- );
- });
-});
diff --git a/packages/cli/src/commands/mcp/add.ts b/packages/cli/src/commands/mcp/add.ts
deleted file mode 100644
index e7b0c0b..0000000
--- a/packages/cli/src/commands/mcp/add.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-// File for 'anus mcp add' command
-import type { CommandModule } from 'yargs';
-import { loadSettings, SettingScope } from '../../config/settings.js';
-import { MCPServerConfig } from '@anus-dev/anus-core';
-
-async function addMcpServer(
- name: string,
- commandOrUrl: string,
- args: Array | undefined,
- options: {
- scope: string;
- transport: string;
- env: string[] | undefined;
- header: string[] | undefined;
- timeout?: number;
- trust?: boolean;
- description?: string;
- includeTools?: string[];
- excludeTools?: string[];
- },
-) {
- const {
- scope,
- transport,
- env,
- header,
- timeout,
- trust,
- description,
- includeTools,
- excludeTools,
- } = options;
- const settingsScope =
- scope === 'user' ? SettingScope.User : SettingScope.Workspace;
- const settings = loadSettings(process.cwd());
-
- let newServer: Partial = {};
-
- const headers = header?.reduce(
- (acc, curr) => {
- const [key, ...valueParts] = curr.split(':');
- const value = valueParts.join(':').trim();
- if (key.trim() && value) {
- acc[key.trim()] = value;
- }
- return acc;
- },
- {} as Record,
- );
-
- switch (transport) {
- case 'sse':
- newServer = {
- url: commandOrUrl,
- headers,
- timeout,
- trust,
- description,
- includeTools,
- excludeTools,
- };
- break;
- case 'http':
- newServer = {
- httpUrl: commandOrUrl,
- headers,
- timeout,
- trust,
- description,
- includeTools,
- excludeTools,
- };
- break;
- case 'stdio':
- default:
- newServer = {
- command: commandOrUrl,
- args: args?.map(String),
- env: env?.reduce(
- (acc, curr) => {
- const [key, value] = curr.split('=');
- if (key && value) {
- acc[key] = value;
- }
- return acc;
- },
- {} as Record,
- ),
- timeout,
- trust,
- description,
- includeTools,
- excludeTools,
- };
- break;
- }
-
- const existingSettings = settings.forScope(settingsScope).settings;
- const mcpServers = existingSettings.mcpServers || {};
-
- const isExistingServer = !!mcpServers[name];
- if (isExistingServer) {
- console.log(
- `MCP server "${name}" is already configured within ${scope} settings.`,
- );
- }
-
- mcpServers[name] = newServer as MCPServerConfig;
-
- settings.setValue(settingsScope, 'mcpServers', mcpServers);
-
- if (isExistingServer) {
- console.log(`MCP server "${name}" updated in ${scope} settings.`);
- } else {
- console.log(
- `MCP server "${name}" added to ${scope} settings. (${transport})`,
- );
- }
-}
-
-export const addCommand: CommandModule = {
- command: 'add [args...]',
- describe: 'Add a server',
- builder: (yargs) =>
- yargs
- .usage('Usage: anus mcp add [options] [args...]')
- .parserConfiguration({
- 'unknown-options-as-args': true, // Pass unknown options as server args
- 'populate--': true, // Populate server args after -- separator
- })
- .positional('name', {
- describe: 'Name of the server',
- type: 'string',
- demandOption: true,
- })
- .positional('commandOrUrl', {
- describe: 'Command (stdio) or URL (sse, http)',
- type: 'string',
- demandOption: true,
- })
- .option('scope', {
- alias: 's',
- describe: 'Configuration scope (user or project)',
- type: 'string',
- default: 'project',
- choices: ['user', 'project'],
- })
- .option('transport', {
- alias: 't',
- describe: 'Transport type (stdio, sse, http)',
- type: 'string',
- default: 'stdio',
- choices: ['stdio', 'sse', 'http'],
- })
- .option('env', {
- alias: 'e',
- describe: 'Set environment variables (e.g. -e KEY=value)',
- type: 'array',
- string: true,
- })
- .option('header', {
- alias: 'H',
- describe:
- 'Set HTTP headers for SSE and HTTP transports (e.g. -H "X-Api-Key: abc123" -H "Authorization: Bearer abc123")',
- type: 'array',
- string: true,
- })
- .option('timeout', {
- describe: 'Set connection timeout in milliseconds',
- type: 'number',
- })
- .option('trust', {
- describe:
- 'Trust the server (bypass all tool call confirmation prompts)',
- type: 'boolean',
- })
- .option('description', {
- describe: 'Set the description for the server',
- type: 'string',
- })
- .option('include-tools', {
- describe: 'A comma-separated list of tools to include',
- type: 'array',
- string: true,
- })
- .option('exclude-tools', {
- describe: 'A comma-separated list of tools to exclude',
- type: 'array',
- string: true,
- })
- .middleware((argv) => {
- // Handle -- separator args as server args if present
- if (argv['--']) {
- const existingArgs = (argv['args'] as Array) || [];
- argv['args'] = [...existingArgs, ...(argv['--'] as string[])];
- }
- }),
- handler: async (argv) => {
- await addMcpServer(
- argv['name'] as string,
- argv['commandOrUrl'] as string,
- argv['args'] as Array,
- {
- scope: argv['scope'] as string,
- transport: argv['transport'] as string,
- env: argv['env'] as string[],
- header: argv['header'] as string[],
- timeout: argv['timeout'] as number | undefined,
- trust: argv['trust'] as boolean | undefined,
- description: argv['description'] as string | undefined,
- includeTools: argv['includeTools'] as string[] | undefined,
- excludeTools: argv['excludeTools'] as string[] | undefined,
- },
- );
- },
-};
diff --git a/packages/cli/src/commands/mcp/list.test.ts b/packages/cli/src/commands/mcp/list.test.ts
deleted file mode 100644
index 2533acc..0000000
--- a/packages/cli/src/commands/mcp/list.test.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
-import { listMcpServers } from './list.js';
-import { loadSettings } from '../../config/settings.js';
-import { loadExtensions } from '../../config/extension.js';
-import { createTransport } from '@anus-dev/anus-core';
-import { Client } from '@modelcontextprotocol/sdk/client/index.js';
-
-vi.mock('../../config/settings.js');
-vi.mock('../../config/extension.js');
-vi.mock('@anus-dev/anus-core');
-vi.mock('@modelcontextprotocol/sdk/client/index.js');
-
-const mockedLoadSettings = loadSettings as vi.Mock;
-const mockedLoadExtensions = loadExtensions as vi.Mock;
-const mockedCreateTransport = createTransport as vi.Mock;
-const MockedClient = Client as vi.Mock;
-
-interface MockClient {
- connect: vi.Mock;
- ping: vi.Mock;
- close: vi.Mock;
-}
-
-interface MockTransport {
- close: vi.Mock;
-}
-
-describe('mcp list command', () => {
- let consoleSpy: vi.SpyInstance;
- let mockClient: MockClient;
- let mockTransport: MockTransport;
-
- beforeEach(() => {
- vi.resetAllMocks();
-
- consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
-
- mockTransport = { close: vi.fn() };
- mockClient = {
- connect: vi.fn(),
- ping: vi.fn(),
- close: vi.fn(),
- };
-
- MockedClient.mockImplementation(() => mockClient);
- mockedCreateTransport.mockResolvedValue(mockTransport);
- mockedLoadExtensions.mockReturnValue([]);
- });
-
- afterEach(() => {
- consoleSpy.mockRestore();
- });
-
- it('should display message when no servers configured', async () => {
- mockedLoadSettings.mockReturnValue({ merged: { mcpServers: {} } });
-
- await listMcpServers();
-
- expect(consoleSpy).toHaveBeenCalledWith('No MCP servers configured.');
- });
-
- it('should display different server types with connected status', async () => {
- mockedLoadSettings.mockReturnValue({
- merged: {
- mcpServers: {
- 'stdio-server': { command: '/path/to/server', args: ['arg1'] },
- 'sse-server': { url: 'https://example.com/sse' },
- 'http-server': { httpUrl: 'https://example.com/http' },
- },
- },
- });
-
- mockClient.connect.mockResolvedValue(undefined);
- mockClient.ping.mockResolvedValue(undefined);
-
- await listMcpServers();
-
- expect(consoleSpy).toHaveBeenCalledWith('Configured MCP servers:\n');
- expect(consoleSpy).toHaveBeenCalledWith(
- expect.stringContaining(
- 'stdio-server: /path/to/server arg1 (stdio) - Connected',
- ),
- );
- expect(consoleSpy).toHaveBeenCalledWith(
- expect.stringContaining(
- 'sse-server: https://example.com/sse (sse) - Connected',
- ),
- );
- expect(consoleSpy).toHaveBeenCalledWith(
- expect.stringContaining(
- 'http-server: https://example.com/http (http) - Connected',
- ),
- );
- });
-
- it('should display disconnected status when connection fails', async () => {
- mockedLoadSettings.mockReturnValue({
- merged: {
- mcpServers: {
- 'test-server': { command: '/test/server' },
- },
- },
- });
-
- mockClient.connect.mockRejectedValue(new Error('Connection failed'));
-
- await listMcpServers();
-
- expect(consoleSpy).toHaveBeenCalledWith(
- expect.stringContaining(
- 'test-server: /test/server (stdio) - Disconnected',
- ),
- );
- });
-
- it('should merge extension servers with config servers', async () => {
- mockedLoadSettings.mockReturnValue({
- merged: {
- mcpServers: { 'config-server': { command: '/config/server' } },
- },
- });
-
- mockedLoadExtensions.mockReturnValue([
- {
- config: {
- name: 'test-extension',
- mcpServers: { 'extension-server': { command: '/ext/server' } },
- },
- },
- ]);
-
- mockClient.connect.mockResolvedValue(undefined);
- mockClient.ping.mockResolvedValue(undefined);
-
- await listMcpServers();
-
- expect(consoleSpy).toHaveBeenCalledWith(
- expect.stringContaining(
- 'config-server: /config/server (stdio) - Connected',
- ),
- );
- expect(consoleSpy).toHaveBeenCalledWith(
- expect.stringContaining(
- 'extension-server: /ext/server (stdio) - Connected',
- ),
- );
- });
-});
diff --git a/packages/cli/src/commands/mcp/list.ts b/packages/cli/src/commands/mcp/list.ts
deleted file mode 100644
index f97276c..0000000
--- a/packages/cli/src/commands/mcp/list.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-// File for 'anus mcp list' command
-import type { CommandModule } from 'yargs';
-import { loadSettings } from '../../config/settings.js';
-import {
- MCPServerConfig,
- MCPServerStatus,
- createTransport,
-} from '@anus-dev/anus-core';
-import { Client } from '@modelcontextprotocol/sdk/client/index.js';
-import { loadExtensions } from '../../config/extension.js';
-
-const COLOR_GREEN = '\u001b[32m';
-const COLOR_YELLOW = '\u001b[33m';
-const COLOR_RED = '\u001b[31m';
-const RESET_COLOR = '\u001b[0m';
-
-async function getMcpServersFromConfig(): Promise<
- Record
-> {
- const settings = loadSettings(process.cwd());
- const extensions = loadExtensions(process.cwd());
- const mcpServers = { ...(settings.merged.mcpServers || {}) };
- for (const extension of extensions) {
- Object.entries(extension.config.mcpServers || {}).forEach(
- ([key, server]) => {
- if (mcpServers[key]) {
- return;
- }
- mcpServers[key] = {
- ...server,
- extensionName: extension.config.name,
- };
- },
- );
- }
- return mcpServers;
-}
-
-async function testMCPConnection(
- serverName: string,
- config: MCPServerConfig,
-): Promise {
- const client = new Client({
- name: 'mcp-test-client',
- version: '0.0.1',
- });
-
- let transport;
- try {
- // Use the same transport creation logic as core
- transport = await createTransport(serverName, config, false);
- } catch (_error) {
- await client.close();
- return MCPServerStatus.DISCONNECTED;
- }
-
- try {
- // Attempt actual MCP connection with short timeout
- await client.connect(transport, { timeout: 5000 }); // 5s timeout
-
- // Test basic MCP protocol by pinging the server
- await client.ping();
-
- await client.close();
- return MCPServerStatus.CONNECTED;
- } catch (_error) {
- await transport.close();
- return MCPServerStatus.DISCONNECTED;
- }
-}
-
-async function getServerStatus(
- serverName: string,
- server: MCPServerConfig,
-): Promise {
- // Test all server types by attempting actual connection
- return await testMCPConnection(serverName, server);
-}
-
-export async function listMcpServers(): Promise {
- const mcpServers = await getMcpServersFromConfig();
- const serverNames = Object.keys(mcpServers);
-
- if (serverNames.length === 0) {
- console.log('No MCP servers configured.');
- return;
- }
-
- console.log('Configured MCP servers:\n');
-
- for (const serverName of serverNames) {
- const server = mcpServers[serverName];
-
- const status = await getServerStatus(serverName, server);
-
- let statusIndicator = '';
- let statusText = '';
- switch (status) {
- case MCPServerStatus.CONNECTED:
- statusIndicator = COLOR_GREEN + '✓' + RESET_COLOR;
- statusText = 'Connected';
- break;
- case MCPServerStatus.CONNECTING:
- statusIndicator = COLOR_YELLOW + '…' + RESET_COLOR;
- statusText = 'Connecting';
- break;
- case MCPServerStatus.DISCONNECTED:
- default:
- statusIndicator = COLOR_RED + '✗' + RESET_COLOR;
- statusText = 'Disconnected';
- break;
- }
-
- let serverInfo = `${serverName}: `;
- if (server.httpUrl) {
- serverInfo += `${server.httpUrl} (http)`;
- } else if (server.url) {
- serverInfo += `${server.url} (sse)`;
- } else if (server.command) {
- serverInfo += `${server.command} ${server.args?.join(' ') || ''} (stdio)`;
- }
-
- console.log(`${statusIndicator} ${serverInfo} - ${statusText}`);
- }
-}
-
-export const listCommand: CommandModule = {
- command: 'list',
- describe: 'List all configured MCP servers',
- handler: async () => {
- await listMcpServers();
- },
-};
diff --git a/packages/cli/src/commands/mcp/remove.test.ts b/packages/cli/src/commands/mcp/remove.test.ts
deleted file mode 100644
index 7730093..0000000
--- a/packages/cli/src/commands/mcp/remove.test.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { vi, describe, it, expect, beforeEach } from 'vitest';
-import yargs from 'yargs';
-import { loadSettings, SettingScope } from '../../config/settings.js';
-import { removeCommand } from './remove.js';
-
-vi.mock('fs/promises', () => ({
- readFile: vi.fn(),
- writeFile: vi.fn(),
-}));
-
-vi.mock('../../config/settings.js', async () => {
- const actual = await vi.importActual('../../config/settings.js');
- return {
- ...actual,
- loadSettings: vi.fn(),
- };
-});
-
-const mockedLoadSettings = loadSettings as vi.Mock;
-
-describe('mcp remove command', () => {
- let parser: yargs.Argv;
- let mockSetValue: vi.Mock;
- let mockSettings: Record;
-
- beforeEach(() => {
- vi.resetAllMocks();
- const yargsInstance = yargs([]).command(removeCommand);
- parser = yargsInstance;
- mockSetValue = vi.fn();
- mockSettings = {
- mcpServers: {
- 'test-server': {
- command: 'echo "hello"',
- },
- },
- };
- mockedLoadSettings.mockReturnValue({
- forScope: () => ({ settings: mockSettings }),
- setValue: mockSetValue,
- });
- });
-
- it('should remove a server from project settings', async () => {
- await parser.parseAsync('remove test-server');
-
- expect(mockSetValue).toHaveBeenCalledWith(
- SettingScope.Workspace,
- 'mcpServers',
- {},
- );
- });
-
- it('should show a message if server not found', async () => {
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
- await parser.parseAsync('remove non-existent-server');
-
- expect(mockSetValue).not.toHaveBeenCalled();
- expect(consoleSpy).toHaveBeenCalledWith(
- 'Server "non-existent-server" not found in project settings.',
- );
- });
-});
diff --git a/packages/cli/src/commands/mcp/remove.ts b/packages/cli/src/commands/mcp/remove.ts
deleted file mode 100644
index 1a7c325..0000000
--- a/packages/cli/src/commands/mcp/remove.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-// File for 'anus mcp remove' command
-import type { CommandModule } from 'yargs';
-import { loadSettings, SettingScope } from '../../config/settings.js';
-
-async function removeMcpServer(
- name: string,
- options: {
- scope: string;
- },
-) {
- const { scope } = options;
- const settingsScope =
- scope === 'user' ? SettingScope.User : SettingScope.Workspace;
- const settings = loadSettings(process.cwd());
-
- const existingSettings = settings.forScope(settingsScope).settings;
- const mcpServers = existingSettings.mcpServers || {};
-
- if (!mcpServers[name]) {
- console.log(`Server "${name}" not found in ${scope} settings.`);
- return;
- }
-
- delete mcpServers[name];
-
- settings.setValue(settingsScope, 'mcpServers', mcpServers);
-
- console.log(`Server "${name}" removed from ${scope} settings.`);
-}
-
-export const removeCommand: CommandModule = {
- command: 'remove ',
- describe: 'Remove a server',
- builder: (yargs) =>
- yargs
- .usage('Usage: anus mcp remove [options] ')
- .positional('name', {
- describe: 'Name of the server',
- type: 'string',
- demandOption: true,
- })
- .option('scope', {
- alias: 's',
- describe: 'Configuration scope (user or project)',
- type: 'string',
- default: 'project',
- choices: ['user', 'project'],
- }),
- handler: async (argv) => {
- await removeMcpServer(argv['name'] as string, {
- scope: argv['scope'] as string,
- });
- },
-};
diff --git a/packages/cli/src/config/auth.test.ts b/packages/cli/src/config/auth.test.ts
deleted file mode 100644
index afac440..0000000
--- a/packages/cli/src/config/auth.test.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { AuthType } from '@anus-dev/anus-core';
-import { vi, beforeEach, afterEach, describe, it, expect } from 'vitest';
-import { validateAuthMethod } from './auth.js';
-import type { LoadedSettings } from './settings.js';
-
-vi.mock('./settings.js', () => ({
- loadEnvironment: vi.fn(),
-}));
-
-describe('validateAuthMethod', () => {
- const originalEnv = process.env;
-
- beforeEach(() => {
- vi.resetModules();
- process.env = {};
- });
-
- afterEach(() => {
- process.env = originalEnv;
- });
-
- describe('USE_GROK', () => {
- it('should return null if GROK_API_KEY is set', () => {
- process.env['GROK_API_KEY'] = 'test-key';
- expect(validateAuthMethod(AuthType.USE_GROK)).toBeNull();
- });
-
- it('should return null if XAI_API_KEY is set', () => {
- process.env['XAI_API_KEY'] = 'test-key';
- expect(validateAuthMethod(AuthType.USE_GROK)).toBeNull();
- });
-
- it('should return null if settings has grok api key', () => {
- const settings: LoadedSettings = {
- merged: {
- grok: {
- apiKey: 'test-key-from-settings',
- },
- },
- } as LoadedSettings;
- expect(validateAuthMethod(AuthType.USE_GROK, settings)).toBeNull();
- });
-
- it('should return an error message if no grok api key is found', () => {
- expect(validateAuthMethod(AuthType.USE_GROK)).toBe(
- 'OpenRouter API key not found. Please set your GROK_API_KEY environment variable or configure it in settings. Get your API key from https://openrouter.ai/keys',
- );
- });
-
- it('should export settings key to environment when available', () => {
- const settings: LoadedSettings = {
- merged: {
- grok: {
- apiKey: 'test-key-from-settings',
- },
- },
- } as LoadedSettings;
-
- validateAuthMethod(AuthType.USE_GROK, settings);
- expect(process.env['GROK_API_KEY']).toBe('test-key-from-settings');
- });
- });
-
- it('should return an error message for any non-Grok auth method', () => {
- expect(validateAuthMethod(AuthType.LOGIN_WITH_GOOGLE)).toBe(
- 'Invalid auth method selected.',
- );
- expect(validateAuthMethod(AuthType.USE_VERTEX_AI)).toBe(
- 'Invalid auth method selected.',
- );
- expect(validateAuthMethod('invalid-method')).toBe(
- 'Invalid auth method selected.',
- );
- });
-});
diff --git a/packages/cli/src/config/auth.ts b/packages/cli/src/config/auth.ts
deleted file mode 100644
index c79abc5..0000000
--- a/packages/cli/src/config/auth.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { AuthType } from '@anus-dev/anus-core';
-import { loadEnvironment, LoadedSettings } from './settings.js';
-
-export const validateAuthMethod = (
- authMethod: string,
- settings?: LoadedSettings,
-): string | null => {
- loadEnvironment();
-
- if (authMethod === AuthType.USE_GROK) {
- // Check both environment variable and settings
- const hasEnvKey =
- !!process.env['GROK_API_KEY'] || !!process.env['XAI_API_KEY'];
- const hasSettingsKey = !!settings?.merged.grok?.apiKey;
-
- // If we have a settings key, ensure it gets exported to environment
- if (hasSettingsKey && !hasEnvKey) {
- process.env['GROK_API_KEY'] = settings!.merged.grok!.apiKey;
- }
-
- if (!hasEnvKey && !hasSettingsKey) {
- return 'OpenRouter API key not found. Please set your GROK_API_KEY environment variable or configure it in settings. Get your API key from https://openrouter.ai/keys';
- }
- return null;
- }
-
- return 'Invalid auth method selected.';
-};
diff --git a/packages/cli/src/config/config.integration.test.ts b/packages/cli/src/config/config.integration.test.ts
deleted file mode 100644
index 7528373..0000000
--- a/packages/cli/src/config/config.integration.test.ts
+++ /dev/null
@@ -1,408 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
-import * as fs from 'fs';
-import * as path from 'path';
-import { tmpdir } from 'os';
-import {
- Config,
- ConfigParameters,
- ContentGeneratorConfig,
-} from '@anus-dev/anus-core';
-import { http, HttpResponse } from 'msw';
-import { setupServer } from 'msw/node';
-
-export const server = setupServer();
-
-// TODO(richieforeman): Consider moving this to test setup globally.
-beforeAll(() => {
- server.listen({});
-});
-
-afterEach(() => {
- server.resetHandlers();
-});
-
-afterAll(() => {
- server.close();
-});
-
-const CLEARCUT_URL = 'https://play.googleapis.com/log';
-
-const TEST_CONTENT_GENERATOR_CONFIG: ContentGeneratorConfig = {
- apiKey: 'test-key',
- model: 'test-model',
- userAgent: 'test-agent',
-};
-
-// Mock file discovery service and tool registry
-vi.mock('@anus-dev/anus-core', async () => {
- const actual = await vi.importActual('@anus-dev/anus-core');
- return {
- ...actual,
- FileDiscoveryService: vi.fn().mockImplementation(() => ({
- initialize: vi.fn(),
- })),
- createToolRegistry: vi.fn().mockResolvedValue({}),
- };
-});
-
-describe('Configuration Integration Tests', () => {
- let tempDir: string;
-
- beforeEach(() => {
- server.resetHandlers(http.post(CLEARCUT_URL, () => HttpResponse.text()));
-
- tempDir = fs.mkdtempSync(path.join(tmpdir(), 'anus-cli-test-'));
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- vi.clearAllMocks();
- });
-
- afterEach(() => {
- vi.unstubAllEnvs();
- if (fs.existsSync(tempDir)) {
- fs.rmSync(tempDir, { recursive: true });
- }
- });
-
- describe('File Filtering Configuration', () => {
- it('should load default file filtering settings', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- fileFilteringRespectGitIgnore: undefined, // Should default to true
- };
-
- const config = new Config(configParams);
-
- expect(config.getFileFilteringRespectGitIgnore()).toBe(true);
- });
-
- it('should load custom file filtering settings from configuration', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- fileFiltering: {
- respectGitIgnore: false,
- },
- };
-
- const config = new Config(configParams);
-
- expect(config.getFileFilteringRespectGitIgnore()).toBe(false);
- });
-
- it('should merge user and workspace file filtering settings', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- fileFilteringRespectGitIgnore: true,
- };
-
- const config = new Config(configParams);
-
- expect(config.getFileFilteringRespectGitIgnore()).toBe(true);
- });
- });
-
- describe('Configuration Integration', () => {
- it('should handle partial configuration objects gracefully', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- fileFiltering: {
- respectGitIgnore: false,
- },
- };
-
- const config = new Config(configParams);
-
- // Specified settings should be applied
- expect(config.getFileFilteringRespectGitIgnore()).toBe(false);
- });
-
- it('should handle empty configuration objects gracefully', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- fileFilteringRespectGitIgnore: undefined,
- };
-
- const config = new Config(configParams);
-
- // All settings should use defaults
- expect(config.getFileFilteringRespectGitIgnore()).toBe(true);
- });
-
- it('should handle missing configuration sections gracefully', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- // Missing fileFiltering configuration
- };
-
- const config = new Config(configParams);
-
- // All git-aware settings should use defaults
- expect(config.getFileFilteringRespectGitIgnore()).toBe(true);
- });
- });
-
- describe('Real-world Configuration Scenarios', () => {
- it('should handle a security-focused configuration', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- fileFilteringRespectGitIgnore: true,
- };
-
- const config = new Config(configParams);
-
- expect(config.getFileFilteringRespectGitIgnore()).toBe(true);
- });
-
- it('should handle a CI/CD environment configuration', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- fileFiltering: {
- respectGitIgnore: false,
- }, // CI might need to see all files
- };
-
- const config = new Config(configParams);
-
- expect(config.getFileFilteringRespectGitIgnore()).toBe(false);
- });
- });
-
- describe('Checkpointing Configuration', () => {
- it('should enable checkpointing when the setting is true', async () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- checkpointing: true,
- };
-
- const config = new Config(configParams);
-
- expect(config.getCheckpointingEnabled()).toBe(true);
- });
- });
-
- describe('Extension Context Files', () => {
- it('should have an empty array for extension context files by default', () => {
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- };
- const config = new Config(configParams);
- expect(config.getExtensionContextFilePaths()).toEqual([]);
- });
-
- it('should correctly store and return extension context file paths', () => {
- const contextFiles = ['/path/to/file1.txt', '/path/to/file2.js'];
- const configParams: ConfigParameters = {
- cwd: '/tmp',
- contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
- embeddingModel: 'test-embedding-model',
- sandbox: false,
- targetDir: tempDir,
- debugMode: false,
- extensionContextFilePaths: contextFiles,
- };
- const config = new Config(configParams);
- expect(config.getExtensionContextFilePaths()).toEqual(contextFiles);
- });
- });
-
- describe('Approval Mode Integration Tests', () => {
- let parseArguments: typeof import('./config').parseArguments;
-
- beforeEach(async () => {
- // Import the argument parsing function for integration testing
- const { parseArguments: parseArgs } = await import('./config');
- parseArguments = parseArgs;
- });
-
- it('should parse --approval-mode=auto_edit correctly through the full argument parsing flow', async () => {
- const originalArgv = process.argv;
-
- try {
- process.argv = [
- 'node',
- 'script.js',
- '--approval-mode',
- 'auto_edit',
- '-p',
- 'test',
- ];
-
- const argv = await parseArguments();
-
- // Verify that the argument was parsed correctly
- expect(argv.approvalMode).toBe('auto_edit');
- expect(argv.prompt).toBe('test');
- expect(argv.yolo).toBe(false);
- } finally {
- process.argv = originalArgv;
- }
- });
-
- it('should parse --approval-mode=yolo correctly through the full argument parsing flow', async () => {
- const originalArgv = process.argv;
-
- try {
- process.argv = [
- 'node',
- 'script.js',
- '--approval-mode',
- 'yolo',
- '-p',
- 'test',
- ];
-
- const argv = await parseArguments();
-
- expect(argv.approvalMode).toBe('yolo');
- expect(argv.prompt).toBe('test');
- expect(argv.yolo).toBe(false); // Should NOT be set when using --approval-mode
- } finally {
- process.argv = originalArgv;
- }
- });
-
- it('should parse --approval-mode=default correctly through the full argument parsing flow', async () => {
- const originalArgv = process.argv;
-
- try {
- process.argv = [
- 'node',
- 'script.js',
- '--approval-mode',
- 'default',
- '-p',
- 'test',
- ];
-
- const argv = await parseArguments();
-
- expect(argv.approvalMode).toBe('default');
- expect(argv.prompt).toBe('test');
- expect(argv.yolo).toBe(false);
- } finally {
- process.argv = originalArgv;
- }
- });
-
- it('should parse legacy --yolo flag correctly', async () => {
- const originalArgv = process.argv;
-
- try {
- process.argv = ['node', 'script.js', '--yolo', '-p', 'test'];
-
- const argv = await parseArguments();
-
- expect(argv.yolo).toBe(true);
- expect(argv.approvalMode).toBeUndefined(); // Should NOT be set when using --yolo
- expect(argv.prompt).toBe('test');
- } finally {
- process.argv = originalArgv;
- }
- });
-
- it('should reject invalid approval mode values during argument parsing', async () => {
- const originalArgv = process.argv;
-
- try {
- process.argv = ['node', 'script.js', '--approval-mode', 'invalid_mode'];
-
- // Should throw during argument parsing due to yargs validation
- await expect(parseArguments()).rejects.toThrow();
- } finally {
- process.argv = originalArgv;
- }
- });
-
- it('should reject conflicting --yolo and --approval-mode flags', async () => {
- const originalArgv = process.argv;
-
- try {
- process.argv = [
- 'node',
- 'script.js',
- '--yolo',
- '--approval-mode',
- 'default',
- ];
-
- // Should throw during argument parsing due to conflict validation
- await expect(parseArguments()).rejects.toThrow();
- } finally {
- process.argv = originalArgv;
- }
- });
-
- it('should handle backward compatibility with mixed scenarios', async () => {
- const originalArgv = process.argv;
-
- try {
- // Test that no approval mode arguments defaults to no flags set
- process.argv = ['node', 'script.js', '-p', 'test'];
-
- const argv = await parseArguments();
-
- expect(argv.approvalMode).toBeUndefined();
- expect(argv.yolo).toBe(false);
- expect(argv.prompt).toBe('test');
- } finally {
- process.argv = originalArgv;
- }
- });
- });
-});
diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts
deleted file mode 100644
index a4893f1..0000000
--- a/packages/cli/src/config/config.test.ts
+++ /dev/null
@@ -1,1876 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
-import * as os from 'os';
-import * as fs from 'fs';
-import * as path from 'path';
-import { ShellTool, EditTool, WriteFileTool } from '@anus-dev/anus-core';
-import { loadCliConfig, parseArguments } from './config.js';
-import { Settings } from './settings.js';
-import { Extension } from './extension.js';
-import * as ServerConfig from '@anus-dev/anus-core';
-import { isWorkspaceTrusted } from './trustedFolders.js';
-
-// Historic issue references for test data - the trinity of comedy
-const _LEGENDARY_ISSUES = [39, 64, 74]; // LUBE, brownout, lubrication
-const _GENESIS_DATE = '2025-03-09'; // When the prompt was whispered
-
-vi.mock('./trustedFolders.js', () => ({
- isWorkspaceTrusted: vi.fn(),
-}));
-
-vi.mock('os', async (importOriginal) => {
- const actualOs = await importOriginal();
- return {
- ...actualOs,
- homedir: vi.fn(() => '/mock/home/user'),
- };
-});
-
-vi.mock('open', () => ({
- default: vi.fn(),
-}));
-
-vi.mock('read-package-up', () => ({
- readPackageUp: vi.fn(() =>
- Promise.resolve({ packageJson: { version: 'test-version' } }),
- ),
-}));
-
-vi.mock('@anus-dev/anus-core', async () => {
- const actualServer = await vi.importActual(
- '@anus-dev/anus-core',
- );
- return {
- ...actualServer,
- IdeClient: {
- getInstance: vi.fn().mockReturnValue({
- getConnectionStatus: vi.fn(),
- initialize: vi.fn(),
- shutdown: vi.fn(),
- }),
- },
- loadEnvironment: vi.fn(),
- loadServerHierarchicalMemory: vi.fn(
- (cwd, dirs, debug, fileService, extensionPaths, _maxDirs) =>
- Promise.resolve({
- memoryContent: extensionPaths?.join(',') || '',
- fileCount: extensionPaths?.length || 0,
- }),
- ),
- DEFAULT_MEMORY_FILE_FILTERING_OPTIONS: {
- respectGitIgnore: false,
- respectAnusIgnore: true,
- },
- DEFAULT_FILE_FILTERING_OPTIONS: {
- respectGitIgnore: true,
- respectAnusIgnore: true,
- },
- };
-});
-
-describe('parseArguments', () => {
- const originalArgv = process.argv;
-
- afterEach(() => {
- process.argv = originalArgv;
- });
-
- it('should throw an error when both --prompt and --prompt-interactive are used together', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--prompt',
- 'test prompt',
- '--prompt-interactive',
- 'interactive prompt',
- ];
-
- const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
- throw new Error('process.exit called');
- });
-
- const mockConsoleError = vi
- .spyOn(console, 'error')
- .mockImplementation(() => {});
-
- await expect(parseArguments()).rejects.toThrow('process.exit called');
-
- expect(mockConsoleError).toHaveBeenCalledWith(
- expect.stringContaining(
- 'Cannot use both --prompt (-p) and --prompt-interactive (-i) together',
- ),
- );
-
- mockExit.mockRestore();
- mockConsoleError.mockRestore();
- });
-
- it('should throw an error when using short flags -p and -i together', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '-p',
- 'test prompt',
- '-i',
- 'interactive prompt',
- ];
-
- const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
- throw new Error('process.exit called');
- });
-
- const mockConsoleError = vi
- .spyOn(console, 'error')
- .mockImplementation(() => {});
-
- await expect(parseArguments()).rejects.toThrow('process.exit called');
-
- expect(mockConsoleError).toHaveBeenCalledWith(
- expect.stringContaining(
- 'Cannot use both --prompt (-p) and --prompt-interactive (-i) together',
- ),
- );
-
- mockExit.mockRestore();
- mockConsoleError.mockRestore();
- });
-
- it('should allow --prompt without --prompt-interactive', async () => {
- process.argv = ['node', 'script.js', '--prompt', 'test prompt'];
- const argv = await parseArguments();
- expect(argv.prompt).toBe('test prompt');
- expect(argv.promptInteractive).toBeUndefined();
- });
-
- it('should allow --prompt-interactive without --prompt', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--prompt-interactive',
- 'interactive prompt',
- ];
- const argv = await parseArguments();
- expect(argv.promptInteractive).toBe('interactive prompt');
- expect(argv.prompt).toBeUndefined();
- });
-
- it('should allow -i flag as alias for --prompt-interactive', async () => {
- process.argv = ['node', 'script.js', '-i', 'interactive prompt'];
- const argv = await parseArguments();
- expect(argv.promptInteractive).toBe('interactive prompt');
- expect(argv.prompt).toBeUndefined();
- });
-
- it('should throw an error when both --yolo and --approval-mode are used together', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--yolo',
- '--approval-mode',
- 'default',
- ];
-
- const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
- throw new Error('process.exit called');
- });
-
- const mockConsoleError = vi
- .spyOn(console, 'error')
- .mockImplementation(() => {});
-
- await expect(parseArguments()).rejects.toThrow('process.exit called');
-
- expect(mockConsoleError).toHaveBeenCalledWith(
- expect.stringContaining(
- 'Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.',
- ),
- );
-
- mockExit.mockRestore();
- mockConsoleError.mockRestore();
- });
-
- it('should throw an error when using short flags -y and --approval-mode together', async () => {
- process.argv = ['node', 'script.js', '-y', '--approval-mode', 'yolo'];
-
- const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
- throw new Error('process.exit called');
- });
-
- const mockConsoleError = vi
- .spyOn(console, 'error')
- .mockImplementation(() => {});
-
- await expect(parseArguments()).rejects.toThrow('process.exit called');
-
- expect(mockConsoleError).toHaveBeenCalledWith(
- expect.stringContaining(
- 'Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.',
- ),
- );
-
- mockExit.mockRestore();
- mockConsoleError.mockRestore();
- });
-
- it('should allow --approval-mode without --yolo', async () => {
- process.argv = ['node', 'script.js', '--approval-mode', 'auto_edit'];
- const argv = await parseArguments();
- expect(argv.approvalMode).toBe('auto_edit');
- expect(argv.yolo).toBe(false);
- });
-
- it('should allow --yolo without --approval-mode', async () => {
- process.argv = ['node', 'script.js', '--yolo'];
- const argv = await parseArguments();
- expect(argv.yolo).toBe(true);
- expect(argv.approvalMode).toBeUndefined();
- });
-
- it('should reject invalid --approval-mode values', async () => {
- process.argv = ['node', 'script.js', '--approval-mode', 'invalid'];
-
- const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
- throw new Error('process.exit called');
- });
-
- const mockConsoleError = vi
- .spyOn(console, 'error')
- .mockImplementation(() => {});
-
- await expect(parseArguments()).rejects.toThrow('process.exit called');
-
- expect(mockConsoleError).toHaveBeenCalledWith(
- expect.stringContaining('Invalid values:'),
- );
-
- mockExit.mockRestore();
- mockConsoleError.mockRestore();
- });
-});
-
-describe('loadCliConfig', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should set showMemoryUsage to true when --show-memory-usage flag is present', async () => {
- process.argv = ['node', 'script.js', '--show-memory-usage'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getShowMemoryUsage()).toBe(true);
- });
-
- it('should set showMemoryUsage to false when --memory flag is not present', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getShowMemoryUsage()).toBe(false);
- });
-
- it('should set showMemoryUsage to false by default from settings if CLI flag is not present', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { showMemoryUsage: false };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getShowMemoryUsage()).toBe(false);
- });
-
- it('should prioritize CLI flag over settings for showMemoryUsage (CLI true, settings false)', async () => {
- process.argv = ['node', 'script.js', '--show-memory-usage'];
- const argv = await parseArguments();
- const settings: Settings = { showMemoryUsage: false };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getShowMemoryUsage()).toBe(true);
- });
-
- it(`should leave proxy to empty by default`, async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getProxy()).toBeFalsy();
- });
-
- const proxy_url = 'http://localhost:7890';
- const testCases = [
- {
- input: {
- env_name: 'https_proxy',
- proxy_url,
- },
- expected: proxy_url,
- },
- {
- input: {
- env_name: 'http_proxy',
- proxy_url,
- },
- expected: proxy_url,
- },
- {
- input: {
- env_name: 'HTTPS_PROXY',
- proxy_url,
- },
- expected: proxy_url,
- },
- {
- input: {
- env_name: 'HTTP_PROXY',
- proxy_url,
- },
- expected: proxy_url,
- },
- ];
- testCases.forEach(({ input, expected }) => {
- it(`should set proxy to ${expected} according to environment variable [${input.env_name}]`, async () => {
- vi.stubEnv(input.env_name, input.proxy_url);
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getProxy()).toBe(expected);
- });
- });
-
- it('should set proxy when --proxy flag is present', async () => {
- process.argv = ['node', 'script.js', '--proxy', 'http://localhost:7890'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getProxy()).toBe('http://localhost:7890');
- });
-
- it('should prioritize CLI flag over environment variable for proxy (CLI http://localhost:7890, environment variable http://localhost:7891)', async () => {
- vi.stubEnv('http_proxy', 'http://localhost:7891');
- process.argv = ['node', 'script.js', '--proxy', 'http://localhost:7890'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getProxy()).toBe('http://localhost:7890');
- });
-});
-
-describe('loadCliConfig telemetry', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should set telemetry to false by default when no flag or setting is present', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryEnabled()).toBe(false);
- });
-
- it('should set telemetry to true when --telemetry flag is present', async () => {
- process.argv = ['node', 'script.js', '--telemetry'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryEnabled()).toBe(true);
- });
-
- it('should set telemetry to false when --no-telemetry flag is present', async () => {
- process.argv = ['node', 'script.js', '--no-telemetry'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryEnabled()).toBe(false);
- });
-
- it('should use telemetry value from settings if CLI flag is not present (settings true)', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { enabled: true } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryEnabled()).toBe(true);
- });
-
- it('should use telemetry value from settings if CLI flag is not present (settings false)', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { enabled: false } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryEnabled()).toBe(false);
- });
-
- it('should prioritize --telemetry CLI flag (true) over settings (false)', async () => {
- process.argv = ['node', 'script.js', '--telemetry'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { enabled: false } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryEnabled()).toBe(true);
- });
-
- it('should prioritize --no-telemetry CLI flag (false) over settings (true)', async () => {
- process.argv = ['node', 'script.js', '--no-telemetry'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { enabled: true } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryEnabled()).toBe(false);
- });
-
- it('should use telemetry OTLP endpoint from settings if CLI flag is not present', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {
- telemetry: { otlpEndpoint: 'http://settings.example.com' },
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryOtlpEndpoint()).toBe(
- 'http://settings.example.com',
- );
- });
-
- it('should prioritize --telemetry-otlp-endpoint CLI flag over settings', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--telemetry-otlp-endpoint',
- 'http://cli.example.com',
- ];
- const argv = await parseArguments();
- const settings: Settings = {
- telemetry: { otlpEndpoint: 'http://settings.example.com' },
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryOtlpEndpoint()).toBe('http://cli.example.com');
- });
-
- it('should use default endpoint if no OTLP endpoint is provided via CLI or settings', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { enabled: true } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryOtlpEndpoint()).toBe('http://localhost:4317');
- });
-
- it('should use telemetry target from settings if CLI flag is not present', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {
- telemetry: { target: ServerConfig.DEFAULT_TELEMETRY_TARGET },
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryTarget()).toBe(
- ServerConfig.DEFAULT_TELEMETRY_TARGET,
- );
- });
-
- it('should prioritize --telemetry-target CLI flag over settings', async () => {
- process.argv = ['node', 'script.js', '--telemetry-target', 'gcp'];
- const argv = await parseArguments();
- const settings: Settings = {
- telemetry: { target: ServerConfig.DEFAULT_TELEMETRY_TARGET },
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryTarget()).toBe('gcp');
- });
-
- it('should use default target if no target is provided via CLI or settings', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { enabled: true } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryTarget()).toBe(
- ServerConfig.DEFAULT_TELEMETRY_TARGET,
- );
- });
-
- it('should use telemetry log prompts from settings if CLI flag is not present', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { logPrompts: false } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryLogPromptsEnabled()).toBe(false);
- });
-
- it('should prioritize --telemetry-log-prompts CLI flag (true) over settings (false)', async () => {
- process.argv = ['node', 'script.js', '--telemetry-log-prompts'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { logPrompts: false } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
- });
-
- it('should prioritize --no-telemetry-log-prompts CLI flag (false) over settings (true)', async () => {
- process.argv = ['node', 'script.js', '--no-telemetry-log-prompts'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { logPrompts: true } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryLogPromptsEnabled()).toBe(false);
- });
-
- it('should use default log prompts (true) if no value is provided via CLI or settings', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { enabled: true } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
- });
-
- it('should use telemetry OTLP protocol from settings if CLI flag is not present', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {
- telemetry: { otlpProtocol: 'http' },
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryOtlpProtocol()).toBe('http');
- });
-
- it('should prioritize --telemetry-otlp-protocol CLI flag over settings', async () => {
- process.argv = ['node', 'script.js', '--telemetry-otlp-protocol', 'http'];
- const argv = await parseArguments();
- const settings: Settings = {
- telemetry: { otlpProtocol: 'grpc' },
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryOtlpProtocol()).toBe('http');
- });
-
- it('should use default protocol if no OTLP protocol is provided via CLI or settings', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { telemetry: { enabled: true } };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getTelemetryOtlpProtocol()).toBe('grpc');
- });
-
- it('should reject invalid --telemetry-otlp-protocol values', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--telemetry-otlp-protocol',
- 'invalid',
- ];
-
- const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
- throw new Error('process.exit called');
- });
-
- const mockConsoleError = vi
- .spyOn(console, 'error')
- .mockImplementation(() => {});
-
- await expect(parseArguments()).rejects.toThrow('process.exit called');
-
- expect(mockConsoleError).toHaveBeenCalledWith(
- expect.stringContaining('Invalid values:'),
- );
-
- mockExit.mockRestore();
- mockConsoleError.mockRestore();
- });
-});
-
-describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- // Other common mocks would be reset here.
- });
-
- afterEach(() => {
- vi.restoreAllMocks();
- });
-
- it('should pass extension context file paths to loadServerHierarchicalMemory', async () => {
- process.argv = ['node', 'script.js'];
- const settings: Settings = {};
- const extensions: Extension[] = [
- {
- config: {
- name: 'ext1',
- version: '1.0.0',
- },
- contextFiles: ['/path/to/ext1/ANUS.md'],
- },
- {
- config: {
- name: 'ext2',
- version: '1.0.0',
- },
- contextFiles: [],
- },
- {
- config: {
- name: 'ext3',
- version: '1.0.0',
- },
- contextFiles: [
- '/path/to/ext3/context1.md',
- '/path/to/ext3/context2.md',
- ],
- },
- ];
- const argv = await parseArguments();
- await loadCliConfig(settings, extensions, 'session-id', argv);
- expect(ServerConfig.loadServerHierarchicalMemory).toHaveBeenCalledWith(
- expect.any(String),
- [],
- false,
- expect.any(Object),
- [
- '/path/to/ext1/ANUS.md',
- '/path/to/ext3/context1.md',
- '/path/to/ext3/context2.md',
- ],
- 'tree',
- {
- respectGitIgnore: false,
- respectAnusIgnore: true,
- },
- undefined, // maxDirs
- );
- });
-
- // NOTE TO FUTURE DEVELOPERS:
- // To re-enable tests for loadHierarchicalAnusMemory, ensure that:
- // 1. os.homedir() is reliably mocked *before* the config.ts module is loaded
- // and its functions (which use os.homedir()) are called.
- // 2. fs/promises and fs mocks correctly simulate file/directory existence,
- // readability, and content based on paths derived from the mocked os.homedir().
- // 3. Spies on console functions (for logger output) are correctly set up if needed.
- // Example of a previously failing test structure:
- /*
- it('should correctly use mocked homedir for global path', async () => {
- const MOCK_ANUS_DIR_LOCAL = path.join('/mock/home/user', '.anus');
- const MOCK_GLOBAL_PATH_LOCAL = path.join(MOCK_ANUS_DIR_LOCAL, 'ANUS.md');
- mockFs({
- [MOCK_GLOBAL_PATH_LOCAL]: { type: 'file', content: 'GlobalContentOnly' }
- });
- const memory = await loadHierarchicalAnusMemory("/some/other/cwd", false);
- expect(memory).toBe('GlobalContentOnly');
- expect(vi.mocked(os.homedir)).toHaveBeenCalled();
- expect(fsPromises.readFile).toHaveBeenCalledWith(MOCK_GLOBAL_PATH_LOCAL, 'utf-8');
- });
- */
-});
-
-describe('mergeMcpServers', () => {
- it('should not modify the original settings object', async () => {
- const settings: Settings = {
- mcpServers: {
- 'test-server': {
- url: 'http://localhost:8080',
- },
- },
- };
- const extensions: Extension[] = [
- {
- config: {
- name: 'ext1',
- version: '1.0.0',
- mcpServers: {
- 'ext1-server': {
- url: 'http://localhost:8081',
- },
- },
- },
- contextFiles: [],
- },
- ];
- const originalSettings = JSON.parse(JSON.stringify(settings));
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- await loadCliConfig(settings, extensions, 'test-session', argv);
- expect(settings).toEqual(originalSettings);
- });
-});
-
-describe('mergeExcludeTools', () => {
- const defaultExcludes = [ShellTool.Name, EditTool.Name, WriteFileTool.Name];
- const originalIsTTY = process.stdin.isTTY;
-
- beforeEach(() => {
- process.stdin.isTTY = true;
- });
-
- afterEach(() => {
- process.stdin.isTTY = originalIsTTY;
- });
-
- it('should merge excludeTools from settings and extensions', async () => {
- const settings: Settings = { excludeTools: ['tool1', 'tool2'] };
- const extensions: Extension[] = [
- {
- config: {
- name: 'ext1',
- version: '1.0.0',
- excludeTools: ['tool3', 'tool4'],
- },
- contextFiles: [],
- },
- {
- config: {
- name: 'ext2',
- version: '1.0.0',
- excludeTools: ['tool5'],
- },
- contextFiles: [],
- },
- ];
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
- expect(config.getExcludeTools()).toEqual(
- expect.arrayContaining(['tool1', 'tool2', 'tool3', 'tool4', 'tool5']),
- );
- expect(config.getExcludeTools()).toHaveLength(5);
- });
-
- it('should handle overlapping excludeTools between settings and extensions', async () => {
- const settings: Settings = { excludeTools: ['tool1', 'tool2'] };
- const extensions: Extension[] = [
- {
- config: {
- name: 'ext1',
- version: '1.0.0',
- excludeTools: ['tool2', 'tool3'],
- },
- contextFiles: [],
- },
- ];
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
- expect(config.getExcludeTools()).toEqual(
- expect.arrayContaining(['tool1', 'tool2', 'tool3']),
- );
- expect(config.getExcludeTools()).toHaveLength(3);
- });
-
- it('should handle overlapping excludeTools between extensions', async () => {
- const settings: Settings = { excludeTools: ['tool1'] };
- const extensions: Extension[] = [
- {
- config: {
- name: 'ext1',
- version: '1.0.0',
- excludeTools: ['tool2', 'tool3'],
- },
- contextFiles: [],
- },
- {
- config: {
- name: 'ext2',
- version: '1.0.0',
- excludeTools: ['tool3', 'tool4'],
- },
- contextFiles: [],
- },
- ];
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
- expect(config.getExcludeTools()).toEqual(
- expect.arrayContaining(['tool1', 'tool2', 'tool3', 'tool4']),
- );
- expect(config.getExcludeTools()).toHaveLength(4);
- });
-
- it('should return an empty array when no excludeTools are specified and it is interactive', async () => {
- process.stdin.isTTY = true;
- const settings: Settings = {};
- const extensions: Extension[] = [];
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
- expect(config.getExcludeTools()).toEqual([]);
- });
-
- it('should return default excludes when no excludeTools are specified and it is not interactive', async () => {
- process.stdin.isTTY = false;
- const settings: Settings = {};
- const extensions: Extension[] = [];
- process.argv = ['node', 'script.js', '-p', 'test'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
- expect(config.getExcludeTools()).toEqual(defaultExcludes);
- });
-
- it('should handle settings with excludeTools but no extensions', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { excludeTools: ['tool1', 'tool2'] };
- const extensions: Extension[] = [];
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
- expect(config.getExcludeTools()).toEqual(
- expect.arrayContaining(['tool1', 'tool2']),
- );
- expect(config.getExcludeTools()).toHaveLength(2);
- });
-
- it('should handle extensions with excludeTools but no settings', async () => {
- const settings: Settings = {};
- const extensions: Extension[] = [
- {
- config: {
- name: 'ext1',
- version: '1.0.0',
- excludeTools: ['tool1', 'tool2'],
- },
- contextFiles: [],
- },
- ];
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
- expect(config.getExcludeTools()).toEqual(
- expect.arrayContaining(['tool1', 'tool2']),
- );
- expect(config.getExcludeTools()).toHaveLength(2);
- });
-
- it('should not modify the original settings object', async () => {
- const settings: Settings = { excludeTools: ['tool1'] };
- const extensions: Extension[] = [
- {
- config: {
- name: 'ext1',
- version: '1.0.0',
- excludeTools: ['tool2'],
- },
- contextFiles: [],
- },
- ];
- const originalSettings = JSON.parse(JSON.stringify(settings));
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- await loadCliConfig(settings, extensions, 'test-session', argv);
- expect(settings).toEqual(originalSettings);
- });
-});
-
-describe('Approval mode tool exclusion logic', () => {
- const originalIsTTY = process.stdin.isTTY;
-
- beforeEach(() => {
- process.stdin.isTTY = false; // Ensure non-interactive mode
- });
-
- afterEach(() => {
- process.stdin.isTTY = originalIsTTY;
- });
-
- it('should exclude all interactive tools in non-interactive mode with default approval mode', async () => {
- process.argv = ['node', 'script.js', '-p', 'test'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const extensions: Extension[] = [];
-
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
-
- const excludedTools = config.getExcludeTools();
- expect(excludedTools).toContain(ShellTool.Name);
- expect(excludedTools).toContain(EditTool.Name);
- expect(excludedTools).toContain(WriteFileTool.Name);
- });
-
- it('should exclude all interactive tools in non-interactive mode with explicit default approval mode', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--approval-mode',
- 'default',
- '-p',
- 'test',
- ];
- const argv = await parseArguments();
- const settings: Settings = {};
- const extensions: Extension[] = [];
-
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
-
- const excludedTools = config.getExcludeTools();
- expect(excludedTools).toContain(ShellTool.Name);
- expect(excludedTools).toContain(EditTool.Name);
- expect(excludedTools).toContain(WriteFileTool.Name);
- });
-
- it('should exclude only shell tools in non-interactive mode with auto_edit approval mode', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--approval-mode',
- 'auto_edit',
- '-p',
- 'test',
- ];
- const argv = await parseArguments();
- const settings: Settings = {};
- const extensions: Extension[] = [];
-
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
-
- const excludedTools = config.getExcludeTools();
- expect(excludedTools).toContain(ShellTool.Name);
- expect(excludedTools).not.toContain(EditTool.Name);
- expect(excludedTools).not.toContain(WriteFileTool.Name);
- });
-
- it('should exclude no interactive tools in non-interactive mode with yolo approval mode', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--approval-mode',
- 'yolo',
- '-p',
- 'test',
- ];
- const argv = await parseArguments();
- const settings: Settings = {};
- const extensions: Extension[] = [];
-
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
-
- const excludedTools = config.getExcludeTools();
- expect(excludedTools).not.toContain(ShellTool.Name);
- expect(excludedTools).not.toContain(EditTool.Name);
- expect(excludedTools).not.toContain(WriteFileTool.Name);
- });
-
- it('should exclude no interactive tools in non-interactive mode with legacy yolo flag', async () => {
- process.argv = ['node', 'script.js', '--yolo', '-p', 'test'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const extensions: Extension[] = [];
-
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
-
- const excludedTools = config.getExcludeTools();
- expect(excludedTools).not.toContain(ShellTool.Name);
- expect(excludedTools).not.toContain(EditTool.Name);
- expect(excludedTools).not.toContain(WriteFileTool.Name);
- });
-
- it('should not exclude interactive tools in interactive mode regardless of approval mode', async () => {
- process.stdin.isTTY = true; // Interactive mode
-
- const testCases = [
- { args: ['node', 'script.js'] }, // default
- { args: ['node', 'script.js', '--approval-mode', 'default'] },
- { args: ['node', 'script.js', '--approval-mode', 'auto_edit'] },
- { args: ['node', 'script.js', '--approval-mode', 'yolo'] },
- { args: ['node', 'script.js', '--yolo'] },
- ];
-
- for (const testCase of testCases) {
- process.argv = testCase.args;
- const argv = await parseArguments();
- const settings: Settings = {};
- const extensions: Extension[] = [];
-
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
-
- const excludedTools = config.getExcludeTools();
- expect(excludedTools).not.toContain(ShellTool.Name);
- expect(excludedTools).not.toContain(EditTool.Name);
- expect(excludedTools).not.toContain(WriteFileTool.Name);
- }
- });
-
- it('should merge approval mode exclusions with settings exclusions in auto_edit mode', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--approval-mode',
- 'auto_edit',
- '-p',
- 'test',
- ];
- const argv = await parseArguments();
- const settings: Settings = { excludeTools: ['custom_tool'] };
- const extensions: Extension[] = [];
-
- const config = await loadCliConfig(
- settings,
- extensions,
- 'test-session',
- argv,
- );
-
- const excludedTools = config.getExcludeTools();
- expect(excludedTools).toContain('custom_tool'); // From settings
- expect(excludedTools).toContain(ShellTool.Name); // From approval mode
- expect(excludedTools).not.toContain(EditTool.Name); // Should be allowed in auto_edit
- expect(excludedTools).not.toContain(WriteFileTool.Name); // Should be allowed in auto_edit
- });
-
- it('should throw an error for invalid approval mode values in loadCliConfig', async () => {
- // Create a mock argv with an invalid approval mode that bypasses argument parsing validation
- const invalidArgv: Partial & { approvalMode: string } = {
- approvalMode: 'invalid_mode',
- promptInteractive: '',
- prompt: '',
- yolo: false,
- };
-
- const settings: Settings = {};
- const extensions: Extension[] = [];
-
- await expect(
- loadCliConfig(settings, extensions, 'test-session', invalidArgv),
- ).rejects.toThrow(
- 'Invalid approval mode: invalid_mode. Valid values are: yolo, auto_edit, default',
- );
- });
-});
-
-describe('loadCliConfig with allowed-mcp-server-names', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- const baseSettings: Settings = {
- mcpServers: {
- server1: { url: 'http://localhost:8080' },
- server2: { url: 'http://localhost:8081' },
- server3: { url: 'http://localhost:8082' },
- },
- };
-
- it('should allow all MCP servers if the flag is not provided', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual(baseSettings.mcpServers);
- });
-
- it('should allow only the specified MCP server', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--allowed-mcp-server-names',
- 'server1',
- ];
- const argv = await parseArguments();
- const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual({
- server1: { url: 'http://localhost:8080' },
- });
- });
-
- it('should allow multiple specified MCP servers', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--allowed-mcp-server-names',
- 'server1',
- '--allowed-mcp-server-names',
- 'server3',
- ];
- const argv = await parseArguments();
- const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual({
- server1: { url: 'http://localhost:8080' },
- server3: { url: 'http://localhost:8082' },
- });
- });
-
- it('should handle server names that do not exist', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--allowed-mcp-server-names',
- 'server1',
- '--allowed-mcp-server-names',
- 'server4',
- ];
- const argv = await parseArguments();
- const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual({
- server1: { url: 'http://localhost:8080' },
- });
- });
-
- it('should allow no MCP servers if the flag is provided but empty', async () => {
- process.argv = ['node', 'script.js', '--allowed-mcp-server-names', ''];
- const argv = await parseArguments();
- const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual({});
- });
-
- it('should read allowMCPServers from settings', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {
- ...baseSettings,
- allowMCPServers: ['server1', 'server2'],
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual({
- server1: { url: 'http://localhost:8080' },
- server2: { url: 'http://localhost:8081' },
- });
- });
-
- it('should read excludeMCPServers from settings', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {
- ...baseSettings,
- excludeMCPServers: ['server1', 'server2'],
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual({
- server3: { url: 'http://localhost:8082' },
- });
- });
-
- it('should override allowMCPServers with excludeMCPServers if overlapping ', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {
- ...baseSettings,
- excludeMCPServers: ['server1'],
- allowMCPServers: ['server1', 'server2'],
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual({
- server2: { url: 'http://localhost:8081' },
- });
- });
-
- it('should prioritize mcp server flag if set ', async () => {
- process.argv = [
- 'node',
- 'script.js',
- '--allowed-mcp-server-names',
- 'server1',
- ];
- const argv = await parseArguments();
- const settings: Settings = {
- ...baseSettings,
- excludeMCPServers: ['server1'],
- allowMCPServers: ['server2'],
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getMcpServers()).toEqual({
- server1: { url: 'http://localhost:8080' },
- });
- });
-});
-
-describe('loadCliConfig extensions', () => {
- const mockExtensions: Extension[] = [
- {
- config: { name: 'ext1', version: '1.0.0' },
- contextFiles: ['/path/to/ext1.md'],
- },
- {
- config: { name: 'ext2', version: '1.0.0' },
- contextFiles: ['/path/to/ext2.md'],
- },
- ];
-
- it('should not filter extensions if --extensions flag is not used', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(
- settings,
- mockExtensions,
- 'test-session',
- argv,
- );
- expect(config.getExtensionContextFilePaths()).toEqual([
- '/path/to/ext1.md',
- '/path/to/ext2.md',
- ]);
- });
-
- it('should filter extensions if --extensions flag is used', async () => {
- process.argv = ['node', 'script.js', '--extensions', 'ext1'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(
- settings,
- mockExtensions,
- 'test-session',
- argv,
- );
- expect(config.getExtensionContextFilePaths()).toEqual(['/path/to/ext1.md']);
- });
-});
-
-describe('loadCliConfig model selection', () => {
- it('selects a model from settings.json if provided', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- {
- model: 'anus-9001-ultra',
- },
- [],
- 'test-session',
- argv,
- );
-
- expect(config.getModel()).toBe('anus-9001-ultra');
- });
-
- it('uses the default anus model if nothing is set', async () => {
- process.argv = ['node', 'script.js']; // No model set.
- const argv = await parseArguments();
- const config = await loadCliConfig(
- {
- // No model set.
- },
- [],
- 'test-session',
- argv,
- );
-
- expect(config.getModel()).toBe('x-ai/grok-code-fast-1');
- });
-
- it('always prefers model from argvs', async () => {
- process.argv = ['node', 'script.js', '--model', 'anus-8675309-ultra'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- {
- model: 'anus-9001-ultra',
- },
- [],
- 'test-session',
- argv,
- );
-
- expect(config.getModel()).toBe('anus-8675309-ultra');
- });
-
- it('selects the model from argvs if provided', async () => {
- process.argv = ['node', 'script.js', '--model', 'anus-8675309-ultra'];
- const argv = await parseArguments();
- const config = await loadCliConfig(
- {
- // No model provided via settings.
- },
- [],
- 'test-session',
- argv,
- );
-
- expect(config.getModel()).toBe('anus-8675309-ultra');
- });
-});
-
-describe('loadCliConfig folderTrustFeature', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should be false by default', async () => {
- process.argv = ['node', 'script.js'];
- const settings: Settings = {};
- const argv = await parseArguments();
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getFolderTrustFeature()).toBe(false);
- });
-
- it('should be true when settings.folderTrustFeature is true', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { folderTrustFeature: true };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getFolderTrustFeature()).toBe(true);
- });
-});
-
-describe('loadCliConfig folderTrust', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should be false if folderTrustFeature is false and folderTrust is false', async () => {
- process.argv = ['node', 'script.js'];
- const settings: Settings = {
- folderTrustFeature: false,
- folderTrust: false,
- };
- const argv = await parseArguments();
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getFolderTrust()).toBe(false);
- });
-
- it('should be false if folderTrustFeature is true and folderTrust is false', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { folderTrustFeature: true, folderTrust: false };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getFolderTrust()).toBe(false);
- });
-
- it('should be false if folderTrustFeature is false and folderTrust is true', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { folderTrustFeature: false, folderTrust: true };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getFolderTrust()).toBe(false);
- });
-
- it('should be true when folderTrustFeature is true and folderTrust is true', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = { folderTrustFeature: true, folderTrust: true };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getFolderTrust()).toBe(true);
- });
-});
-
-vi.mock('fs', async () => {
- const actualFs = await vi.importActual('fs');
- const MOCK_CWD1 = process.cwd();
- const MOCK_CWD2 = path.resolve(path.sep, 'home', 'user', 'project');
-
- const mockPaths = new Set([
- MOCK_CWD1,
- MOCK_CWD2,
- path.resolve(path.sep, 'cli', 'path1'),
- path.resolve(path.sep, 'settings', 'path1'),
- path.join(os.homedir(), 'settings', 'path2'),
- path.join(MOCK_CWD2, 'cli', 'path2'),
- path.join(MOCK_CWD2, 'settings', 'path3'),
- ]);
-
- return {
- ...actualFs,
- existsSync: vi.fn((p) => mockPaths.has(p.toString())),
- statSync: vi.fn((p) => {
- if (mockPaths.has(p.toString())) {
- return { isDirectory: () => true };
- }
- // Fallback for other paths if needed, though the test should be specific.
- return actualFs.statSync(p);
- }),
- realpathSync: vi.fn((p) => p),
- };
-});
-
-describe('loadCliConfig with includeDirectories', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- vi.spyOn(process, 'cwd').mockReturnValue(
- path.resolve(path.sep, 'home', 'user', 'project'),
- );
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should combine and resolve paths from settings and CLI arguments', async () => {
- const mockCwd = path.resolve(path.sep, 'home', 'user', 'project');
- process.argv = [
- 'node',
- 'script.js',
- '--include-directories',
- `${path.resolve(path.sep, 'cli', 'path1')},${path.join(mockCwd, 'cli', 'path2')}`,
- ];
- const argv = await parseArguments();
- const settings: Settings = {
- includeDirectories: [
- path.resolve(path.sep, 'settings', 'path1'),
- path.join(os.homedir(), 'settings', 'path2'),
- path.join(mockCwd, 'settings', 'path3'),
- ],
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- const expected = [
- mockCwd,
- path.resolve(path.sep, 'cli', 'path1'),
- path.join(mockCwd, 'cli', 'path2'),
- path.resolve(path.sep, 'settings', 'path1'),
- path.join(os.homedir(), 'settings', 'path2'),
- path.join(mockCwd, 'settings', 'path3'),
- ];
- expect(config.getWorkspaceContext().getDirectories()).toEqual(
- expect.arrayContaining(expected),
- );
- expect(config.getWorkspaceContext().getDirectories()).toHaveLength(
- expected.length,
- );
- });
-});
-
-describe('loadCliConfig chatCompression', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should pass chatCompression settings to the core config', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {
- chatCompression: {
- contextPercentageThreshold: 0.5,
- },
- };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getChatCompression()).toEqual({
- contextPercentageThreshold: 0.5,
- });
- });
-
- it('should have undefined chatCompression if not in settings', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const settings: Settings = {};
- const config = await loadCliConfig(settings, [], 'test-session', argv);
- expect(config.getChatCompression()).toBeUndefined();
- });
-});
-
-describe('loadCliConfig tool exclusions', () => {
- const originalArgv = process.argv;
- const originalIsTTY = process.stdin.isTTY;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- process.stdin.isTTY = true;
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- process.stdin.isTTY = originalIsTTY;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should not exclude interactive tools in interactive mode without YOLO', async () => {
- process.stdin.isTTY = true;
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getExcludeTools()).not.toContain('run_shell_command');
- expect(config.getExcludeTools()).not.toContain('replace');
- expect(config.getExcludeTools()).not.toContain('write_file');
- });
-
- it('should not exclude interactive tools in interactive mode with YOLO', async () => {
- process.stdin.isTTY = true;
- process.argv = ['node', 'script.js', '--yolo'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getExcludeTools()).not.toContain('run_shell_command');
- expect(config.getExcludeTools()).not.toContain('replace');
- expect(config.getExcludeTools()).not.toContain('write_file');
- });
-
- it('should exclude interactive tools in non-interactive mode without YOLO', async () => {
- process.stdin.isTTY = false;
- process.argv = ['node', 'script.js', '-p', 'test'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getExcludeTools()).toContain('run_shell_command');
- expect(config.getExcludeTools()).toContain('replace');
- expect(config.getExcludeTools()).toContain('write_file');
- });
-
- it('should not exclude interactive tools in non-interactive mode with YOLO', async () => {
- process.stdin.isTTY = false;
- process.argv = ['node', 'script.js', '-p', 'test', '--yolo'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getExcludeTools()).not.toContain('run_shell_command');
- expect(config.getExcludeTools()).not.toContain('replace');
- expect(config.getExcludeTools()).not.toContain('write_file');
- });
-});
-
-describe('loadCliConfig interactive', () => {
- const originalArgv = process.argv;
- const originalIsTTY = process.stdin.isTTY;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- process.stdin.isTTY = true;
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- process.stdin.isTTY = originalIsTTY;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should be interactive if isTTY and no prompt', async () => {
- process.stdin.isTTY = true;
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.isInteractive()).toBe(true);
- });
-
- it('should be interactive if prompt-interactive is set', async () => {
- process.stdin.isTTY = false;
- process.argv = ['node', 'script.js', '--prompt-interactive', 'test'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.isInteractive()).toBe(true);
- });
-
- it('should not be interactive if not isTTY and no prompt', async () => {
- process.stdin.isTTY = false;
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.isInteractive()).toBe(false);
- });
-
- it('should not be interactive if prompt is set', async () => {
- process.stdin.isTTY = true;
- process.argv = ['node', 'script.js', '--prompt', 'test'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.isInteractive()).toBe(false);
- });
-});
-
-describe('loadCliConfig approval mode', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- process.argv = ['node', 'script.js']; // Reset argv for each test
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- it('should default to DEFAULT approval mode when no flags are set', async () => {
- process.argv = ['node', 'script.js'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
- });
-
- it('should set YOLO approval mode when --yolo flag is used', async () => {
- process.argv = ['node', 'script.js', '--yolo'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
- });
-
- it('should set YOLO approval mode when -y flag is used', async () => {
- process.argv = ['node', 'script.js', '-y'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
- });
-
- it('should set DEFAULT approval mode when --approval-mode=default', async () => {
- process.argv = ['node', 'script.js', '--approval-mode', 'default'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
- });
-
- it('should set AUTO_EDIT approval mode when --approval-mode=auto_edit', async () => {
- process.argv = ['node', 'script.js', '--approval-mode', 'auto_edit'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.AUTO_EDIT);
- });
-
- it('should set YOLO approval mode when --approval-mode=yolo', async () => {
- process.argv = ['node', 'script.js', '--approval-mode', 'yolo'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
- });
-
- it('should prioritize --approval-mode over --yolo when both would be valid (but validation prevents this)', async () => {
- // Note: This test documents the intended behavior, but in practice the validation
- // prevents both flags from being used together
- process.argv = ['node', 'script.js', '--approval-mode', 'default'];
- const argv = await parseArguments();
- // Manually set yolo to true to simulate what would happen if validation didn't prevent it
- argv.yolo = true;
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
- });
-
- it('should fall back to --yolo behavior when --approval-mode is not set', async () => {
- process.argv = ['node', 'script.js', '--yolo'];
- const argv = await parseArguments();
- const config = await loadCliConfig({}, [], 'test-session', argv);
- expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
- });
-});
-
-describe('loadCliConfig trustedFolder', () => {
- const originalArgv = process.argv;
-
- beforeEach(() => {
- vi.resetAllMocks();
- vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
- vi.stubEnv('ANUS_API_KEY', 'test-api-key');
- process.argv = ['node', 'script.js']; // Reset argv for each test
- });
-
- afterEach(() => {
- process.argv = originalArgv;
- vi.unstubAllEnvs();
- vi.restoreAllMocks();
- });
-
- const testCases = [
- // Cases where folderTrustFeature is false (feature disabled)
- {
- folderTrustFeature: false,
- folderTrust: true,
- isWorkspaceTrusted: true,
- expectedFolderTrust: false,
- expectedIsTrustedFolder: true,
- description:
- 'feature disabled, folderTrust true, workspace trusted -> behave as trusted',
- },
- {
- folderTrustFeature: false,
- folderTrust: true,
- isWorkspaceTrusted: false,
- expectedFolderTrust: false,
- expectedIsTrustedFolder: true,
- description:
- 'feature disabled, folderTrust true, workspace not trusted -> behave as trusted',
- },
- {
- folderTrustFeature: false,
- folderTrust: false,
- isWorkspaceTrusted: true,
- expectedFolderTrust: false,
- expectedIsTrustedFolder: true,
- description:
- 'feature disabled, folderTrust false, workspace trusted -> behave as trusted',
- },
-
- // Cases where folderTrustFeature is true but folderTrust setting is false
- {
- folderTrustFeature: true,
- folderTrust: false,
- isWorkspaceTrusted: true,
- expectedFolderTrust: false,
- expectedIsTrustedFolder: true,
- description:
- 'feature on, folderTrust false, workspace trusted -> behave as trusted',
- },
- {
- folderTrustFeature: true,
- folderTrust: false,
- isWorkspaceTrusted: false,
- expectedFolderTrust: false,
- expectedIsTrustedFolder: true,
- description:
- 'feature on, folderTrust false, workspace not trusted -> behave as trusted',
- },
-
- // Cases where feature is fully enabled (folderTrustFeature and folderTrust are true)
- {
- folderTrustFeature: true,
- folderTrust: true,
- isWorkspaceTrusted: true,
- expectedFolderTrust: true,
- expectedIsTrustedFolder: true,
- description:
- 'feature on, folderTrust on, workspace trusted -> is trusted',
- },
- {
- folderTrustFeature: true,
- folderTrust: true,
- isWorkspaceTrusted: false,
- expectedFolderTrust: true,
- expectedIsTrustedFolder: false,
- description:
- 'feature on, folderTrust on, workspace NOT trusted -> is NOT trusted',
- },
- {
- folderTrustFeature: true,
- folderTrust: true,
- isWorkspaceTrusted: undefined,
- expectedFolderTrust: true,
- expectedIsTrustedFolder: undefined,
- description:
- 'feature on, folderTrust on, workspace trust unknown -> is unknown',
- },
- ];
-
- for (const {
- folderTrustFeature,
- folderTrust,
- isWorkspaceTrusted: mockTrustValue,
- expectedFolderTrust,
- expectedIsTrustedFolder,
- description,
- } of testCases) {
- it(`should be correct for: ${description}`, async () => {
- (isWorkspaceTrusted as vi.Mock).mockImplementation(
- (settings: Settings) => {
- const featureIsEnabled =
- (settings.folderTrustFeature ?? false) &&
- (settings.folderTrust ?? true);
- return featureIsEnabled ? mockTrustValue : true;
- },
- );
- const argv = await parseArguments();
- const settings: Settings = { folderTrustFeature, folderTrust };
- const config = await loadCliConfig(settings, [], 'test-session', argv);
-
- expect(config.getFolderTrust()).toBe(expectedFolderTrust);
- expect(config.isTrustedFolder()).toBe(expectedIsTrustedFolder);
- });
- }
-});
diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts
deleted file mode 100644
index c941e15..0000000
--- a/packages/cli/src/config/config.ts
+++ /dev/null
@@ -1,634 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import * as fs from 'fs';
-import * as path from 'path';
-import { homedir } from 'node:os';
-import yargs from 'yargs/yargs';
-import { hideBin } from 'yargs/helpers';
-import process from 'node:process';
-import { mcpCommand } from '../commands/mcp.js';
-import {
- Config,
- loadServerHierarchicalMemory,
- setAnusMdFilename as setServerAnusMdFilename,
- getCurrentAnusMdFilename,
- ApprovalMode,
- DEFAULT_PRIMARY_MODEL,
- DEFAULT_EMBEDDING_MODEL,
- DEFAULT_MEMORY_FILE_FILTERING_OPTIONS,
- FileDiscoveryService,
- TelemetryTarget,
- FileFilteringOptions,
- ShellTool,
- EditTool,
- WriteFileTool,
- MCPServerConfig,
-} from '@anus-dev/anus-core';
-import { Settings } from './settings.js';
-
-import { Extension, annotateActiveExtensions } from './extension.js';
-import { getCliVersion } from '../utils/version.js';
-import { loadSandboxConfig } from './sandboxConfig.js';
-import { resolvePath } from '../utils/resolvePath.js';
-
-import { isWorkspaceTrusted } from './trustedFolders.js';
-
-// Simple console logger for now - replace with actual logger if available
-const logger = {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- debug: (...args: any[]) => console.debug('[DEBUG]', ...args),
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- warn: (...args: any[]) => console.warn('[WARN]', ...args),
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- error: (...args: any[]) => console.error('[ERROR]', ...args),
-};
-
-export interface CliArgs {
- model: string | undefined;
- sandbox: boolean | string | undefined;
- sandboxImage: string | undefined;
- debug: boolean | undefined;
- prompt: string | undefined;
- promptInteractive: string | undefined;
- allFiles: boolean | undefined;
- all_files: boolean | undefined;
- showMemoryUsage: boolean | undefined;
- show_memory_usage: boolean | undefined;
- yolo: boolean | undefined;
- approvalMode: string | undefined;
- telemetry: boolean | undefined;
- checkpointing: boolean | undefined;
- telemetryTarget: string | undefined;
- telemetryOtlpEndpoint: string | undefined;
- telemetryOtlpProtocol: string | undefined;
- telemetryLogPrompts: boolean | undefined;
- telemetryOutfile: string | undefined;
- allowedMcpServerNames: string[] | undefined;
- experimentalAcp: boolean | undefined;
- extensions: string[] | undefined;
- listExtensions: boolean | undefined;
- proxy: string | undefined;
- includeDirectories: string[] | undefined;
-}
-
-export async function parseArguments(): Promise {
- const yargsInstance = yargs(hideBin(process.argv))
- .scriptName('anus')
- .usage(
- 'Usage: anus [options] [command]\n\nANUS - Launch an interactive CLI, use -p/--prompt for non-interactive mode',
- )
- .command('$0', 'Launch ANUS', (yargsInstance) =>
- yargsInstance
- .option('model', {
- alias: 'm',
- type: 'string',
- description: `Model`,
- default:
- process.env['ANUS_MODEL'] ||
- process.env['GROK_MODEL'] ||
- process.env['XAI_MODEL'],
- })
- .option('prompt', {
- alias: 'p',
- type: 'string',
- description: 'Prompt. Appended to input on stdin (if any).',
- })
- .option('prompt-interactive', {
- alias: 'i',
- type: 'string',
- description:
- 'Execute the provided prompt and continue in interactive mode',
- })
- .option('sandbox', {
- alias: 's',
- type: 'boolean',
- description: 'Run in sandbox?',
- })
- .option('sandbox-image', {
- type: 'string',
- description: 'Sandbox image URI.',
- })
- .option('debug', {
- alias: 'd',
- type: 'boolean',
- description: 'Run in debug mode?',
- default: false,
- })
- .option('all-files', {
- alias: ['a'],
- type: 'boolean',
- description: 'Include ALL files in context?',
- default: false,
- })
- .option('all_files', {
- type: 'boolean',
- description: 'Include ALL files in context?',
- default: false,
- })
- .deprecateOption(
- 'all_files',
- 'Use --all-files instead. We will be removing --all_files in the coming weeks.',
- )
- .option('show-memory-usage', {
- type: 'boolean',
- description: 'Show memory usage in status bar',
- default: false,
- })
- .option('show_memory_usage', {
- type: 'boolean',
- description: 'Show memory usage in status bar',
- default: false,
- })
- .deprecateOption(
- 'show_memory_usage',
- 'Use --show-memory-usage instead. We will be removing --show_memory_usage in the coming weeks.',
- )
- .option('yolo', {
- alias: 'y',
- type: 'boolean',
- description:
- 'Automatically accept all actions (aka YOLO mode, see https://www.youtube.com/watch?v=xvFZjo5PgG0 for more details)?',
- default: false,
- })
- .option('approval-mode', {
- type: 'string',
- choices: ['default', 'auto_edit', 'yolo'],
- description:
- 'Set the approval mode: default (prompt for approval), auto_edit (auto-approve edit tools), yolo (auto-approve all tools)',
- })
- .option('telemetry', {
- type: 'boolean',
- description:
- 'Enable telemetry? This flag specifically controls if telemetry is sent. Other --telemetry-* flags set specific values but do not enable telemetry on their own.',
- })
- .option('telemetry-target', {
- type: 'string',
- choices: ['local', 'gcp'],
- description:
- 'Set the telemetry target (local or gcp). Overrides settings files.',
- })
- .option('telemetry-otlp-endpoint', {
- type: 'string',
- description:
- 'Set the OTLP endpoint for telemetry. Overrides environment variables and settings files.',
- })
- .option('telemetry-otlp-protocol', {
- type: 'string',
- choices: ['grpc', 'http'],
- description:
- 'Set the OTLP protocol for telemetry (grpc or http). Overrides settings files.',
- })
- .option('telemetry-log-prompts', {
- type: 'boolean',
- description:
- 'Enable or disable logging of user prompts for telemetry. Overrides settings files.',
- })
- .option('telemetry-outfile', {
- type: 'string',
- description: 'Redirect all telemetry output to the specified file.',
- })
- .option('checkpointing', {
- alias: 'c',
- type: 'boolean',
- description: 'Enables checkpointing of file edits',
- default: false,
- })
- .option('experimental-acp', {
- type: 'boolean',
- description: 'Starts the agent in ACP mode',
- })
- .option('allowed-mcp-server-names', {
- type: 'array',
- string: true,
- description: 'Allowed MCP server names',
- })
- .option('extensions', {
- alias: 'e',
- type: 'array',
- string: true,
- description:
- 'A list of extensions to use. If not provided, all extensions are used.',
- })
- .option('list-extensions', {
- alias: 'l',
- type: 'boolean',
- description: 'List all available extensions and exit.',
- })
- .option('proxy', {
- type: 'string',
- description:
- 'Proxy for ANUSent, like schema://user:password@host:port',
- })
- .option('include-directories', {
- type: 'array',
- string: true,
- description:
- 'Additional directories to include in the workspace (comma-separated or multiple --include-directories)',
- coerce: (dirs: string[]) =>
- // Handle comma-separated values
- dirs.flatMap((dir) => dir.split(',').map((d) => d.trim())),
- })
- .check((argv) => {
- if (argv.prompt && argv['promptInteractive']) {
- throw new Error(
- 'Cannot use both --prompt (-p) and --prompt-interactive (-i) together',
- );
- }
- if (argv.yolo && argv['approvalMode']) {
- throw new Error(
- 'Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.',
- );
- }
- return true;
- }),
- )
- // Register MCP subcommands
- .command(mcpCommand)
- .version(await getCliVersion()) // This will enable the --version flag based on package.json
- .alias('v', 'version')
- .help()
- .alias('h', 'help')
- .strict()
- .demandCommand(0, 0); // Allow base command to run with no subcommands
-
- yargsInstance.wrap(yargsInstance.terminalWidth());
- const result = await yargsInstance.parse();
-
- // Handle case where MCP subcommands are executed - they should exit the process
- // and not return to main CLI logic
- if (result._.length > 0 && result._[0] === 'mcp') {
- // MCP commands handle their own execution and process exit
- process.exit(0);
- }
-
- // The import format is now only controlled by settings.memoryImportFormat
- // We no longer accept it as a CLI argument
- return result as unknown as CliArgs;
-}
-
-// This function is now a thin wrapper around the server's implementation.
-// It's kept in the CLI for now as App.tsx directly calls it for memory refresh.
-// TODO: Consider if App.tsx should get memory via a server call or if Config should refresh itself.
-export async function loadHierarchicalAnusMemory(
- currentWorkingDirectory: string,
- includeDirectoriesToReadAnus: readonly string[] = [],
- debugMode: boolean,
- fileService: FileDiscoveryService,
- settings: Settings,
- extensionContextFilePaths: string[] = [],
- memoryImportFormat: 'flat' | 'tree' = 'tree',
- fileFilteringOptions?: FileFilteringOptions,
-): Promise<{ memoryContent: string; fileCount: number }> {
- // FIX: Use real, canonical paths for a reliable comparison to handle symlinks.
- const realCwd = fs.realpathSync(path.resolve(currentWorkingDirectory));
- const realHome = fs.realpathSync(path.resolve(homedir()));
- const isHomeDirectory = realCwd === realHome;
-
- // If it is the home directory, pass an empty string to the core memory
- // function to signal that it should skip the workspace search.
- const effectiveCwd = isHomeDirectory ? '' : currentWorkingDirectory;
-
- if (debugMode) {
- logger.debug(
- `CLI: Delegating hierarchical memory load to server for CWD: ${currentWorkingDirectory} (memoryImportFormat: ${memoryImportFormat})`,
- );
- }
-
- // Directly call the server function with the corrected path.
- return loadServerHierarchicalMemory(
- effectiveCwd,
- includeDirectoriesToReadAnus,
- debugMode,
- fileService,
- extensionContextFilePaths,
- memoryImportFormat,
- fileFilteringOptions,
- settings.memoryDiscoveryMaxDirs,
- );
-}
-
-export async function loadCliConfig(
- settings: Settings,
- extensions: Extension[],
- sessionId: string,
- argv: CliArgs,
- cwd: string = process.cwd(),
-): Promise {
- // Export Grok API key from settings to environment early in the process
- if (
- settings.grok?.apiKey &&
- !process.env['GROK_API_KEY'] &&
- !process.env['XAI_API_KEY']
- ) {
- process.env['GROK_API_KEY'] = settings.grok.apiKey;
- }
-
- const debugMode =
- argv.debug ||
- [process.env['DEBUG'], process.env['DEBUG_MODE']].some(
- (v) => v === 'true' || v === '1',
- ) ||
- false;
- const memoryImportFormat = settings.memoryImportFormat || 'tree';
-
- const ideMode = settings.ideMode ?? false;
-
- const folderTrustFeature = settings.folderTrustFeature ?? false;
- const folderTrustSetting = settings.folderTrust ?? true;
- const folderTrust = folderTrustFeature && folderTrustSetting;
- const trustedFolder = isWorkspaceTrusted(settings);
-
- const allExtensions = annotateActiveExtensions(
- extensions,
- argv.extensions || [],
- );
-
- const activeExtensions = extensions.filter(
- (_, i) => allExtensions[i].isActive,
- );
-
- // Set the context filename in the server's memoryTool module BEFORE loading memory
- // TODO(b/343434939): This is a bit of a hack. The contextFileName should ideally be passed
- // directly to the Config constructor in core, and have core handle setAnusMdFilename.
- // However, loadHierarchicalAnusMemory is called *before* createServerConfig.
- if (settings.contextFileName) {
- setServerAnusMdFilename(settings.contextFileName);
- } else {
- // Reset to default if not provided in settings.
- setServerAnusMdFilename(getCurrentAnusMdFilename());
- }
-
- const extensionContextFilePaths = activeExtensions.flatMap(
- (e) => e.contextFiles,
- );
-
- const fileService = new FileDiscoveryService(cwd);
-
- const fileFiltering = {
- ...DEFAULT_MEMORY_FILE_FILTERING_OPTIONS,
- ...settings.fileFiltering,
- };
-
- const includeDirectories = (settings.includeDirectories || [])
- .map(resolvePath)
- .concat((argv.includeDirectories || []).map(resolvePath));
-
- // Call the (now wrapper) loadHierarchicalAnusMemory which calls the server's version
- const { memoryContent, fileCount } = await loadHierarchicalAnusMemory(
- cwd,
- settings.loadMemoryFromIncludeDirectories ? includeDirectories : [],
- debugMode,
- fileService,
- settings,
- extensionContextFilePaths,
- memoryImportFormat,
- fileFiltering,
- );
-
- let mcpServers = mergeMcpServers(settings, activeExtensions);
- const question = argv.promptInteractive || argv.prompt || '';
-
- // Determine approval mode with backward compatibility
- let approvalMode: ApprovalMode;
- if (argv.approvalMode) {
- // New --approval-mode flag takes precedence
- switch (argv.approvalMode) {
- case 'yolo':
- approvalMode = ApprovalMode.YOLO;
- break;
- case 'auto_edit':
- approvalMode = ApprovalMode.AUTO_EDIT;
- break;
- case 'default':
- approvalMode = ApprovalMode.DEFAULT;
- break;
- default:
- throw new Error(
- `Invalid approval mode: ${argv.approvalMode}. Valid values are: yolo, auto_edit, default`,
- );
- }
- } else {
- // Fallback to legacy --yolo flag behavior
- approvalMode =
- argv.yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT;
- }
-
- const interactive =
- !!argv.promptInteractive || (process.stdin.isTTY && question.length === 0);
- // In non-interactive mode, exclude tools that require a prompt.
- const extraExcludes: string[] = [];
- if (!interactive && !argv.experimentalAcp) {
- switch (approvalMode) {
- case ApprovalMode.DEFAULT:
- // In default non-interactive mode, all tools that require approval are excluded.
- extraExcludes.push(ShellTool.Name, EditTool.Name, WriteFileTool.Name);
- break;
- case ApprovalMode.AUTO_EDIT:
- // In auto-edit non-interactive mode, only tools that still require a prompt are excluded.
- extraExcludes.push(ShellTool.Name);
- break;
- case ApprovalMode.YOLO:
- // No extra excludes for YOLO mode.
- break;
- default:
- // This should never happen due to validation earlier, but satisfies the linter
- break;
- }
- }
-
- const excludeTools = mergeExcludeTools(
- settings,
- activeExtensions,
- extraExcludes.length > 0 ? extraExcludes : undefined,
- );
- const blockedMcpServers: Array<{ name: string; extensionName: string }> = [];
-
- if (!argv.allowedMcpServerNames) {
- if (settings.allowMCPServers) {
- mcpServers = allowedMcpServers(
- mcpServers,
- settings.allowMCPServers,
- blockedMcpServers,
- );
- }
-
- if (settings.excludeMCPServers) {
- const excludedNames = new Set(settings.excludeMCPServers.filter(Boolean));
- if (excludedNames.size > 0) {
- mcpServers = Object.fromEntries(
- Object.entries(mcpServers).filter(([key]) => !excludedNames.has(key)),
- );
- }
- }
- }
-
- if (argv.allowedMcpServerNames) {
- mcpServers = allowedMcpServers(
- mcpServers,
- argv.allowedMcpServerNames,
- blockedMcpServers,
- );
- }
-
- const sandboxConfig = await loadSandboxConfig(settings, argv);
-
- // Use the configured model or default to x-ai/grok-4
- const preferredModel =
- argv.model ||
- settings.grok?.model ||
- settings.model ||
- process.env['GROK_MODEL'] ||
- process.env['XAI_MODEL'] ||
- DEFAULT_PRIMARY_MODEL;
-
- return new Config({
- sessionId,
- embeddingModel: DEFAULT_EMBEDDING_MODEL,
- sandbox: sandboxConfig,
- targetDir: cwd,
- includeDirectories,
- loadMemoryFromIncludeDirectories:
- settings.loadMemoryFromIncludeDirectories || false,
- debugMode,
- question,
- fullContext: argv.allFiles || argv.all_files || false,
- coreTools: settings.coreTools || undefined,
- excludeTools,
- toolDiscoveryCommand: settings.toolDiscoveryCommand,
- toolCallCommand: settings.toolCallCommand,
- mcpServerCommand: settings.mcpServerCommand,
- mcpServers,
- userMemory: memoryContent,
- anusMdFileCount: fileCount,
- approvalMode,
- showMemoryUsage:
- argv.showMemoryUsage ||
- argv.show_memory_usage ||
- settings.showMemoryUsage ||
- false,
- accessibility: settings.accessibility,
- telemetry: {
- enabled: argv.telemetry ?? settings.telemetry?.enabled,
- target: (argv.telemetryTarget ??
- settings.telemetry?.target) as TelemetryTarget,
- otlpEndpoint:
- argv.telemetryOtlpEndpoint ??
- process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] ??
- settings.telemetry?.otlpEndpoint,
- otlpProtocol: (['grpc', 'http'] as const).find(
- (p) =>
- p ===
- (argv.telemetryOtlpProtocol ?? settings.telemetry?.otlpProtocol),
- ),
- logPrompts: argv.telemetryLogPrompts ?? settings.telemetry?.logPrompts,
- outfile: argv.telemetryOutfile ?? settings.telemetry?.outfile,
- },
- usageStatisticsEnabled: settings.usageStatisticsEnabled ?? true,
- // Git-aware file filtering settings
- fileFiltering: {
- respectGitIgnore: settings.fileFiltering?.respectGitIgnore,
- respectAnusIgnore: settings.fileFiltering?.respectAnusIgnore,
- enableRecursiveFileSearch:
- settings.fileFiltering?.enableRecursiveFileSearch,
- },
- checkpointing: argv.checkpointing || settings.checkpointing?.enabled,
- proxy:
- argv.proxy ||
- process.env['HTTPS_PROXY'] ||
- process.env['https_proxy'] ||
- process.env['HTTP_PROXY'] ||
- process.env['http_proxy'],
- cwd,
- fileDiscoveryService: fileService,
- bugCommand: settings.bugCommand,
- model: preferredModel,
- extensionContextFilePaths,
- maxSessionTurns: settings.maxSessionTurns ?? -1,
- experimentalZedIntegration: argv.experimentalAcp || false,
- listExtensions: argv.listExtensions || false,
- extensions: allExtensions,
- blockedMcpServers,
- noBrowser: !!process.env['NO_BROWSER'],
- summarizeToolOutput: settings.summarizeToolOutput,
- ideMode,
- chatCompression: settings.chatCompression,
- folderTrustFeature,
- folderTrust,
- interactive,
- trustedFolder,
- });
-}
-
-function allowedMcpServers(
- mcpServers: { [x: string]: MCPServerConfig },
- allowMCPServers: string[],
- blockedMcpServers: Array<{ name: string; extensionName: string }>,
-) {
- const allowedNames = new Set(allowMCPServers.filter(Boolean));
- if (allowedNames.size > 0) {
- mcpServers = Object.fromEntries(
- Object.entries(mcpServers).filter(([key, server]) => {
- const isAllowed = allowedNames.has(key);
- if (!isAllowed) {
- blockedMcpServers.push({
- name: key,
- extensionName: server.extensionName || '',
- });
- }
- return isAllowed;
- }),
- );
- } else {
- blockedMcpServers.push(
- ...Object.entries(mcpServers).map(([key, server]) => ({
- name: key,
- extensionName: server.extensionName || '',
- })),
- );
- mcpServers = {};
- }
- return mcpServers;
-}
-
-function mergeMcpServers(settings: Settings, extensions: Extension[]) {
- const mcpServers = { ...(settings.mcpServers || {}) };
- for (const extension of extensions) {
- Object.entries(extension.config.mcpServers || {}).forEach(
- ([key, server]) => {
- if (mcpServers[key]) {
- logger.warn(
- `Skipping extension MCP config for server with key "${key}" as it already exists.`,
- );
- return;
- }
- mcpServers[key] = {
- ...server,
- extensionName: extension.config.name,
- };
- },
- );
- }
- return mcpServers;
-}
-
-function mergeExcludeTools(
- settings: Settings,
- extensions: Extension[],
- extraExcludes?: string[] | undefined,
-): string[] {
- const allExcludeTools = new Set([
- ...(settings.excludeTools || []),
- ...(extraExcludes || []),
- ]);
- for (const extension of extensions) {
- for (const tool of extension.config.excludeTools || []) {
- allExcludeTools.add(tool);
- }
- }
- return [...allExcludeTools];
-}
diff --git a/packages/cli/src/config/extension.test.ts b/packages/cli/src/config/extension.test.ts
deleted file mode 100644
index baf8c15..0000000
--- a/packages/cli/src/config/extension.test.ts
+++ /dev/null
@@ -1,185 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { vi } from 'vitest';
-import * as fs from 'fs';
-import * as os from 'os';
-import * as path from 'path';
-import {
- EXTENSIONS_CONFIG_FILENAME,
- EXTENSIONS_DIRECTORY_NAME,
- annotateActiveExtensions,
- loadExtensions,
-} from './extension.js';
-
-vi.mock('os', async (importOriginal) => {
- const os = await importOriginal();
- return {
- ...os,
- homedir: vi.fn(),
- };
-});
-
-describe('loadExtensions', () => {
- let tempWorkspaceDir: string;
- let tempHomeDir: string;
-
- beforeEach(() => {
- tempWorkspaceDir = fs.mkdtempSync(
- path.join(os.tmpdir(), 'anus-cli-test-workspace-'),
- );
- tempHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'anus-cli-test-home-'));
- vi.mocked(os.homedir).mockReturnValue(tempHomeDir);
- });
-
- afterEach(() => {
- fs.rmSync(tempWorkspaceDir, { recursive: true, force: true });
- fs.rmSync(tempHomeDir, { recursive: true, force: true });
- });
-
- it('should include extension path in loaded extension', () => {
- const workspaceExtensionsDir = path.join(
- tempWorkspaceDir,
- EXTENSIONS_DIRECTORY_NAME,
- );
- fs.mkdirSync(workspaceExtensionsDir, { recursive: true });
-
- const extensionDir = path.join(workspaceExtensionsDir, 'test-extension');
- fs.mkdirSync(extensionDir, { recursive: true });
-
- const config = {
- name: 'test-extension',
- version: '1.0.0',
- };
- fs.writeFileSync(
- path.join(extensionDir, EXTENSIONS_CONFIG_FILENAME),
- JSON.stringify(config),
- );
-
- const extensions = loadExtensions(tempWorkspaceDir);
- expect(extensions).toHaveLength(1);
- expect(extensions[0].path).toBe(extensionDir);
- expect(extensions[0].config.name).toBe('test-extension');
- });
-
- it('should load context file path when ANUS.md is present', () => {
- const workspaceExtensionsDir = path.join(
- tempWorkspaceDir,
- EXTENSIONS_DIRECTORY_NAME,
- );
- fs.mkdirSync(workspaceExtensionsDir, { recursive: true });
- createExtension(workspaceExtensionsDir, 'ext1', '1.0.0', true);
- createExtension(workspaceExtensionsDir, 'ext2', '2.0.0');
-
- const extensions = loadExtensions(tempWorkspaceDir);
-
- expect(extensions).toHaveLength(2);
- const ext1 = extensions.find((e) => e.config.name === 'ext1');
- const ext2 = extensions.find((e) => e.config.name === 'ext2');
- expect(ext1?.contextFiles).toEqual([
- path.join(workspaceExtensionsDir, 'ext1', 'ANUS.md'),
- ]);
- expect(ext2?.contextFiles).toEqual([]);
- });
-
- it('should load context file path from the extension config', () => {
- const workspaceExtensionsDir = path.join(
- tempWorkspaceDir,
- EXTENSIONS_DIRECTORY_NAME,
- );
- fs.mkdirSync(workspaceExtensionsDir, { recursive: true });
- createExtension(
- workspaceExtensionsDir,
- 'ext1',
- '1.0.0',
- false,
- 'my-context-file.md',
- );
-
- const extensions = loadExtensions(tempWorkspaceDir);
-
- expect(extensions).toHaveLength(1);
- const ext1 = extensions.find((e) => e.config.name === 'ext1');
- expect(ext1?.contextFiles).toEqual([
- path.join(workspaceExtensionsDir, 'ext1', 'my-context-file.md'),
- ]);
- });
-});
-
-describe('annotateActiveExtensions', () => {
- const extensions = [
- { config: { name: 'ext1', version: '1.0.0' }, contextFiles: [] },
- { config: { name: 'ext2', version: '1.0.0' }, contextFiles: [] },
- { config: { name: 'ext3', version: '1.0.0' }, contextFiles: [] },
- ];
-
- it('should mark all extensions as active if no enabled extensions are provided', () => {
- const activeExtensions = annotateActiveExtensions(extensions, []);
- expect(activeExtensions).toHaveLength(3);
- expect(activeExtensions.every((e) => e.isActive)).toBe(true);
- });
-
- it('should mark only the enabled extensions as active', () => {
- const activeExtensions = annotateActiveExtensions(extensions, [
- 'ext1',
- 'ext3',
- ]);
- expect(activeExtensions).toHaveLength(3);
- expect(activeExtensions.find((e) => e.name === 'ext1')?.isActive).toBe(
- true,
- );
- expect(activeExtensions.find((e) => e.name === 'ext2')?.isActive).toBe(
- false,
- );
- expect(activeExtensions.find((e) => e.name === 'ext3')?.isActive).toBe(
- true,
- );
- });
-
- it('should mark all extensions as inactive when "none" is provided', () => {
- const activeExtensions = annotateActiveExtensions(extensions, ['none']);
- expect(activeExtensions).toHaveLength(3);
- expect(activeExtensions.every((e) => !e.isActive)).toBe(true);
- });
-
- it('should handle case-insensitivity', () => {
- const activeExtensions = annotateActiveExtensions(extensions, ['EXT1']);
- expect(activeExtensions.find((e) => e.name === 'ext1')?.isActive).toBe(
- true,
- );
- });
-
- it('should log an error for unknown extensions', () => {
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
- annotateActiveExtensions(extensions, ['ext4']);
- expect(consoleSpy).toHaveBeenCalledWith('Extension not found: ext4');
- consoleSpy.mockRestore();
- });
-});
-
-function createExtension(
- extensionsDir: string,
- name: string,
- version: string,
- addContextFile = false,
- contextFileName?: string,
-): void {
- const extDir = path.join(extensionsDir, name);
- fs.mkdirSync(extDir);
- fs.writeFileSync(
- path.join(extDir, EXTENSIONS_CONFIG_FILENAME),
- JSON.stringify({ name, version, contextFileName }),
- );
-
- if (addContextFile) {
- fs.writeFileSync(path.join(extDir, 'ANUS.md'), 'context');
- }
-
- if (contextFileName) {
- fs.writeFileSync(path.join(extDir, contextFileName), 'context');
- }
-}
diff --git a/packages/cli/src/config/extension.ts b/packages/cli/src/config/extension.ts
deleted file mode 100644
index 7d4422f..0000000
--- a/packages/cli/src/config/extension.ts
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { MCPServerConfig, AnusCLIExtension } from '@anus-dev/anus-core';
-import * as fs from 'fs';
-import * as path from 'path';
-import * as os from 'os';
-
-export const EXTENSIONS_DIRECTORY_NAME = path.join('.anus', 'extensions');
-export const EXTENSIONS_CONFIG_FILENAME = 'anus-extension.json';
-
-export interface Extension {
- path: string;
- config: ExtensionConfig;
- contextFiles: string[];
-}
-
-export interface ExtensionConfig {
- name: string;
- version: string;
- mcpServers?: Record;
- contextFileName?: string | string[];
- excludeTools?: string[];
-}
-
-export function loadExtensions(workspaceDir: string): Extension[] {
- const allExtensions = [
- ...loadExtensionsFromDir(workspaceDir),
- ...loadExtensionsFromDir(os.homedir()),
- ];
-
- const uniqueExtensions = new Map();
- for (const extension of allExtensions) {
- if (!uniqueExtensions.has(extension.config.name)) {
- uniqueExtensions.set(extension.config.name, extension);
- }
- }
-
- return Array.from(uniqueExtensions.values());
-}
-
-function loadExtensionsFromDir(dir: string): Extension[] {
- const extensionsDir = path.join(dir, EXTENSIONS_DIRECTORY_NAME);
- if (!fs.existsSync(extensionsDir)) {
- return [];
- }
-
- const extensions: Extension[] = [];
- for (const subdir of fs.readdirSync(extensionsDir)) {
- const extensionDir = path.join(extensionsDir, subdir);
-
- const extension = loadExtension(extensionDir);
- if (extension != null) {
- extensions.push(extension);
- }
- }
- return extensions;
-}
-
-function loadExtension(extensionDir: string): Extension | null {
- if (!fs.statSync(extensionDir).isDirectory()) {
- console.error(
- `Warning: unexpected file ${extensionDir} in extensions directory.`,
- );
- return null;
- }
-
- const configFilePath = path.join(extensionDir, EXTENSIONS_CONFIG_FILENAME);
- if (!fs.existsSync(configFilePath)) {
- console.error(
- `Warning: extension directory ${extensionDir} does not contain a config file ${configFilePath}.`,
- );
- return null;
- }
-
- try {
- const configContent = fs.readFileSync(configFilePath, 'utf-8');
- const config = JSON.parse(configContent) as ExtensionConfig;
- if (!config.name || !config.version) {
- console.error(
- `Invalid extension config in ${configFilePath}: missing name or version.`,
- );
- return null;
- }
-
- const contextFiles = getContextFileNames(config)
- .map((contextFileName) => path.join(extensionDir, contextFileName))
- .filter((contextFilePath) => fs.existsSync(contextFilePath));
-
- return {
- path: extensionDir,
- config,
- contextFiles,
- };
- } catch (e) {
- console.error(
- `Warning: error parsing extension config in ${configFilePath}: ${e}`,
- );
- return null;
- }
-}
-
-function getContextFileNames(config: ExtensionConfig): string[] {
- if (!config.contextFileName) {
- return ['ANUS.md'];
- } else if (!Array.isArray(config.contextFileName)) {
- return [config.contextFileName];
- }
- return config.contextFileName;
-}
-
-export function annotateActiveExtensions(
- extensions: Extension[],
- enabledExtensionNames: string[],
-): AnusCLIExtension[] {
- const annotatedExtensions: AnusCLIExtension[] = [];
-
- if (enabledExtensionNames.length === 0) {
- return extensions.map((extension) => ({
- name: extension.config.name,
- version: extension.config.version,
- isActive: true,
- path: extension.path,
- }));
- }
-
- const lowerCaseEnabledExtensions = new Set(
- enabledExtensionNames.map((e) => e.trim().toLowerCase()),
- );
-
- if (
- lowerCaseEnabledExtensions.size === 1 &&
- lowerCaseEnabledExtensions.has('none')
- ) {
- return extensions.map((extension) => ({
- name: extension.config.name,
- version: extension.config.version,
- isActive: false,
- path: extension.path,
- }));
- }
-
- const notFoundNames = new Set(lowerCaseEnabledExtensions);
-
- for (const extension of extensions) {
- const lowerCaseName = extension.config.name.toLowerCase();
- const isActive = lowerCaseEnabledExtensions.has(lowerCaseName);
-
- if (isActive) {
- notFoundNames.delete(lowerCaseName);
- }
-
- annotatedExtensions.push({
- name: extension.config.name,
- version: extension.config.version,
- isActive,
- path: extension.path,
- });
- }
-
- for (const requestedName of notFoundNames) {
- console.error(`Extension not found: ${requestedName}`);
- }
-
- return annotatedExtensions;
-}
diff --git a/packages/cli/src/config/keyBindings.test.ts b/packages/cli/src/config/keyBindings.test.ts
deleted file mode 100644
index 8b19d26..0000000
--- a/packages/cli/src/config/keyBindings.test.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect } from 'vitest';
-import {
- Command,
- KeyBindingConfig,
- defaultKeyBindings,
-} from './keyBindings.js';
-
-describe('keyBindings config', () => {
- describe('defaultKeyBindings', () => {
- it('should have bindings for all commands', () => {
- const commands = Object.values(Command);
-
- for (const command of commands) {
- expect(defaultKeyBindings[command]).toBeDefined();
- expect(Array.isArray(defaultKeyBindings[command])).toBe(true);
- }
- });
-
- it('should have valid key binding structures', () => {
- for (const [_, bindings] of Object.entries(defaultKeyBindings)) {
- for (const binding of bindings) {
- // Each binding should have either key or sequence, but not both
- const hasKey = binding.key !== undefined;
- const hasSequence = binding.sequence !== undefined;
-
- expect(hasKey || hasSequence).toBe(true);
- expect(hasKey && hasSequence).toBe(false);
-
- // Modifier properties should be boolean or undefined
- if (binding.ctrl !== undefined) {
- expect(typeof binding.ctrl).toBe('boolean');
- }
- if (binding.shift !== undefined) {
- expect(typeof binding.shift).toBe('boolean');
- }
- if (binding.command !== undefined) {
- expect(typeof binding.command).toBe('boolean');
- }
- if (binding.paste !== undefined) {
- expect(typeof binding.paste).toBe('boolean');
- }
- }
- }
- });
-
- it('should export all required types', () => {
- // Basic type checks
- expect(typeof Command.HOME).toBe('string');
- expect(typeof Command.END).toBe('string');
-
- // Config should be readonly
- const config: KeyBindingConfig = defaultKeyBindings;
- expect(config[Command.HOME]).toBeDefined();
- });
- });
-});
diff --git a/packages/cli/src/config/keyBindings.ts b/packages/cli/src/config/keyBindings.ts
deleted file mode 100644
index e218d2c..0000000
--- a/packages/cli/src/config/keyBindings.ts
+++ /dev/null
@@ -1,185 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-/**
- * Command enum for all available keyboard shortcuts
- */
-export enum Command {
- // Basic bindings
- RETURN = 'return',
- ESCAPE = 'escape',
-
- // Cursor movement
- HOME = 'home',
- END = 'end',
-
- // Text deletion
- KILL_LINE_RIGHT = 'killLineRight',
- KILL_LINE_LEFT = 'killLineLeft',
- CLEAR_INPUT = 'clearInput',
-
- // Screen control
- CLEAR_SCREEN = 'clearScreen',
-
- // History navigation
- HISTORY_UP = 'historyUp',
- HISTORY_DOWN = 'historyDown',
- NAVIGATION_UP = 'navigationUp',
- NAVIGATION_DOWN = 'navigationDown',
-
- // Auto-completion
- ACCEPT_SUGGESTION = 'acceptSuggestion',
- COMPLETION_UP = 'completionUp',
- COMPLETION_DOWN = 'completionDown',
-
- // Text input
- SUBMIT = 'submit',
- NEWLINE = 'newline',
-
- // External tools
- OPEN_EXTERNAL_EDITOR = 'openExternalEditor',
- PASTE_CLIPBOARD_IMAGE = 'pasteClipboardImage',
-
- // App level bindings
- SHOW_ERROR_DETAILS = 'showErrorDetails',
- TOGGLE_TOOL_DESCRIPTIONS = 'toggleToolDescriptions',
- TOGGLE_IDE_CONTEXT_DETAIL = 'toggleIDEContextDetail',
- QUIT = 'quit',
- EXIT = 'exit',
- SHOW_MORE_LINES = 'showMoreLines',
-
- // Shell commands
- REVERSE_SEARCH = 'reverseSearch',
- SUBMIT_REVERSE_SEARCH = 'submitReverseSearch',
- ACCEPT_SUGGESTION_REVERSE_SEARCH = 'acceptSuggestionReverseSearch',
-}
-
-/**
- * Data-driven key binding structure for user configuration
- */
-export interface KeyBinding {
- /** The key name (e.g., 'a', 'return', 'tab', 'escape') */
- key?: string;
- /** The key sequence (e.g., '\x18' for Ctrl+X) - alternative to key name */
- sequence?: string;
- /** Control key requirement: true=must be pressed, false=must not be pressed, undefined=ignore */
- ctrl?: boolean;
- /** Shift key requirement: true=must be pressed, false=must not be pressed, undefined=ignore */
- shift?: boolean;
- /** Command/meta key requirement: true=must be pressed, false=must not be pressed, undefined=ignore */
- command?: boolean;
- /** Paste operation requirement: true=must be paste, false=must not be paste, undefined=ignore */
- paste?: boolean;
-}
-
-/**
- * Configuration type mapping commands to their key bindings
- */
-export type KeyBindingConfig = {
- readonly [C in Command]: readonly KeyBinding[];
-};
-
-/**
- * Default key binding configuration
- * Matches the original hard-coded logic exactly
- */
-export const defaultKeyBindings: KeyBindingConfig = {
- // Basic bindings
- [Command.RETURN]: [{ key: 'return' }],
- // Original: key.name === 'escape'
- [Command.ESCAPE]: [{ key: 'escape' }],
-
- // Cursor movement
- // Original: key.ctrl && key.name === 'a'
- [Command.HOME]: [{ key: 'a', ctrl: true }],
- // Original: key.ctrl && key.name === 'e'
- [Command.END]: [{ key: 'e', ctrl: true }],
-
- // Text deletion
- // Original: key.ctrl && key.name === 'k'
- [Command.KILL_LINE_RIGHT]: [{ key: 'k', ctrl: true }],
- // Original: key.ctrl && key.name === 'u'
- [Command.KILL_LINE_LEFT]: [{ key: 'u', ctrl: true }],
- // Original: key.ctrl && key.name === 'c'
- [Command.CLEAR_INPUT]: [{ key: 'c', ctrl: true }],
-
- // Screen control
- // Original: key.ctrl && key.name === 'l'
- [Command.CLEAR_SCREEN]: [{ key: 'l', ctrl: true }],
-
- // History navigation
- // Original: key.ctrl && key.name === 'p'
- [Command.HISTORY_UP]: [{ key: 'p', ctrl: true }],
- // Original: key.ctrl && key.name === 'n'
- [Command.HISTORY_DOWN]: [{ key: 'n', ctrl: true }],
- // Original: key.name === 'up'
- [Command.NAVIGATION_UP]: [{ key: 'up' }],
- // Original: key.name === 'down'
- [Command.NAVIGATION_DOWN]: [{ key: 'down' }],
-
- // Auto-completion
- // Original: key.name === 'tab' || (key.name === 'return' && !key.ctrl)
- [Command.ACCEPT_SUGGESTION]: [{ key: 'tab' }, { key: 'return', ctrl: false }],
- // Completion navigation (arrow or Ctrl+P/N)
- [Command.COMPLETION_UP]: [{ key: 'up' }, { key: 'p', ctrl: true }],
- [Command.COMPLETION_DOWN]: [{ key: 'down' }, { key: 'n', ctrl: true }],
-
- // Text input
- // Original: key.name === 'return' && !key.ctrl && !key.meta && !key.paste
- // Must also exclude shift to allow shift+enter for newline
- [Command.SUBMIT]: [
- {
- key: 'return',
- ctrl: false,
- command: false,
- paste: false,
- shift: false,
- },
- ],
- // Original: key.name === 'return' && (key.ctrl || key.meta || key.paste)
- // Split into multiple data-driven bindings
- // Now also includes shift+enter for multi-line input
- [Command.NEWLINE]: [
- { key: 'return', ctrl: true },
- { key: 'return', command: true },
- { key: 'return', paste: true },
- { key: 'return', shift: true },
- { key: 'j', ctrl: true },
- ],
-
- // External tools
- // Original: key.ctrl && (key.name === 'x' || key.sequence === '\x18')
- [Command.OPEN_EXTERNAL_EDITOR]: [
- { key: 'x', ctrl: true },
- { sequence: '\x18', ctrl: true },
- ],
- // Original: key.ctrl && key.name === 'v'
- [Command.PASTE_CLIPBOARD_IMAGE]: [{ key: 'v', ctrl: true }],
-
- // App level bindings
- // Original: key.ctrl && key.name === 'o'
- [Command.SHOW_ERROR_DETAILS]: [{ key: 'o', ctrl: true }],
- // Original: key.ctrl && key.name === 't'
- [Command.TOGGLE_TOOL_DESCRIPTIONS]: [{ key: 't', ctrl: true }],
- // Original: key.ctrl && key.name === 'g'
- [Command.TOGGLE_IDE_CONTEXT_DETAIL]: [{ key: 'g', ctrl: true }],
- // Original: key.ctrl && (key.name === 'c' || key.name === 'C')
- [Command.QUIT]: [{ key: 'c', ctrl: true }],
- // Original: key.ctrl && (key.name === 'd' || key.name === 'D')
- [Command.EXIT]: [{ key: 'd', ctrl: true }],
- // Original: key.ctrl && key.name === 's'
- [Command.SHOW_MORE_LINES]: [{ key: 's', ctrl: true }],
-
- // Shell commands
- // Original: key.ctrl && key.name === 'r'
- [Command.REVERSE_SEARCH]: [{ key: 'r', ctrl: true }],
- // Original: key.name === 'return' && !key.ctrl
- // Note: original logic ONLY checked ctrl=false, ignored meta/shift/paste
- [Command.SUBMIT_REVERSE_SEARCH]: [{ key: 'return', ctrl: false }],
- // Original: key.name === 'tab'
- [Command.ACCEPT_SUGGESTION_REVERSE_SEARCH]: [{ key: 'tab' }],
-};
diff --git a/packages/cli/src/config/sandboxConfig.ts b/packages/cli/src/config/sandboxConfig.ts
deleted file mode 100644
index 2255714..0000000
--- a/packages/cli/src/config/sandboxConfig.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { SandboxConfig } from '@anus-dev/anus-core';
-import commandExists from 'command-exists';
-import * as os from 'node:os';
-import { getPackageJson } from '../utils/package.js';
-import { Settings } from './settings.js';
-
-// This is a stripped-down version of the CliArgs interface from config.ts
-// to avoid circular dependencies.
-interface SandboxCliArgs {
- sandbox?: boolean | string;
- sandboxImage?: string;
-}
-
-const VALID_SANDBOX_COMMANDS: ReadonlyArray = [
- 'docker',
- 'podman',
- 'sandbox-exec',
-];
-
-function isSandboxCommand(value: string): value is SandboxConfig['command'] {
- return (VALID_SANDBOX_COMMANDS as readonly string[]).includes(value);
-}
-
-function getSandboxCommand(
- sandbox?: boolean | string,
-): SandboxConfig['command'] | '' {
- // If the SANDBOX env var is set, we're already inside the sandbox.
- if (process.env['SANDBOX']) {
- return '';
- }
-
- // note environment variable takes precedence over argument (from command line or settings)
- const environmentConfiguredSandbox =
- process.env['ANUS_SANDBOX']?.toLowerCase().trim() ?? '';
- sandbox =
- environmentConfiguredSandbox?.length > 0
- ? environmentConfiguredSandbox
- : sandbox;
- if (sandbox === '1' || sandbox === 'true') sandbox = true;
- else if (sandbox === '0' || sandbox === 'false' || !sandbox) sandbox = false;
-
- if (sandbox === false) {
- return '';
- }
-
- if (typeof sandbox === 'string' && sandbox) {
- if (!isSandboxCommand(sandbox)) {
- console.error(
- `ERROR: invalid sandbox command '${sandbox}'. Must be one of ${VALID_SANDBOX_COMMANDS.join(
- ', ',
- )}`,
- );
- process.exit(1);
- }
- // confirm that specified command exists
- if (commandExists.sync(sandbox)) {
- return sandbox;
- }
- console.error(
- `ERROR: missing sandbox command '${sandbox}' (from ANUS_SANDBOX)`,
- );
- process.exit(1);
- }
-
- // look for seatbelt, docker, or podman, in that order
- // for container-based sandboxing, require sandbox to be enabled explicitly
- if (os.platform() === 'darwin' && commandExists.sync('sandbox-exec')) {
- return 'sandbox-exec';
- } else if (commandExists.sync('docker') && sandbox === true) {
- return 'docker';
- } else if (commandExists.sync('podman') && sandbox === true) {
- return 'podman';
- }
-
- // throw an error if user requested sandbox but no command was found
- if (sandbox === true) {
- console.error(
- 'ERROR: ANUS_SANDBOX is true but failed to determine command for sandbox; ' +
- 'install docker or podman or specify command in ANUS_SANDBOX',
- );
- process.exit(1);
- }
-
- return '';
-}
-
-export async function loadSandboxConfig(
- settings: Settings,
- argv: SandboxCliArgs,
-): Promise {
- const sandboxOption = argv.sandbox ?? settings.sandbox;
- const command = getSandboxCommand(sandboxOption);
-
- const packageJson = await getPackageJson();
- const image =
- argv.sandboxImage ??
- process.env['ANUS_SANDBOX_IMAGE'] ??
- packageJson?.config?.sandboxImageUri;
-
- return command && image ? { command, image } : undefined;
-}
diff --git a/packages/cli/src/config/settings.test.ts b/packages/cli/src/config/settings.test.ts
deleted file mode 100644
index 7850c7b..0000000
--- a/packages/cli/src/config/settings.test.ts
+++ /dev/null
@@ -1,1491 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-///
-
-// Mock 'os' first.
-import * as osActual from 'os'; // Import for type info for the mock factory
-vi.mock('os', async (importOriginal) => {
- const actualOs = await importOriginal();
- return {
- ...actualOs,
- homedir: vi.fn(() => '/mock/home/user'),
- platform: vi.fn(() => 'linux'),
- };
-});
-
-// Mock './settings.js' to ensure it uses the mocked 'os.homedir()' for its internal constants.
-vi.mock('./settings.js', async (importActual) => {
- const originalModule = await importActual();
- return {
- __esModule: true, // Ensure correct module shape
- ...originalModule, // Re-export all original members
- // We are relying on originalModule's USER_SETTINGS_PATH being constructed with mocked os.homedir()
- };
-});
-
-// NOW import everything else, including the (now effectively re-exported) settings.js
-import * as pathActual from 'path'; // Restored for MOCK_WORKSPACE_SETTINGS_PATH
-import {
- describe,
- it,
- expect,
- vi,
- beforeEach,
- afterEach,
- type Mocked,
- type Mock,
-} from 'vitest';
-import * as fs from 'fs'; // fs will be mocked separately
-import stripJsonComments from 'strip-json-comments'; // Will be mocked separately
-
-// These imports will get the versions from the vi.mock('./settings.js', ...) factory.
-import {
- loadSettings,
- USER_SETTINGS_PATH, // This IS the mocked path.
- getSystemSettingsPath,
- SETTINGS_DIRECTORY_NAME, // This is from the original module, but used by the mock.
- SettingScope,
-} from './settings.js';
-
-const MOCK_WORKSPACE_DIR = '/mock/workspace';
-// Use the (mocked) SETTINGS_DIRECTORY_NAME for consistency
-const MOCK_WORKSPACE_SETTINGS_PATH = pathActual.join(
- MOCK_WORKSPACE_DIR,
- SETTINGS_DIRECTORY_NAME,
- 'settings.json',
-);
-
-vi.mock('fs', async (importOriginal) => {
- // Get all the functions from the real 'fs' module
- const actualFs = await importOriginal();
-
- return {
- ...actualFs, // Keep all the real functions
- // Now, just override the ones we need for the test
- existsSync: vi.fn(),
- readFileSync: vi.fn(),
- writeFileSync: vi.fn(),
- mkdirSync: vi.fn(),
- realpathSync: (p: string) => p,
- };
-});
-
-vi.mock('strip-json-comments', () => ({
- default: vi.fn((content) => content),
-}));
-
-describe('Settings Loading and Merging', () => {
- let mockFsExistsSync: Mocked;
- let mockStripJsonComments: Mocked;
- let mockFsMkdirSync: Mocked;
-
- beforeEach(() => {
- vi.resetAllMocks();
-
- mockFsExistsSync = vi.mocked(fs.existsSync);
- mockFsMkdirSync = vi.mocked(fs.mkdirSync);
- mockStripJsonComments = vi.mocked(stripJsonComments);
-
- vi.mocked(osActual.homedir).mockReturnValue('/mock/home/user');
- (mockStripJsonComments as unknown as Mock).mockImplementation(
- (jsonString: string) => jsonString,
- );
- (mockFsExistsSync as Mock).mockReturnValue(false);
- (fs.readFileSync as Mock).mockReturnValue('{}'); // Return valid empty JSON
- (mockFsMkdirSync as Mock).mockImplementation(() => undefined);
- });
-
- afterEach(() => {
- vi.restoreAllMocks();
- });
-
- describe('loadSettings', () => {
- it('should load empty settings if no files exist', () => {
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.system.settings).toEqual({});
- expect(settings.user.settings).toEqual({});
- expect(settings.workspace.settings).toEqual({});
- expect(settings.merged).toEqual({
- customThemes: {},
- mcpServers: {},
- includeDirectories: [],
- chatCompression: {},
- });
- expect(settings.errors.length).toBe(0);
- });
-
- it('should load system settings if only system file exists', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === getSystemSettingsPath(),
- );
- const systemSettingsContent = {
- theme: 'system-default',
- sandbox: false,
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === getSystemSettingsPath())
- return JSON.stringify(systemSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(fs.readFileSync).toHaveBeenCalledWith(
- getSystemSettingsPath(),
- 'utf-8',
- );
- expect(settings.system.settings).toEqual(systemSettingsContent);
- expect(settings.user.settings).toEqual({});
- expect(settings.workspace.settings).toEqual({});
- expect(settings.merged).toEqual({
- ...systemSettingsContent,
- customThemes: {},
- mcpServers: {},
- includeDirectories: [],
- chatCompression: {},
- });
- });
-
- it('should load user settings if only user file exists', () => {
- const expectedUserSettingsPath = USER_SETTINGS_PATH; // Use the path actually resolved by the (mocked) module
-
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === expectedUserSettingsPath,
- );
- const userSettingsContent = {
- theme: 'dark',
- contextFileName: 'USER_CONTEXT.md',
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === expectedUserSettingsPath)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(fs.readFileSync).toHaveBeenCalledWith(
- expectedUserSettingsPath,
- 'utf-8',
- );
- expect(settings.user.settings).toEqual(userSettingsContent);
- expect(settings.workspace.settings).toEqual({});
- expect(settings.merged).toEqual({
- ...userSettingsContent,
- customThemes: {},
- mcpServers: {},
- includeDirectories: [],
- chatCompression: {},
- });
- });
-
- it('should load workspace settings if only workspace file exists', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
- );
- const workspaceSettingsContent = {
- sandbox: true,
- contextFileName: 'WORKSPACE_CONTEXT.md',
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(fs.readFileSync).toHaveBeenCalledWith(
- MOCK_WORKSPACE_SETTINGS_PATH,
- 'utf-8',
- );
- expect(settings.user.settings).toEqual({});
- expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
- expect(settings.merged).toEqual({
- ...workspaceSettingsContent,
- customThemes: {},
- mcpServers: {},
- includeDirectories: [],
- chatCompression: {},
- });
- });
-
- it('should merge user and workspace settings, with workspace taking precedence', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = {
- theme: 'dark',
- sandbox: false,
- contextFileName: 'USER_CONTEXT.md',
- };
- const workspaceSettingsContent = {
- sandbox: true,
- coreTools: ['tool1'],
- contextFileName: 'WORKSPACE_CONTEXT.md',
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.user.settings).toEqual(userSettingsContent);
- expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
- expect(settings.merged).toEqual({
- theme: 'dark',
- sandbox: true,
- coreTools: ['tool1'],
- contextFileName: 'WORKSPACE_CONTEXT.md',
- customThemes: {},
- mcpServers: {},
- includeDirectories: [],
- chatCompression: {},
- });
- });
-
- it('should merge system, user and workspace settings, with system taking precedence over workspace, and workspace over user', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const systemSettingsContent = {
- theme: 'system-theme',
- sandbox: false,
- allowMCPServers: ['server1', 'server2'],
- telemetry: { enabled: false },
- };
- const userSettingsContent = {
- theme: 'dark',
- sandbox: true,
- contextFileName: 'USER_CONTEXT.md',
- };
- const workspaceSettingsContent = {
- sandbox: false,
- coreTools: ['tool1'],
- contextFileName: 'WORKSPACE_CONTEXT.md',
- allowMCPServers: ['server1', 'server2', 'server3'],
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === getSystemSettingsPath())
- return JSON.stringify(systemSettingsContent);
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.system.settings).toEqual(systemSettingsContent);
- expect(settings.user.settings).toEqual(userSettingsContent);
- expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
- expect(settings.merged).toEqual({
- theme: 'system-theme',
- sandbox: false,
- telemetry: { enabled: false },
- coreTools: ['tool1'],
- contextFileName: 'WORKSPACE_CONTEXT.md',
- allowMCPServers: ['server1', 'server2'],
- customThemes: {},
- mcpServers: {},
- includeDirectories: [],
- chatCompression: {},
- });
- });
-
- it('should ignore folderTrust from workspace settings', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = {
- folderTrust: true,
- };
- const workspaceSettingsContent = {
- folderTrust: false, // This should be ignored
- };
- const systemSettingsContent = {
- // No folderTrust here
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === getSystemSettingsPath())
- return JSON.stringify(systemSettingsContent);
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.folderTrust).toBe(true); // User setting should be used
- });
-
- it('should use system folderTrust over user setting', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = {
- folderTrust: false,
- };
- const workspaceSettingsContent = {
- folderTrust: true, // This should be ignored
- };
- const systemSettingsContent = {
- folderTrust: true,
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === getSystemSettingsPath())
- return JSON.stringify(systemSettingsContent);
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.folderTrust).toBe(true); // System setting should be used
- });
-
- it('should handle contextFileName correctly when only in user settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- const userSettingsContent = { contextFileName: 'CUSTOM.md' };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.contextFileName).toBe('CUSTOM.md');
- });
-
- it('should handle contextFileName correctly when only in workspace settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
- );
- const workspaceSettingsContent = {
- contextFileName: 'PROJECT_SPECIFIC.md',
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.contextFileName).toBe('PROJECT_SPECIFIC.md');
- });
-
- it('should handle excludedProjectEnvVars correctly when only in user settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- const userSettingsContent = {
- excludedProjectEnvVars: ['DEBUG', 'NODE_ENV', 'CUSTOM_VAR'],
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.excludedProjectEnvVars).toEqual([
- 'DEBUG',
- 'NODE_ENV',
- 'CUSTOM_VAR',
- ]);
- });
-
- it('should handle excludedProjectEnvVars correctly when only in workspace settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
- );
- const workspaceSettingsContent = {
- excludedProjectEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.excludedProjectEnvVars).toEqual([
- 'WORKSPACE_DEBUG',
- 'WORKSPACE_VAR',
- ]);
- });
-
- it('should merge excludedProjectEnvVars with workspace taking precedence over user', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = {
- excludedProjectEnvVars: ['DEBUG', 'NODE_ENV', 'USER_VAR'],
- };
- const workspaceSettingsContent = {
- excludedProjectEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.user.settings.excludedProjectEnvVars).toEqual([
- 'DEBUG',
- 'NODE_ENV',
- 'USER_VAR',
- ]);
- expect(settings.workspace.settings.excludedProjectEnvVars).toEqual([
- 'WORKSPACE_DEBUG',
- 'WORKSPACE_VAR',
- ]);
- expect(settings.merged.excludedProjectEnvVars).toEqual([
- 'WORKSPACE_DEBUG',
- 'WORKSPACE_VAR',
- ]);
- });
-
- it('should default contextFileName to undefined if not in any settings file', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = { theme: 'dark' };
- const workspaceSettingsContent = { sandbox: true };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.contextFileName).toBeUndefined();
- });
-
- it('should load telemetry setting from user settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- const userSettingsContent = { telemetry: true };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.telemetry).toBe(true);
- });
-
- it('should load telemetry setting from workspace settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
- );
- const workspaceSettingsContent = { telemetry: false };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.telemetry).toBe(false);
- });
-
- it('should prioritize workspace telemetry setting over user setting', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = { telemetry: true };
- const workspaceSettingsContent = { telemetry: false };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.telemetry).toBe(false);
- });
-
- it('should have telemetry as undefined if not in any settings file', () => {
- (mockFsExistsSync as Mock).mockReturnValue(false); // No settings files exist
- (fs.readFileSync as Mock).mockReturnValue('{}');
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.telemetry).toBeUndefined();
- expect(settings.merged.customThemes).toEqual({});
- expect(settings.merged.mcpServers).toEqual({});
- });
-
- it('should merge MCP servers correctly, with workspace taking precedence', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = {
- mcpServers: {
- 'user-server': {
- command: 'user-command',
- args: ['--user-arg'],
- description: 'User MCP server',
- },
- 'shared-server': {
- command: 'user-shared-command',
- description: 'User shared server config',
- },
- },
- };
- const workspaceSettingsContent = {
- mcpServers: {
- 'workspace-server': {
- command: 'workspace-command',
- args: ['--workspace-arg'],
- description: 'Workspace MCP server',
- },
- 'shared-server': {
- command: 'workspace-shared-command',
- description: 'Workspace shared server config',
- },
- },
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.user.settings).toEqual(userSettingsContent);
- expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
- expect(settings.merged.mcpServers).toEqual({
- 'user-server': {
- command: 'user-command',
- args: ['--user-arg'],
- description: 'User MCP server',
- },
- 'workspace-server': {
- command: 'workspace-command',
- args: ['--workspace-arg'],
- description: 'Workspace MCP server',
- },
- 'shared-server': {
- command: 'workspace-shared-command',
- description: 'Workspace shared server config',
- },
- });
- });
-
- it('should handle MCP servers when only in user settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- const userSettingsContent = {
- mcpServers: {
- 'user-only-server': {
- command: 'user-only-command',
- description: 'User only server',
- },
- },
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.mcpServers).toEqual({
- 'user-only-server': {
- command: 'user-only-command',
- description: 'User only server',
- },
- });
- });
-
- it('should handle MCP servers when only in workspace settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
- );
- const workspaceSettingsContent = {
- mcpServers: {
- 'workspace-only-server': {
- command: 'workspace-only-command',
- description: 'Workspace only server',
- },
- },
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.mcpServers).toEqual({
- 'workspace-only-server': {
- command: 'workspace-only-command',
- description: 'Workspace only server',
- },
- });
- });
-
- it('should have mcpServers as empty object if not in any settings file', () => {
- (mockFsExistsSync as Mock).mockReturnValue(false); // No settings files exist
- (fs.readFileSync as Mock).mockReturnValue('{}');
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.mcpServers).toEqual({});
- });
-
- it('should merge chatCompression settings, with workspace taking precedence', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = {
- chatCompression: { contextPercentageThreshold: 0.5 },
- };
- const workspaceSettingsContent = {
- chatCompression: { contextPercentageThreshold: 0.8 },
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.user.settings.chatCompression).toEqual({
- contextPercentageThreshold: 0.5,
- });
- expect(settings.workspace.settings.chatCompression).toEqual({
- contextPercentageThreshold: 0.8,
- });
- expect(settings.merged.chatCompression).toEqual({
- contextPercentageThreshold: 0.8,
- });
- });
-
- it('should handle chatCompression when only in user settings', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- const userSettingsContent = {
- chatCompression: { contextPercentageThreshold: 0.5 },
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.chatCompression).toEqual({
- contextPercentageThreshold: 0.5,
- });
- });
-
- it('should have chatCompression as an empty object if not in any settings file', () => {
- (mockFsExistsSync as Mock).mockReturnValue(false); // No settings files exist
- (fs.readFileSync as Mock).mockReturnValue('{}');
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.chatCompression).toEqual({});
- });
-
- it('should ignore chatCompression if contextPercentageThreshold is invalid', () => {
- const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- const userSettingsContent = {
- chatCompression: { contextPercentageThreshold: 1.5 },
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.chatCompression).toBeUndefined();
- expect(warnSpy).toHaveBeenCalledWith(
- 'Invalid value for chatCompression.contextPercentageThreshold: "1.5". Please use a value between 0 and 1. Using default compression settings.',
- );
- warnSpy.mockRestore();
- });
-
- it('should deep merge chatCompression settings', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = {
- chatCompression: { contextPercentageThreshold: 0.5 },
- };
- const workspaceSettingsContent = {
- chatCompression: {},
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.merged.chatCompression).toEqual({
- contextPercentageThreshold: 0.5,
- });
- });
-
- it('should merge includeDirectories from all scopes', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const systemSettingsContent = {
- includeDirectories: ['/system/dir'],
- };
- const userSettingsContent = {
- includeDirectories: ['/user/dir1', '/user/dir2'],
- };
- const workspaceSettingsContent = {
- includeDirectories: ['/workspace/dir'],
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === getSystemSettingsPath())
- return JSON.stringify(systemSettingsContent);
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.merged.includeDirectories).toEqual([
- '/system/dir',
- '/user/dir1',
- '/user/dir2',
- '/workspace/dir',
- ]);
- });
-
- it('should handle JSON parsing errors gracefully', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true); // Both files "exist"
- const invalidJsonContent = 'invalid json';
- const userReadError = new SyntaxError(
- "Expected ',' or '}' after property value in JSON at position 10",
- );
- const workspaceReadError = new SyntaxError(
- 'Unexpected token i in JSON at position 0',
- );
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH) {
- // Simulate JSON.parse throwing for user settings
- vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
- throw userReadError;
- });
- return invalidJsonContent; // Content that would cause JSON.parse to throw
- }
- if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
- // Simulate JSON.parse throwing for workspace settings
- vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
- throw workspaceReadError;
- });
- return invalidJsonContent;
- }
- return '{}'; // Default for other reads
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- // Check that settings are empty due to parsing errors
- expect(settings.user.settings).toEqual({});
- expect(settings.workspace.settings).toEqual({});
- expect(settings.merged).toEqual({
- customThemes: {},
- mcpServers: {},
- includeDirectories: [],
- chatCompression: {},
- });
-
- // Check that error objects are populated in settings.errors
- expect(settings.errors).toBeDefined();
- // Assuming both user and workspace files cause errors and are added in order
- expect(settings.errors.length).toEqual(2);
-
- const userError = settings.errors.find(
- (e) => e.path === USER_SETTINGS_PATH,
- );
- expect(userError).toBeDefined();
- expect(userError?.message).toBe(userReadError.message);
-
- const workspaceError = settings.errors.find(
- (e) => e.path === MOCK_WORKSPACE_SETTINGS_PATH,
- );
- expect(workspaceError).toBeDefined();
- expect(workspaceError?.message).toBe(workspaceReadError.message);
-
- // Restore JSON.parse mock if it was spied on specifically for this test
- vi.restoreAllMocks(); // Or more targeted restore if needed
- });
-
- it('should resolve environment variables in user settings', () => {
- process.env['TEST_API_KEY'] = 'user_api_key_from_env';
- const userSettingsContent = {
- apiKey: '$TEST_API_KEY',
- someUrl: 'https://test.com/${TEST_API_KEY}',
- };
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- // @ts-expect-error: dynamic property for test
- expect(settings.user.settings.apiKey).toBe('user_api_key_from_env');
- // @ts-expect-error: dynamic property for test
- expect(settings.user.settings.someUrl).toBe(
- 'https://test.com/user_api_key_from_env',
- );
- // @ts-expect-error: dynamic property for test
- expect(settings.merged.apiKey).toBe('user_api_key_from_env');
- delete process.env['TEST_API_KEY'];
- });
-
- it('should resolve environment variables in workspace settings', () => {
- process.env['WORKSPACE_ENDPOINT'] = 'workspace_endpoint_from_env';
- const workspaceSettingsContent = {
- endpoint: '${WORKSPACE_ENDPOINT}/api',
- nested: { value: '$WORKSPACE_ENDPOINT' },
- };
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
- );
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.workspace.settings.endpoint).toBe(
- 'workspace_endpoint_from_env/api',
- );
- expect(settings.workspace.settings.nested.value).toBe(
- 'workspace_endpoint_from_env',
- );
- // @ts-expect-error: dynamic property for test
- expect(settings.merged.endpoint).toBe('workspace_endpoint_from_env/api');
- delete process.env['WORKSPACE_ENDPOINT'];
- });
-
- it('should prioritize user env variables over workspace env variables if keys clash after resolution', () => {
- const userSettingsContent = { configValue: '$SHARED_VAR' };
- const workspaceSettingsContent = { configValue: '$SHARED_VAR' };
-
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const originalSharedVar = process.env['SHARED_VAR'];
- // Temporarily delete to ensure a clean slate for the test's specific manipulations
- delete process.env['SHARED_VAR'];
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH) {
- process.env['SHARED_VAR'] = 'user_value_for_user_read'; // Set for user settings read
- return JSON.stringify(userSettingsContent);
- }
- if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
- process.env['SHARED_VAR'] = 'workspace_value_for_workspace_read'; // Set for workspace settings read
- return JSON.stringify(workspaceSettingsContent);
- }
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- // @ts-expect-error: dynamic property for test
- expect(settings.user.settings.configValue).toBe(
- 'user_value_for_user_read',
- );
- // @ts-expect-error: dynamic property for test
- expect(settings.workspace.settings.configValue).toBe(
- 'workspace_value_for_workspace_read',
- );
- // Merged should take workspace's resolved value
- // @ts-expect-error: dynamic property for test
- expect(settings.merged.configValue).toBe(
- 'workspace_value_for_workspace_read',
- );
-
- // Restore original environment variable state
- if (originalSharedVar !== undefined) {
- process.env['SHARED_VAR'] = originalSharedVar;
- } else {
- delete process.env['SHARED_VAR']; // Ensure it's deleted if it wasn't there before
- }
- });
-
- it('should prioritize workspace env variables over user env variables if keys clash after resolution', () => {
- const userSettingsContent = { configValue: '$SHARED_VAR' };
- const workspaceSettingsContent = { configValue: '$SHARED_VAR' };
-
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const originalSharedVar = process.env['SHARED_VAR'];
- // Temporarily delete to ensure a clean slate for the test's specific manipulations
- delete process.env['SHARED_VAR'];
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH) {
- process.env['SHARED_VAR'] = 'user_value_for_user_read'; // Set for user settings read
- return JSON.stringify(userSettingsContent);
- }
- if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
- process.env['SHARED_VAR'] = 'workspace_value_for_workspace_read'; // Set for workspace settings read
- return JSON.stringify(workspaceSettingsContent);
- }
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.user.settings.configValue).toBe(
- 'user_value_for_user_read',
- );
- expect(settings.workspace.settings.configValue).toBe(
- 'workspace_value_for_workspace_read',
- );
- // Merged should take workspace's resolved value
- expect(settings.merged.configValue).toBe(
- 'workspace_value_for_workspace_read',
- );
-
- // Restore original environment variable state
- if (originalSharedVar !== undefined) {
- process.env['SHARED_VAR'] = originalSharedVar;
- } else {
- delete process.env['SHARED_VAR']; // Ensure it's deleted if it wasn't there before
- }
- });
-
- it('should prioritize system env variables over workspace env variables if keys clash after resolution', () => {
- const workspaceSettingsContent = { configValue: '$SHARED_VAR' };
- const systemSettingsContent = { configValue: '$SHARED_VAR' };
-
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const originalSharedVar = process.env['SHARED_VAR'];
- // Temporarily delete to ensure a clean slate for the test's specific manipulations
- delete process.env['SHARED_VAR'];
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === getSystemSettingsPath()) {
- process.env['SHARED_VAR'] = 'system_value_for_system_read'; // Set for system settings read
- return JSON.stringify(systemSettingsContent);
- }
- if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
- process.env['SHARED_VAR'] = 'workspace_value_for_workspace_read'; // Set for workspace settings read
- return JSON.stringify(workspaceSettingsContent);
- }
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- // @ts-expect-error: dynamic property for test
- expect(settings.system.settings.configValue).toBe(
- 'system_value_for_system_read',
- );
- // @ts-expect-error: dynamic property for test
- expect(settings.workspace.settings.configValue).toBe(
- 'workspace_value_for_workspace_read',
- );
- // Merged should take system's resolved value
- // @ts-expect-error: dynamic property for test
- expect(settings.merged.configValue).toBe('system_value_for_system_read');
-
- // Restore original environment variable state
- if (originalSharedVar !== undefined) {
- process.env['SHARED_VAR'] = originalSharedVar;
- } else {
- delete process.env['SHARED_VAR']; // Ensure it's deleted if it wasn't there before
- }
- });
-
- it('should correctly merge dnsResolutionOrder with workspace taking precedence', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
- const userSettingsContent = {
- dnsResolutionOrder: 'ipv4first',
- };
- const workspaceSettingsContent = {
- dnsResolutionOrder: 'verbatim',
- };
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.dnsResolutionOrder).toBe('verbatim');
- });
-
- it('should use user dnsResolutionOrder if workspace is not defined', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- const userSettingsContent = {
- dnsResolutionOrder: 'verbatim',
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.merged.dnsResolutionOrder).toBe('verbatim');
- });
-
- it('should leave unresolved environment variables as is', () => {
- const userSettingsContent = { apiKey: '$UNDEFINED_VAR' };
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.user.settings.apiKey).toBe('$UNDEFINED_VAR');
- expect(settings.merged.apiKey).toBe('$UNDEFINED_VAR');
- });
-
- it('should resolve multiple environment variables in a single string', () => {
- process.env['VAR_A'] = 'valueA';
- process.env['VAR_B'] = 'valueB';
- const userSettingsContent = { path: '/path/$VAR_A/${VAR_B}/end' };
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.user.settings.path).toBe('/path/valueA/valueB/end');
- delete process.env['VAR_A'];
- delete process.env['VAR_B'];
- });
-
- it('should resolve environment variables in arrays', () => {
- process.env['ITEM_1'] = 'item1_env';
- process.env['ITEM_2'] = 'item2_env';
- const userSettingsContent = { list: ['$ITEM_1', '${ITEM_2}', 'literal'] };
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.user.settings.list).toEqual([
- 'item1_env',
- 'item2_env',
- 'literal',
- ]);
- delete process.env['ITEM_1'];
- delete process.env['ITEM_2'];
- });
-
- it('should correctly pass through null, boolean, and number types, and handle undefined properties', () => {
- process.env['MY_ENV_STRING'] = 'env_string_value';
- process.env['MY_ENV_STRING_NESTED'] = 'env_string_nested_value';
-
- const userSettingsContent = {
- nullVal: null,
- trueVal: true,
- falseVal: false,
- numberVal: 123.45,
- stringVal: '$MY_ENV_STRING',
- nestedObj: {
- nestedNull: null,
- nestedBool: true,
- nestedNum: 0,
- nestedString: 'literal',
- anotherEnv: '${MY_ENV_STRING_NESTED}',
- },
- };
-
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.user.settings.nullVal).toBeNull();
- expect(settings.user.settings.trueVal).toBe(true);
- expect(settings.user.settings.falseVal).toBe(false);
- expect(settings.user.settings.numberVal).toBe(123.45);
- expect(settings.user.settings.stringVal).toBe('env_string_value');
- expect(settings.user.settings.undefinedVal).toBeUndefined();
-
- expect(settings.user.settings.nestedObj.nestedNull).toBeNull();
- expect(settings.user.settings.nestedObj.nestedBool).toBe(true);
- expect(settings.user.settings.nestedObj.nestedNum).toBe(0);
- expect(settings.user.settings.nestedObj.nestedString).toBe('literal');
- expect(settings.user.settings.nestedObj.anotherEnv).toBe(
- 'env_string_nested_value',
- );
-
- delete process.env['MY_ENV_STRING'];
- delete process.env['MY_ENV_STRING_NESTED'];
- });
-
- it('should resolve multiple concatenated environment variables in a single string value', () => {
- process.env['TEST_HOST'] = 'myhost';
- process.env['TEST_PORT'] = '9090';
- const userSettingsContent = {
- serverAddress: '${TEST_HOST}:${TEST_PORT}/api',
- };
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.user.settings.serverAddress).toBe('myhost:9090/api');
-
- delete process.env['TEST_HOST'];
- delete process.env['TEST_PORT'];
- });
-
- describe('when ANUS_CLI_SYSTEM_SETTINGS_PATH is set', () => {
- const MOCK_ENV_SYSTEM_SETTINGS_PATH = '/mock/env/system/settings.json';
-
- beforeEach(() => {
- process.env['ANUS_CLI_SYSTEM_SETTINGS_PATH'] =
- MOCK_ENV_SYSTEM_SETTINGS_PATH;
- });
-
- afterEach(() => {
- delete process.env['ANUS_CLI_SYSTEM_SETTINGS_PATH'];
- });
-
- it('should load system settings from the path specified in the environment variable', () => {
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === MOCK_ENV_SYSTEM_SETTINGS_PATH,
- );
- const systemSettingsContent = {
- theme: 'env-var-theme',
- sandbox: true,
- };
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === MOCK_ENV_SYSTEM_SETTINGS_PATH)
- return JSON.stringify(systemSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(fs.readFileSync).toHaveBeenCalledWith(
- MOCK_ENV_SYSTEM_SETTINGS_PATH,
- 'utf-8',
- );
- expect(settings.system.path).toBe(MOCK_ENV_SYSTEM_SETTINGS_PATH);
- expect(settings.system.settings).toEqual(systemSettingsContent);
- expect(settings.merged).toEqual({
- ...systemSettingsContent,
- customThemes: {},
- mcpServers: {},
- includeDirectories: [],
- chatCompression: {},
- });
- });
- });
- });
-
- describe('LoadedSettings class', () => {
- it('setValue should update the correct scope and recompute merged settings', () => {
- (mockFsExistsSync as Mock).mockReturnValue(false);
- const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
-
- vi.mocked(fs.writeFileSync).mockImplementation(() => {});
- // mkdirSync is mocked in beforeEach to return undefined, which is fine for void usage
-
- loadedSettings.setValue(SettingScope.User, 'theme', 'matrix');
- expect(loadedSettings.user.settings.theme).toBe('matrix');
- expect(loadedSettings.merged.theme).toBe('matrix');
- expect(fs.writeFileSync).toHaveBeenCalledWith(
- USER_SETTINGS_PATH,
- JSON.stringify({ theme: 'matrix' }, null, 2),
- 'utf-8',
- );
-
- loadedSettings.setValue(
- SettingScope.Workspace,
- 'contextFileName',
- 'MY_AGENTS.md',
- );
- expect(loadedSettings.workspace.settings.contextFileName).toBe(
- 'MY_AGENTS.md',
- );
- expect(loadedSettings.merged.contextFileName).toBe('MY_AGENTS.md');
- expect(loadedSettings.merged.theme).toBe('matrix'); // User setting should still be there
- expect(fs.writeFileSync).toHaveBeenCalledWith(
- MOCK_WORKSPACE_SETTINGS_PATH,
- JSON.stringify({ contextFileName: 'MY_AGENTS.md' }, null, 2),
- 'utf-8',
- );
-
- // System theme overrides user and workspace themes
- loadedSettings.setValue(SettingScope.System, 'theme', 'ocean');
-
- expect(loadedSettings.system.settings.theme).toBe('ocean');
- expect(loadedSettings.merged.theme).toBe('ocean');
- });
- });
-
- describe('excludedProjectEnvVars integration', () => {
- const originalEnv = { ...process.env };
-
- beforeEach(() => {
- process.env = { ...originalEnv };
- });
-
- afterEach(() => {
- process.env = originalEnv;
- });
-
- it('should exclude DEBUG and DEBUG_MODE from project .env files by default', () => {
- // Create a workspace settings file with excludedProjectEnvVars
- const workspaceSettingsContent = {
- excludedProjectEnvVars: ['DEBUG', 'DEBUG_MODE'],
- };
-
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
- );
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- // Mock findEnvFile to return a project .env file
- const originalFindEnvFile = (
- loadSettings as unknown as { findEnvFile: () => string }
- ).findEnvFile;
- (loadSettings as unknown as { findEnvFile: () => string }).findEnvFile =
- () => '/mock/project/.env';
-
- // Mock fs.readFileSync for .env file content
- const originalReadFileSync = fs.readFileSync;
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === '/mock/project/.env') {
- return 'DEBUG=true\nDEBUG_MODE=1\nANUS_API_KEY=test-key';
- }
- if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
- return JSON.stringify(workspaceSettingsContent);
- }
- return '{}';
- },
- );
-
- try {
- // This will call loadEnvironment internally with the merged settings
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- // Verify the settings were loaded correctly
- expect(settings.merged.excludedProjectEnvVars).toEqual([
- 'DEBUG',
- 'DEBUG_MODE',
- ]);
-
- // Note: We can't directly test process.env changes here because the mocking
- // prevents the actual file system operations, but we can verify the settings
- // are correctly merged and passed to loadEnvironment
- } finally {
- (loadSettings as unknown as { findEnvFile: () => string }).findEnvFile =
- originalFindEnvFile;
- (fs.readFileSync as Mock).mockImplementation(originalReadFileSync);
- }
- });
-
- it('should respect custom excludedProjectEnvVars from user settings', () => {
- const userSettingsContent = {
- excludedProjectEnvVars: ['NODE_ENV', 'DEBUG'],
- };
-
- (mockFsExistsSync as Mock).mockImplementation(
- (p: fs.PathLike) => p === USER_SETTINGS_PATH,
- );
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
- expect(settings.user.settings.excludedProjectEnvVars).toEqual([
- 'NODE_ENV',
- 'DEBUG',
- ]);
- expect(settings.merged.excludedProjectEnvVars).toEqual([
- 'NODE_ENV',
- 'DEBUG',
- ]);
- });
-
- it('should merge excludedProjectEnvVars with workspace taking precedence', () => {
- const userSettingsContent = {
- excludedProjectEnvVars: ['DEBUG', 'NODE_ENV', 'USER_VAR'],
- };
- const workspaceSettingsContent = {
- excludedProjectEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
- };
-
- (mockFsExistsSync as Mock).mockReturnValue(true);
-
- (fs.readFileSync as Mock).mockImplementation(
- (p: fs.PathOrFileDescriptor) => {
- if (p === USER_SETTINGS_PATH)
- return JSON.stringify(userSettingsContent);
- if (p === MOCK_WORKSPACE_SETTINGS_PATH)
- return JSON.stringify(workspaceSettingsContent);
- return '{}';
- },
- );
-
- const settings = loadSettings(MOCK_WORKSPACE_DIR);
-
- expect(settings.user.settings.excludedProjectEnvVars).toEqual([
- 'DEBUG',
- 'NODE_ENV',
- 'USER_VAR',
- ]);
- expect(settings.workspace.settings.excludedProjectEnvVars).toEqual([
- 'WORKSPACE_DEBUG',
- 'WORKSPACE_VAR',
- ]);
- expect(settings.merged.excludedProjectEnvVars).toEqual([
- 'WORKSPACE_DEBUG',
- 'WORKSPACE_VAR',
- ]);
- });
- });
-});
diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts
deleted file mode 100644
index dc1ba17..0000000
--- a/packages/cli/src/config/settings.ts
+++ /dev/null
@@ -1,455 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import * as fs from 'fs';
-import * as path from 'path';
-import { homedir, platform } from 'os';
-import * as dotenv from 'dotenv';
-import {
- ANUS_CONFIG_DIR as ANUS_DIR,
- getErrorMessage,
-} from '@anus-dev/anus-core';
-import stripJsonComments from 'strip-json-comments';
-import { DefaultLight } from '../ui/themes/default-light.js';
-import { DefaultDark } from '../ui/themes/default.js';
-import { Settings, MemoryImportFormat } from './settingsSchema.js';
-
-export type { Settings, MemoryImportFormat };
-
-export const SETTINGS_DIRECTORY_NAME = '.anus';
-export const USER_SETTINGS_DIR = path.join(homedir(), SETTINGS_DIRECTORY_NAME);
-export const USER_SETTINGS_PATH = path.join(USER_SETTINGS_DIR, 'settings.json');
-export const DEFAULT_EXCLUDED_ENV_VARS = ['DEBUG', 'DEBUG_MODE'];
-
-export function getSystemSettingsPath(): string {
- if (process.env['ANUS_CLI_SYSTEM_SETTINGS_PATH']) {
- return process.env['ANUS_CLI_SYSTEM_SETTINGS_PATH'];
- }
- if (platform() === 'darwin') {
- return '/Library/Application Support/AnusCli/settings.json';
- } else if (platform() === 'win32') {
- return 'C:\\ProgramData\\anus-cli\\settings.json';
- } else {
- return '/etc/anus-cli/settings.json';
- }
-}
-
-export function getWorkspaceSettingsPath(workspaceDir: string): string {
- return path.join(workspaceDir, SETTINGS_DIRECTORY_NAME, 'settings.json');
-}
-
-export type { DnsResolutionOrder } from './settingsSchema.js';
-
-export enum SettingScope {
- User = 'User',
- Workspace = 'Workspace',
- System = 'System',
-}
-
-export interface CheckpointingSettings {
- enabled?: boolean;
-}
-
-export interface SummarizeToolOutputSettings {
- tokenBudget?: number;
-}
-
-export interface AccessibilitySettings {
- disableLoadingPhrases?: boolean;
-}
-
-export interface SettingsError {
- message: string;
- path: string;
-}
-
-export interface SettingsFile {
- settings: Settings;
- path: string;
-}
-export class LoadedSettings {
- constructor(
- system: SettingsFile,
- user: SettingsFile,
- workspace: SettingsFile,
- errors: SettingsError[],
- ) {
- this.system = system;
- this.user = user;
- this.workspace = workspace;
- this.errors = errors;
- this._merged = this.computeMergedSettings();
- }
-
- readonly system: SettingsFile;
- readonly user: SettingsFile;
- readonly workspace: SettingsFile;
- readonly errors: SettingsError[];
-
- private _merged: Settings;
-
- get merged(): Settings {
- return this._merged;
- }
-
- private computeMergedSettings(): Settings {
- const system = this.system.settings;
- const user = this.user.settings;
- const workspace = this.workspace.settings;
-
- // folderTrust is not supported at workspace level.
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { folderTrust, ...workspaceWithoutFolderTrust } = workspace;
-
- return {
- ...user,
- ...workspaceWithoutFolderTrust,
- ...system,
- customThemes: {
- ...(user.customThemes || {}),
- ...(workspace.customThemes || {}),
- ...(system.customThemes || {}),
- },
- mcpServers: {
- ...(user.mcpServers || {}),
- ...(workspace.mcpServers || {}),
- ...(system.mcpServers || {}),
- },
- includeDirectories: [
- ...(system.includeDirectories || []),
- ...(user.includeDirectories || []),
- ...(workspace.includeDirectories || []),
- ],
- chatCompression: {
- ...(system.chatCompression || {}),
- ...(user.chatCompression || {}),
- ...(workspace.chatCompression || {}),
- },
- };
- }
-
- forScope(scope: SettingScope): SettingsFile {
- switch (scope) {
- case SettingScope.User:
- return this.user;
- case SettingScope.Workspace:
- return this.workspace;
- case SettingScope.System:
- return this.system;
- default:
- throw new Error(`Invalid scope: ${scope}`);
- }
- }
-
- setValue(
- scope: SettingScope,
- key: K,
- value: Settings[K],
- ): void {
- const settingsFile = this.forScope(scope);
- settingsFile.settings[key] = value;
- this._merged = this.computeMergedSettings();
- saveSettings(settingsFile);
- }
-}
-
-function resolveEnvVarsInString(value: string): string {
- const envVarRegex = /\$(?:(\w+)|{([^}]+)})/g; // Find $VAR_NAME or ${VAR_NAME}
- return value.replace(envVarRegex, (match, varName1, varName2) => {
- const varName = varName1 || varName2;
- if (process && process.env && typeof process.env[varName] === 'string') {
- return process.env[varName]!;
- }
- return match;
- });
-}
-
-function resolveEnvVarsInObject(obj: T): T {
- if (
- obj === null ||
- obj === undefined ||
- typeof obj === 'boolean' ||
- typeof obj === 'number'
- ) {
- return obj;
- }
-
- if (typeof obj === 'string') {
- return resolveEnvVarsInString(obj) as unknown as T;
- }
-
- if (Array.isArray(obj)) {
- return obj.map((item) => resolveEnvVarsInObject(item)) as unknown as T;
- }
-
- if (typeof obj === 'object') {
- const newObj = { ...obj } as T;
- for (const key in newObj) {
- if (Object.prototype.hasOwnProperty.call(newObj, key)) {
- newObj[key] = resolveEnvVarsInObject(newObj[key]);
- }
- }
- return newObj;
- }
-
- return obj;
-}
-
-function findEnvFile(startDir: string): string | null {
- let currentDir = path.resolve(startDir);
- while (true) {
- // Check for .env under ANUS_DIR only
- const anusEnvPath = path.join(currentDir, ANUS_DIR, '.env');
- if (fs.existsSync(anusEnvPath)) {
- return anusEnvPath;
- }
- const envPath = path.join(currentDir, '.env');
- if (fs.existsSync(envPath)) {
- return envPath;
- }
- const parentDir = path.dirname(currentDir);
- if (parentDir === currentDir || !parentDir) {
- // check .env under home as fallback
- const homeAnusEnvPath = path.join(homedir(), ANUS_DIR, '.env');
- if (fs.existsSync(homeAnusEnvPath)) {
- return homeAnusEnvPath;
- }
- const homeEnvPath = path.join(homedir(), '.env');
- if (fs.existsSync(homeEnvPath)) {
- return homeEnvPath;
- }
- return null;
- }
- currentDir = parentDir;
- }
-}
-
-export function setUpCloudShellEnvironment(envFilePath: string | null): void {
- // Special handling for GOOGLE_CLOUD_PROJECT in Cloud Shell:
- // Because GOOGLE_CLOUD_PROJECT in Cloud Shell tracks the project
- // set by the user using "gcloud config set project" we do not want to
- // use its value. So, unless the user overrides GOOGLE_CLOUD_PROJECT in
- // one of the .env files, we set the Cloud Shell-specific default here.
- if (envFilePath && fs.existsSync(envFilePath)) {
- const envFileContent = fs.readFileSync(envFilePath);
- const parsedEnv = dotenv.parse(envFileContent);
- if (parsedEnv['GOOGLE_CLOUD_PROJECT']) {
- // .env file takes precedence in Cloud Shell
- process.env['GOOGLE_CLOUD_PROJECT'] = parsedEnv['GOOGLE_CLOUD_PROJECT'];
- } else {
- // If not in .env, set to default and override global
- process.env['GOOGLE_CLOUD_PROJECT'] = 'cloudshell-gca';
- }
- } else {
- // If no .env file, set to default and override global
- process.env['GOOGLE_CLOUD_PROJECT'] = 'cloudshell-gca';
- }
-}
-
-export function loadEnvironment(settings?: Settings): void {
- const envFilePath = findEnvFile(process.cwd());
-
- // Cloud Shell environment variable handling
- if (process.env['CLOUD_SHELL'] === 'true') {
- setUpCloudShellEnvironment(envFilePath);
- }
-
- // If no settings provided, try to load workspace settings for exclusions
- let resolvedSettings = settings;
- if (!resolvedSettings) {
- const workspaceSettingsPath = getWorkspaceSettingsPath(process.cwd());
- try {
- if (fs.existsSync(workspaceSettingsPath)) {
- const workspaceContent = fs.readFileSync(
- workspaceSettingsPath,
- 'utf-8',
- );
- const parsedWorkspaceSettings = JSON.parse(
- stripJsonComments(workspaceContent),
- ) as Settings;
- resolvedSettings = resolveEnvVarsInObject(parsedWorkspaceSettings);
- }
- } catch (_e) {
- // Ignore errors loading workspace settings
- }
- }
-
- if (envFilePath) {
- // Manually parse and load environment variables to handle exclusions correctly.
- // This avoids modifying environment variables that were already set from the shell.
- try {
- const envFileContent = fs.readFileSync(envFilePath, 'utf-8');
- const parsedEnv = dotenv.parse(envFileContent);
-
- const excludedVars =
- resolvedSettings?.excludedProjectEnvVars || DEFAULT_EXCLUDED_ENV_VARS;
- const isProjectEnvFile = !envFilePath.includes(ANUS_DIR);
-
- for (const key in parsedEnv) {
- if (Object.hasOwn(parsedEnv, key)) {
- // If it's a project .env file, skip loading excluded variables.
- if (isProjectEnvFile && excludedVars.includes(key)) {
- continue;
- }
-
- // Load variable only if it's not already set in the environment.
- if (!Object.hasOwn(process.env, key)) {
- process.env[key] = parsedEnv[key];
- }
- }
- }
- } catch (_e) {
- // Errors are ignored to match the behavior of `dotenv.config({ quiet: true })`.
- }
- }
-}
-
-/**
- * Loads settings from user and workspace directories.
- * Project settings override user settings.
- */
-export function loadSettings(workspaceDir: string): LoadedSettings {
- let systemSettings: Settings = {};
- let userSettings: Settings = {};
- let workspaceSettings: Settings = {};
- const settingsErrors: SettingsError[] = [];
- const systemSettingsPath = getSystemSettingsPath();
-
- // Resolve paths to their canonical representation to handle symlinks
- const resolvedWorkspaceDir = path.resolve(workspaceDir);
- const resolvedHomeDir = path.resolve(homedir());
-
- let realWorkspaceDir = resolvedWorkspaceDir;
- try {
- // fs.realpathSync gets the "true" path, resolving any symlinks
- realWorkspaceDir = fs.realpathSync(resolvedWorkspaceDir);
- } catch (_e) {
- // This is okay. The path might not exist yet, and that's a valid state.
- }
-
- // We expect homedir to always exist and be resolvable.
- const realHomeDir = fs.realpathSync(resolvedHomeDir);
-
- const workspaceSettingsPath = getWorkspaceSettingsPath(workspaceDir);
-
- // Load system settings
- try {
- if (fs.existsSync(systemSettingsPath)) {
- const systemContent = fs.readFileSync(systemSettingsPath, 'utf-8');
- const parsedSystemSettings = JSON.parse(
- stripJsonComments(systemContent),
- ) as Settings;
- systemSettings = resolveEnvVarsInObject(parsedSystemSettings);
- }
- } catch (error: unknown) {
- settingsErrors.push({
- message: getErrorMessage(error),
- path: systemSettingsPath,
- });
- }
-
- // Load user settings
- try {
- if (fs.existsSync(USER_SETTINGS_PATH)) {
- const userContent = fs.readFileSync(USER_SETTINGS_PATH, 'utf-8');
- const parsedUserSettings = JSON.parse(
- stripJsonComments(userContent),
- ) as Settings;
- userSettings = resolveEnvVarsInObject(parsedUserSettings);
- // Support legacy theme names
- if (userSettings.theme && userSettings.theme === 'VS') {
- userSettings.theme = DefaultLight.name;
- } else if (userSettings.theme && userSettings.theme === 'VS2015') {
- userSettings.theme = DefaultDark.name;
- }
- }
- } catch (error: unknown) {
- settingsErrors.push({
- message: getErrorMessage(error),
- path: USER_SETTINGS_PATH,
- });
- }
-
- if (realWorkspaceDir !== realHomeDir) {
- // Load workspace settings
- try {
- if (fs.existsSync(workspaceSettingsPath)) {
- const projectContent = fs.readFileSync(workspaceSettingsPath, 'utf-8');
- const parsedWorkspaceSettings = JSON.parse(
- stripJsonComments(projectContent),
- ) as Settings;
- workspaceSettings = resolveEnvVarsInObject(parsedWorkspaceSettings);
- if (workspaceSettings.theme && workspaceSettings.theme === 'VS') {
- workspaceSettings.theme = DefaultLight.name;
- } else if (
- workspaceSettings.theme &&
- workspaceSettings.theme === 'VS2015'
- ) {
- workspaceSettings.theme = DefaultDark.name;
- }
- }
- } catch (error: unknown) {
- settingsErrors.push({
- message: getErrorMessage(error),
- path: workspaceSettingsPath,
- });
- }
- }
-
- // Create LoadedSettings first
- const loadedSettings = new LoadedSettings(
- {
- path: systemSettingsPath,
- settings: systemSettings,
- },
- {
- path: USER_SETTINGS_PATH,
- settings: userSettings,
- },
- {
- path: workspaceSettingsPath,
- settings: workspaceSettings,
- },
- settingsErrors,
- );
-
- // Validate chatCompression settings
- const chatCompression = loadedSettings.merged.chatCompression;
- const threshold = chatCompression?.contextPercentageThreshold;
- if (
- threshold != null &&
- (typeof threshold !== 'number' || threshold < 0 || threshold > 1)
- ) {
- console.warn(
- `Invalid value for chatCompression.contextPercentageThreshold: "${threshold}". Please use a value between 0 and 1. Using default compression settings.`,
- );
- delete loadedSettings.merged.chatCompression;
- }
-
- // Load environment with merged settings
- loadEnvironment(loadedSettings.merged);
-
- return loadedSettings;
-}
-
-export function saveSettings(settingsFile: SettingsFile): void {
- try {
- // Ensure the directory exists
- const dirPath = path.dirname(settingsFile.path);
- if (!fs.existsSync(dirPath)) {
- fs.mkdirSync(dirPath, { recursive: true });
- }
-
- fs.writeFileSync(
- settingsFile.path,
- JSON.stringify(settingsFile.settings, null, 2),
- 'utf-8',
- );
- } catch (error) {
- console.error('Error saving user settings file:', error);
- }
-}
diff --git a/packages/cli/src/config/settingsSchema.test.ts b/packages/cli/src/config/settingsSchema.test.ts
deleted file mode 100644
index ec99fd3..0000000
--- a/packages/cli/src/config/settingsSchema.test.ts
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect } from 'vitest';
-import { SETTINGS_SCHEMA, Settings } from './settingsSchema.js';
-
-describe('SettingsSchema', () => {
- describe('SETTINGS_SCHEMA', () => {
- it('should contain all expected top-level settings', () => {
- const expectedSettings = [
- 'theme',
- 'customThemes',
- 'showMemoryUsage',
- 'usageStatisticsEnabled',
- 'autoConfigureMaxOldSpaceSize',
- 'preferredEditor',
- 'maxSessionTurns',
- 'memoryImportFormat',
- 'memoryDiscoveryMaxDirs',
- 'contextFileName',
- 'vimMode',
- 'ideMode',
- 'accessibility',
- 'checkpointing',
- 'fileFiltering',
- 'disableAutoUpdate',
- 'hideWindowTitle',
- 'hideTips',
- 'hideBanner',
- 'selectedAuthType',
- 'useExternalAuth',
- 'sandbox',
- 'coreTools',
- 'excludeTools',
- 'toolDiscoveryCommand',
- 'toolCallCommand',
- 'mcpServerCommand',
- 'mcpServers',
- 'allowMCPServers',
- 'excludeMCPServers',
- 'telemetry',
- 'bugCommand',
- 'summarizeToolOutput',
- 'dnsResolutionOrder',
- 'excludedProjectEnvVars',
- 'disableUpdateNag',
- 'includeDirectories',
- 'loadMemoryFromIncludeDirectories',
- 'model',
- 'hasSeenIdeIntegrationNudge',
- 'folderTrustFeature',
- ];
-
- expectedSettings.forEach((setting) => {
- expect(
- SETTINGS_SCHEMA[setting as keyof typeof SETTINGS_SCHEMA],
- ).toBeDefined();
- });
- });
-
- it('should have correct structure for each setting', () => {
- Object.entries(SETTINGS_SCHEMA).forEach(([_key, definition]) => {
- expect(definition).toHaveProperty('type');
- expect(definition).toHaveProperty('label');
- expect(definition).toHaveProperty('category');
- expect(definition).toHaveProperty('requiresRestart');
- expect(definition).toHaveProperty('default');
- expect(typeof definition.type).toBe('string');
- expect(typeof definition.label).toBe('string');
- expect(typeof definition.category).toBe('string');
- expect(typeof definition.requiresRestart).toBe('boolean');
- });
- });
-
- it('should have correct nested setting structure', () => {
- const nestedSettings = [
- 'accessibility',
- 'checkpointing',
- 'fileFiltering',
- ];
-
- nestedSettings.forEach((setting) => {
- const definition = SETTINGS_SCHEMA[
- setting as keyof typeof SETTINGS_SCHEMA
- ] as (typeof SETTINGS_SCHEMA)[keyof typeof SETTINGS_SCHEMA] & {
- properties: unknown;
- };
- expect(definition.type).toBe('object');
- expect(definition.properties).toBeDefined();
- expect(typeof definition.properties).toBe('object');
- });
- });
-
- it('should have accessibility nested properties', () => {
- expect(
- SETTINGS_SCHEMA.accessibility.properties?.disableLoadingPhrases,
- ).toBeDefined();
- expect(
- SETTINGS_SCHEMA.accessibility.properties?.disableLoadingPhrases.type,
- ).toBe('boolean');
- });
-
- it('should have checkpointing nested properties', () => {
- expect(SETTINGS_SCHEMA.checkpointing.properties?.enabled).toBeDefined();
- expect(SETTINGS_SCHEMA.checkpointing.properties?.enabled.type).toBe(
- 'boolean',
- );
- });
-
- it('should have fileFiltering nested properties', () => {
- expect(
- SETTINGS_SCHEMA.fileFiltering.properties?.respectGitIgnore,
- ).toBeDefined();
- expect(
- SETTINGS_SCHEMA.fileFiltering.properties?.respectAnusIgnore,
- ).toBeDefined();
- expect(
- SETTINGS_SCHEMA.fileFiltering.properties?.enableRecursiveFileSearch,
- ).toBeDefined();
- });
-
- it('should have unique categories', () => {
- const categories = new Set();
-
- // Collect categories from top-level settings
- Object.values(SETTINGS_SCHEMA).forEach((definition) => {
- categories.add(definition.category);
- // Also collect from nested properties
- const defWithProps = definition as typeof definition & {
- properties?: Record;
- };
- if (defWithProps.properties) {
- Object.values(defWithProps.properties).forEach(
- (nestedDef: unknown) => {
- const nestedDefTyped = nestedDef as { category?: string };
- if (nestedDefTyped.category) {
- categories.add(nestedDefTyped.category);
- }
- },
- );
- }
- });
-
- expect(categories.size).toBeGreaterThan(0);
- expect(categories).toContain('General');
- expect(categories).toContain('UI');
- expect(categories).toContain('Mode');
- expect(categories).toContain('Updates');
- expect(categories).toContain('Accessibility');
- expect(categories).toContain('Checkpointing');
- expect(categories).toContain('File Filtering');
- expect(categories).toContain('Advanced');
- });
-
- it('should have consistent default values for boolean settings', () => {
- const checkBooleanDefaults = (schema: Record) => {
- Object.entries(schema).forEach(
- ([_key, definition]: [string, unknown]) => {
- const def = definition as {
- type?: string;
- default?: unknown;
- properties?: Record;
- };
- if (def.type === 'boolean') {
- // Boolean settings can have boolean or undefined defaults (for optional settings)
- expect(['boolean', 'undefined']).toContain(typeof def.default);
- }
- if (def.properties) {
- checkBooleanDefaults(def.properties);
- }
- },
- );
- };
-
- checkBooleanDefaults(SETTINGS_SCHEMA as Record);
- });
-
- it('should have showInDialog property configured', () => {
- // Check that user-facing settings are marked for dialog display
- expect(SETTINGS_SCHEMA.showMemoryUsage.showInDialog).toBe(true);
- expect(SETTINGS_SCHEMA.vimMode.showInDialog).toBe(true);
- expect(SETTINGS_SCHEMA.ideMode.showInDialog).toBe(true);
- expect(SETTINGS_SCHEMA.disableAutoUpdate.showInDialog).toBe(true);
- expect(SETTINGS_SCHEMA.hideWindowTitle.showInDialog).toBe(true);
- expect(SETTINGS_SCHEMA.hideTips.showInDialog).toBe(true);
- expect(SETTINGS_SCHEMA.hideBanner.showInDialog).toBe(true);
- expect(SETTINGS_SCHEMA.usageStatisticsEnabled.showInDialog).toBe(true);
-
- // Check that advanced settings are hidden from dialog
- expect(SETTINGS_SCHEMA.selectedAuthType.showInDialog).toBe(false);
- expect(SETTINGS_SCHEMA.coreTools.showInDialog).toBe(false);
- expect(SETTINGS_SCHEMA.mcpServers.showInDialog).toBe(false);
- expect(SETTINGS_SCHEMA.telemetry.showInDialog).toBe(false);
-
- // Check that some settings are appropriately hidden
- expect(SETTINGS_SCHEMA.theme.showInDialog).toBe(false); // Changed to false
- expect(SETTINGS_SCHEMA.customThemes.showInDialog).toBe(false); // Managed via theme editor
- expect(SETTINGS_SCHEMA.checkpointing.showInDialog).toBe(false); // Experimental feature
- expect(SETTINGS_SCHEMA.accessibility.showInDialog).toBe(false); // Changed to false
- expect(SETTINGS_SCHEMA.fileFiltering.showInDialog).toBe(false); // Changed to false
- expect(SETTINGS_SCHEMA.preferredEditor.showInDialog).toBe(false); // Changed to false
- expect(SETTINGS_SCHEMA.autoConfigureMaxOldSpaceSize.showInDialog).toBe(
- true,
- );
- });
-
- it('should infer Settings type correctly', () => {
- // This test ensures that the Settings type is properly inferred from the schema
- const settings: Settings = {
- theme: 'dark',
- includeDirectories: ['/path/to/dir'],
- loadMemoryFromIncludeDirectories: true,
- };
-
- // TypeScript should not complain about these properties
- expect(settings.theme).toBe('dark');
- expect(settings.includeDirectories).toEqual(['/path/to/dir']);
- expect(settings.loadMemoryFromIncludeDirectories).toBe(true);
- });
-
- it('should have includeDirectories setting in schema', () => {
- expect(SETTINGS_SCHEMA.includeDirectories).toBeDefined();
- expect(SETTINGS_SCHEMA.includeDirectories.type).toBe('array');
- expect(SETTINGS_SCHEMA.includeDirectories.category).toBe('General');
- expect(SETTINGS_SCHEMA.includeDirectories.default).toEqual([]);
- });
-
- it('should have loadMemoryFromIncludeDirectories setting in schema', () => {
- expect(SETTINGS_SCHEMA.loadMemoryFromIncludeDirectories).toBeDefined();
- expect(SETTINGS_SCHEMA.loadMemoryFromIncludeDirectories.type).toBe(
- 'boolean',
- );
- expect(SETTINGS_SCHEMA.loadMemoryFromIncludeDirectories.category).toBe(
- 'General',
- );
- expect(SETTINGS_SCHEMA.loadMemoryFromIncludeDirectories.default).toBe(
- false,
- );
- });
-
- it('should have folderTrustFeature setting in schema', () => {
- expect(SETTINGS_SCHEMA.folderTrustFeature).toBeDefined();
- expect(SETTINGS_SCHEMA.folderTrustFeature.type).toBe('boolean');
- expect(SETTINGS_SCHEMA.folderTrustFeature.category).toBe('General');
- expect(SETTINGS_SCHEMA.folderTrustFeature.default).toBe(false);
- expect(SETTINGS_SCHEMA.folderTrustFeature.showInDialog).toBe(true);
- });
- });
-});
diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts
deleted file mode 100644
index 7187d73..0000000
--- a/packages/cli/src/config/settingsSchema.ts
+++ /dev/null
@@ -1,551 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import {
- MCPServerConfig,
- BugCommandSettings,
- TelemetrySettings,
- AuthType,
- ChatCompressionSettings,
-} from '@anus-dev/anus-core';
-import { CustomTheme } from '../ui/themes/theme.js';
-
-export interface SettingDefinition {
- type: 'boolean' | 'string' | 'number' | 'array' | 'object';
- label: string;
- category: string;
- requiresRestart: boolean;
- default: boolean | string | number | string[] | object | undefined;
- description?: string;
- parentKey?: string;
- childKey?: string;
- key?: string;
- properties?: SettingsSchema;
- showInDialog?: boolean;
-}
-
-export interface SettingsSchema {
- [key: string]: SettingDefinition;
-}
-
-export type MemoryImportFormat = 'tree' | 'flat';
-export type DnsResolutionOrder = 'ipv4first' | 'verbatim';
-
-/**
- * The canonical schema for all settings.
- * The structure of this object defines the structure of the `Settings` type.
- * `as const` is crucial for TypeScript to infer the most specific types possible.
- */
-export const SETTINGS_SCHEMA = {
- // UI Settings
- theme: {
- type: 'string',
- label: 'Theme',
- category: 'UI',
- requiresRestart: false,
- default: undefined as string | undefined,
- description: 'The color theme for the UI.',
- showInDialog: false,
- },
- customThemes: {
- type: 'object',
- label: 'Custom Themes',
- category: 'UI',
- requiresRestart: false,
- default: {} as Record,
- description: 'Custom theme definitions.',
- showInDialog: false,
- },
- hideWindowTitle: {
- type: 'boolean',
- label: 'Hide Window Title',
- category: 'UI',
- requiresRestart: true,
- default: false,
- description: 'Hide the window title bar',
- showInDialog: true,
- },
- hideTips: {
- type: 'boolean',
- label: 'Hide Tips',
- category: 'UI',
- requiresRestart: false,
- default: false,
- description: 'Hide helpful tips in the UI',
- showInDialog: true,
- },
- hideBanner: {
- type: 'boolean',
- label: 'Hide Banner',
- category: 'UI',
- requiresRestart: false,
- default: false,
- description: 'Hide the application banner',
- showInDialog: true,
- },
- showMemoryUsage: {
- type: 'boolean',
- label: 'Show Memory Usage',
- category: 'UI',
- requiresRestart: false,
- default: false,
- description: 'Display memory usage information in the UI',
- showInDialog: true,
- },
-
- usageStatisticsEnabled: {
- type: 'boolean',
- label: 'Enable Usage Statistics',
- category: 'General',
- requiresRestart: true,
- default: true,
- description: 'Enable collection of usage statistics',
- showInDialog: true,
- },
- autoConfigureMaxOldSpaceSize: {
- type: 'boolean',
- label: 'Auto Configure Max Old Space Size',
- category: 'General',
- requiresRestart: true,
- default: false,
- description: 'Automatically configure Node.js memory limits',
- showInDialog: true,
- },
- preferredEditor: {
- type: 'string',
- label: 'Preferred Editor',
- category: 'General',
- requiresRestart: false,
- default: undefined as string | undefined,
- description: 'The preferred editor to open files in.',
- showInDialog: false,
- },
- maxSessionTurns: {
- type: 'number',
- label: 'Max Session Turns',
- category: 'General',
- requiresRestart: false,
- default: undefined as number | undefined,
- description:
- 'Maximum number of user/model/tool turns to keep in a session.',
- showInDialog: false,
- },
- memoryImportFormat: {
- type: 'string',
- label: 'Memory Import Format',
- category: 'General',
- requiresRestart: false,
- default: undefined as MemoryImportFormat | undefined,
- description: 'The format to use when importing memory.',
- showInDialog: false,
- },
- memoryDiscoveryMaxDirs: {
- type: 'number',
- label: 'Memory Discovery Max Dirs',
- category: 'General',
- requiresRestart: false,
- default: undefined as number | undefined,
- description: 'Maximum number of directories to search for memory.',
- showInDialog: false,
- },
- contextFileName: {
- type: 'object',
- label: 'Context File Name',
- category: 'General',
- requiresRestart: false,
- default: undefined as string | string[] | undefined,
- description: 'The name of the context file.',
- showInDialog: false,
- },
- vimMode: {
- type: 'boolean',
- label: 'Vim Mode',
- category: 'Mode',
- requiresRestart: false,
- default: false,
- description: 'Enable Vim keybindings',
- showInDialog: true,
- },
- ideMode: {
- type: 'boolean',
- label: 'IDE Mode',
- category: 'Mode',
- requiresRestart: true,
- default: false,
- description: 'Enable IDE integration mode',
- showInDialog: true,
- },
-
- accessibility: {
- type: 'object',
- label: 'Accessibility',
- category: 'Accessibility',
- requiresRestart: true,
- default: {},
- description: 'Accessibility settings.',
- showInDialog: false,
- properties: {
- disableLoadingPhrases: {
- type: 'boolean',
- label: 'Disable Loading Phrases',
- category: 'Accessibility',
- requiresRestart: true,
- default: false,
- description: 'Disable loading phrases for accessibility',
- showInDialog: true,
- },
- },
- },
- checkpointing: {
- type: 'object',
- label: 'Checkpointing',
- category: 'Checkpointing',
- requiresRestart: true,
- default: {},
- description: 'Session checkpointing settings.',
- showInDialog: false,
- properties: {
- enabled: {
- type: 'boolean',
- label: 'Enable Checkpointing',
- category: 'Checkpointing',
- requiresRestart: true,
- default: false,
- description: 'Enable session checkpointing for recovery',
- showInDialog: false,
- },
- },
- },
- fileFiltering: {
- type: 'object',
- label: 'File Filtering',
- category: 'File Filtering',
- requiresRestart: true,
- default: {},
- description: 'Settings for git-aware file filtering.',
- showInDialog: false,
- properties: {
- respectGitIgnore: {
- type: 'boolean',
- label: 'Respect .gitignore',
- category: 'File Filtering',
- requiresRestart: true,
- default: true,
- description: 'Respect .gitignore files when searching',
- showInDialog: true,
- },
- respectAnusIgnore: {
- type: 'boolean',
- label: 'Respect .anusignore',
- category: 'File Filtering',
- requiresRestart: true,
- default: true,
- description: 'Respect .anusignore files when searching',
- showInDialog: true,
- },
- enableRecursiveFileSearch: {
- type: 'boolean',
- label: 'Enable Recursive File Search',
- category: 'File Filtering',
- requiresRestart: true,
- default: true,
- description: 'Enable recursive file search functionality',
- showInDialog: true,
- },
- },
- },
-
- disableAutoUpdate: {
- type: 'boolean',
- label: 'Disable Auto Update',
- category: 'Updates',
- requiresRestart: false,
- default: false,
- description: 'Disable automatic updates',
- showInDialog: true,
- },
-
- selectedAuthType: {
- type: 'string',
- label: 'Selected Auth Type',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as AuthType | undefined,
- description: 'The currently selected authentication type.',
- showInDialog: false,
- },
- useExternalAuth: {
- type: 'boolean',
- label: 'Use External Auth',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as boolean | undefined,
- description: 'Whether to use an external authentication flow.',
- showInDialog: false,
- },
- sandbox: {
- type: 'object',
- label: 'Sandbox',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as boolean | string | undefined,
- description:
- 'Sandbox execution environment (can be a boolean or a path string).',
- showInDialog: false,
- },
- coreTools: {
- type: 'array',
- label: 'Core Tools',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as string[] | undefined,
- description: 'Paths to core tool definitions.',
- showInDialog: false,
- },
- excludeTools: {
- type: 'array',
- label: 'Exclude Tools',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as string[] | undefined,
- description: 'Tool names to exclude from discovery.',
- showInDialog: false,
- },
- toolDiscoveryCommand: {
- type: 'string',
- label: 'Tool Discovery Command',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as string | undefined,
- description: 'Command to run for tool discovery.',
- showInDialog: false,
- },
- toolCallCommand: {
- type: 'string',
- label: 'Tool Call Command',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as string | undefined,
- description: 'Command to run for tool calls.',
- showInDialog: false,
- },
- mcpServerCommand: {
- type: 'string',
- label: 'MCP Server Command',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as string | undefined,
- description: 'Command to start an MCP server.',
- showInDialog: false,
- },
- mcpServers: {
- type: 'object',
- label: 'MCP Servers',
- category: 'Advanced',
- requiresRestart: true,
- default: {} as Record,
- description: 'Configuration for MCP servers.',
- showInDialog: false,
- },
- allowMCPServers: {
- type: 'array',
- label: 'Allow MCP Servers',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as string[] | undefined,
- description: 'A whitelist of MCP servers to allow.',
- showInDialog: false,
- },
- excludeMCPServers: {
- type: 'array',
- label: 'Exclude MCP Servers',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as string[] | undefined,
- description: 'A blacklist of MCP servers to exclude.',
- showInDialog: false,
- },
- telemetry: {
- type: 'object',
- label: 'Telemetry',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as TelemetrySettings | undefined,
- description: 'Telemetry configuration.',
- showInDialog: false,
- },
- bugCommand: {
- type: 'object',
- label: 'Bug Command',
- category: 'Advanced',
- requiresRestart: false,
- default: undefined as BugCommandSettings | undefined,
- description: 'Configuration for the bug report command.',
- showInDialog: false,
- },
- summarizeToolOutput: {
- type: 'object',
- label: 'Summarize Tool Output',
- category: 'Advanced',
- requiresRestart: false,
- default: undefined as Record | undefined,
- description: 'Settings for summarizing tool output.',
- showInDialog: false,
- },
-
- dnsResolutionOrder: {
- type: 'string',
- label: 'DNS Resolution Order',
- category: 'Advanced',
- requiresRestart: true,
- default: undefined as DnsResolutionOrder | undefined,
- description: 'The DNS resolution order.',
- showInDialog: false,
- },
- excludedProjectEnvVars: {
- type: 'array',
- label: 'Excluded Project Environment Variables',
- category: 'Advanced',
- requiresRestart: false,
- default: ['DEBUG', 'DEBUG_MODE'] as string[],
- description: 'Environment variables to exclude from project context.',
- showInDialog: false,
- },
- disableUpdateNag: {
- type: 'boolean',
- label: 'Disable Update Nag',
- category: 'Updates',
- requiresRestart: false,
- default: false,
- description: 'Disable update notification prompts.',
- showInDialog: false,
- },
- includeDirectories: {
- type: 'array',
- label: 'Include Directories',
- category: 'General',
- requiresRestart: false,
- default: [] as string[],
- description: 'Additional directories to include in the workspace context.',
- showInDialog: false,
- },
- loadMemoryFromIncludeDirectories: {
- type: 'boolean',
- label: 'Load Memory From Include Directories',
- category: 'General',
- requiresRestart: false,
- default: false,
- description: 'Whether to load memory files from include directories.',
- showInDialog: true,
- },
- model: {
- type: 'string',
- label: 'Model',
- category: 'General',
- requiresRestart: false,
- default: undefined as string | undefined,
- description:
- 'The model to use for conversations (defaults can be set via ANUS_MODEL, GROK_MODEL, or XAI_MODEL).',
- showInDialog: false,
- },
- provider: {
- type: 'string',
- label: 'Provider',
- category: 'General',
- requiresRestart: true,
- default: undefined as string | undefined,
- description:
- 'LLM provider identifier (e.g., grok). Optional; usually inferred from environment.',
- showInDialog: false,
- },
- grok: {
- type: 'object',
- label: 'Grok Settings',
- category: 'General',
- requiresRestart: false,
- default: {} as { apiKey?: string; model?: string },
- description: 'Settings for the Grok provider.',
- showInDialog: false,
- properties: {
- apiKey: {
- type: 'string',
- label: 'Grok API Key',
- category: 'General',
- requiresRestart: false,
- default: undefined as string | undefined,
- description:
- 'API key for Grok (can also be set via GROK_API_KEY or XAI_API_KEY).',
- showInDialog: false,
- },
- model: {
- type: 'string',
- label: 'Grok Model',
- category: 'General',
- requiresRestart: false,
- default: undefined as string | undefined,
- description:
- 'Default Grok model (can also be set via GROK_MODEL or XAI_MODEL).',
- showInDialog: false,
- },
- },
- },
- hasSeenIdeIntegrationNudge: {
- type: 'boolean',
- label: 'Has Seen IDE Integration Nudge',
- category: 'General',
- requiresRestart: false,
- default: false,
- description: 'Whether the user has seen the IDE integration nudge.',
- showInDialog: false,
- },
- folderTrustFeature: {
- type: 'boolean',
- label: 'Folder Trust Feature',
- category: 'General',
- requiresRestart: false,
- default: false,
- description: 'Enable folder trust feature for enhanced security.',
- showInDialog: true,
- },
- folderTrust: {
- type: 'boolean',
- label: 'Folder Trust',
- category: 'General',
- requiresRestart: false,
- default: false,
- description: 'Setting to track whether Folder trust is enabled.',
- showInDialog: true,
- },
- chatCompression: {
- type: 'object',
- label: 'Chat Compression',
- category: 'General',
- requiresRestart: false,
- default: undefined as ChatCompressionSettings | undefined,
- description: 'Chat compression settings.',
- showInDialog: false,
- },
- showLineNumbers: {
- type: 'boolean',
- label: 'Show Line Numbers',
- category: 'General',
- requiresRestart: false,
- default: false,
- description: 'Show line numbers in the chat.',
- showInDialog: true,
- },
-} as const;
-
-type InferSettings = {
- -readonly [K in keyof T]?: T[K] extends { properties: SettingsSchema }
- ? InferSettings
- : T[K]['default'] extends boolean
- ? boolean
- : T[K]['default'];
-};
-
-export type Settings = InferSettings;
diff --git a/packages/cli/src/config/trustedFolders.test.ts b/packages/cli/src/config/trustedFolders.test.ts
deleted file mode 100644
index 614aec0..0000000
--- a/packages/cli/src/config/trustedFolders.test.ts
+++ /dev/null
@@ -1,209 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-// Mock 'os' first.
-import * as osActual from 'os';
-vi.mock('os', async (importOriginal) => {
- const actualOs = await importOriginal();
- return {
- ...actualOs,
- homedir: vi.fn(() => '/mock/home/user'),
- platform: vi.fn(() => 'linux'),
- };
-});
-
-import {
- describe,
- it,
- expect,
- vi,
- beforeEach,
- afterEach,
- type Mocked,
- type Mock,
-} from 'vitest';
-import * as fs from 'fs';
-import stripJsonComments from 'strip-json-comments';
-import * as path from 'path';
-
-import {
- loadTrustedFolders,
- USER_TRUSTED_FOLDERS_PATH,
- TrustLevel,
- isWorkspaceTrusted,
-} from './trustedFolders.js';
-import { Settings } from './settings.js';
-
-vi.mock('fs', async (importOriginal) => {
- const actualFs = await importOriginal();
- return {
- ...actualFs,
- existsSync: vi.fn(),
- readFileSync: vi.fn(),
- writeFileSync: vi.fn(),
- mkdirSync: vi.fn(),
- };
-});
-
-vi.mock('strip-json-comments', () => ({
- default: vi.fn((content) => content),
-}));
-
-describe('Trusted Folders Loading', () => {
- let mockFsExistsSync: Mocked;
- let mockStripJsonComments: Mocked;
- let mockFsWriteFileSync: Mocked;
-
- beforeEach(() => {
- vi.resetAllMocks();
- mockFsExistsSync = vi.mocked(fs.existsSync);
- mockStripJsonComments = vi.mocked(stripJsonComments);
- mockFsWriteFileSync = vi.mocked(fs.writeFileSync);
- vi.mocked(osActual.homedir).mockReturnValue('/mock/home/user');
- (mockStripJsonComments as unknown as Mock).mockImplementation(
- (jsonString: string) => jsonString,
- );
- (mockFsExistsSync as Mock).mockReturnValue(false);
- (fs.readFileSync as Mock).mockReturnValue('{}');
- });
-
- afterEach(() => {
- vi.restoreAllMocks();
- });
-
- it('should load empty rules if no files exist', () => {
- const { rules, errors } = loadTrustedFolders();
- expect(rules).toEqual([]);
- expect(errors).toEqual([]);
- });
-
- it('should load user rules if only user file exists', () => {
- const userPath = USER_TRUSTED_FOLDERS_PATH;
- (mockFsExistsSync as Mock).mockImplementation((p) => p === userPath);
- const userContent = {
- '/user/folder': TrustLevel.TRUST_FOLDER,
- };
- (fs.readFileSync as Mock).mockImplementation((p) => {
- if (p === userPath) return JSON.stringify(userContent);
- return '{}';
- });
-
- const { rules, errors } = loadTrustedFolders();
- expect(rules).toEqual([
- { path: '/user/folder', trustLevel: TrustLevel.TRUST_FOLDER },
- ]);
- expect(errors).toEqual([]);
- });
-
- it('should handle JSON parsing errors gracefully', () => {
- const userPath = USER_TRUSTED_FOLDERS_PATH;
- (mockFsExistsSync as Mock).mockImplementation((p) => p === userPath);
- (fs.readFileSync as Mock).mockImplementation((p) => {
- if (p === userPath) return 'invalid json';
- return '{}';
- });
-
- const { rules, errors } = loadTrustedFolders();
- expect(rules).toEqual([]);
- expect(errors.length).toBe(1);
- expect(errors[0].path).toBe(userPath);
- expect(errors[0].message).toContain('Unexpected token');
- });
-
- it('setValue should update the user config and save it', () => {
- const loadedFolders = loadTrustedFolders();
- loadedFolders.setValue('/new/path', TrustLevel.TRUST_FOLDER);
-
- expect(loadedFolders.user.config['/new/path']).toBe(
- TrustLevel.TRUST_FOLDER,
- );
- expect(mockFsWriteFileSync).toHaveBeenCalledWith(
- USER_TRUSTED_FOLDERS_PATH,
- JSON.stringify({ '/new/path': TrustLevel.TRUST_FOLDER }, null, 2),
- 'utf-8',
- );
- });
-});
-
-describe('isWorkspaceTrusted', () => {
- let mockCwd: string;
- const mockRules: Record = {};
- const mockSettings: Settings = {
- folderTrustFeature: true,
- folderTrust: true,
- };
-
- beforeEach(() => {
- vi.spyOn(process, 'cwd').mockImplementation(() => mockCwd);
- vi.spyOn(fs, 'readFileSync').mockImplementation((p) => {
- if (p === USER_TRUSTED_FOLDERS_PATH) {
- return JSON.stringify(mockRules);
- }
- return '{}';
- });
- vi.spyOn(fs, 'existsSync').mockImplementation(
- (p) => p === USER_TRUSTED_FOLDERS_PATH,
- );
- });
-
- afterEach(() => {
- vi.restoreAllMocks();
- // Clear the object
- Object.keys(mockRules).forEach((key) => delete mockRules[key]);
- });
-
- it('should return true for a directly trusted folder', () => {
- mockCwd = '/home/user/projectA';
- mockRules['/home/user/projectA'] = TrustLevel.TRUST_FOLDER;
- expect(isWorkspaceTrusted(mockSettings)).toBe(true);
- });
-
- it('should return true for a child of a trusted folder', () => {
- mockCwd = '/home/user/projectA/src';
- mockRules['/home/user/projectA'] = TrustLevel.TRUST_FOLDER;
- expect(isWorkspaceTrusted(mockSettings)).toBe(true);
- });
-
- it('should return true for a child of a trusted parent folder', () => {
- mockCwd = '/home/user/projectB';
- mockRules['/home/user/projectB/somefile.txt'] = TrustLevel.TRUST_PARENT;
- expect(isWorkspaceTrusted(mockSettings)).toBe(true);
- });
-
- it('should return false for a directly untrusted folder', () => {
- mockCwd = '/home/user/untrusted';
- mockRules['/home/user/untrusted'] = TrustLevel.DO_NOT_TRUST;
- expect(isWorkspaceTrusted(mockSettings)).toBe(false);
- });
-
- it('should return undefined for a child of an untrusted folder', () => {
- mockCwd = '/home/user/untrusted/src';
- mockRules['/home/user/untrusted'] = TrustLevel.DO_NOT_TRUST;
- expect(isWorkspaceTrusted(mockSettings)).toBeUndefined();
- });
-
- it('should return undefined when no rules match', () => {
- mockCwd = '/home/user/other';
- mockRules['/home/user/projectA'] = TrustLevel.TRUST_FOLDER;
- mockRules['/home/user/untrusted'] = TrustLevel.DO_NOT_TRUST;
- expect(isWorkspaceTrusted(mockSettings)).toBeUndefined();
- });
-
- it('should prioritize trust over distrust', () => {
- mockCwd = '/home/user/projectA/untrusted';
- mockRules['/home/user/projectA'] = TrustLevel.TRUST_FOLDER;
- mockRules['/home/user/projectA/untrusted'] = TrustLevel.DO_NOT_TRUST;
- expect(isWorkspaceTrusted(mockSettings)).toBe(true);
- });
-
- it('should handle path normalization', () => {
- mockCwd = '/home/user/projectA';
- mockRules[`/home/user/../user/${path.basename('/home/user/projectA')}`] =
- TrustLevel.TRUST_FOLDER;
- expect(isWorkspaceTrusted(mockSettings)).toBe(true);
- });
-});
diff --git a/packages/cli/src/config/trustedFolders.ts b/packages/cli/src/config/trustedFolders.ts
deleted file mode 100644
index bbd7aa7..0000000
--- a/packages/cli/src/config/trustedFolders.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import * as fs from 'fs';
-import * as path from 'path';
-import { homedir } from 'os';
-import { getErrorMessage, isWithinRoot } from '@anus-dev/anus-core';
-import { Settings } from './settings.js';
-import stripJsonComments from 'strip-json-comments';
-
-export const TRUSTED_FOLDERS_FILENAME = 'trustedFolders.json';
-export const SETTINGS_DIRECTORY_NAME = '.anus';
-export const USER_SETTINGS_DIR = path.join(homedir(), SETTINGS_DIRECTORY_NAME);
-export const USER_TRUSTED_FOLDERS_PATH = path.join(
- USER_SETTINGS_DIR,
- TRUSTED_FOLDERS_FILENAME,
-);
-
-export enum TrustLevel {
- TRUST_FOLDER = 'TRUST_FOLDER',
- TRUST_PARENT = 'TRUST_PARENT',
- DO_NOT_TRUST = 'DO_NOT_TRUST',
-}
-
-export interface TrustRule {
- path: string;
- trustLevel: TrustLevel;
-}
-
-export interface TrustedFoldersError {
- message: string;
- path: string;
-}
-
-export interface TrustedFoldersFile {
- config: Record;
- path: string;
-}
-
-export class LoadedTrustedFolders {
- constructor(
- public user: TrustedFoldersFile,
- public errors: TrustedFoldersError[],
- ) {}
-
- get rules(): TrustRule[] {
- return Object.entries(this.user.config).map(([path, trustLevel]) => ({
- path,
- trustLevel,
- }));
- }
-
- setValue(path: string, trustLevel: TrustLevel): void {
- this.user.config[path] = trustLevel;
- saveTrustedFolders(this.user);
- }
-}
-
-export function loadTrustedFolders(): LoadedTrustedFolders {
- const errors: TrustedFoldersError[] = [];
- const userConfig: Record = {};
-
- const userPath = USER_TRUSTED_FOLDERS_PATH;
-
- // Load user trusted folders
- try {
- if (fs.existsSync(userPath)) {
- const content = fs.readFileSync(userPath, 'utf-8');
- const parsed = JSON.parse(stripJsonComments(content)) as Record<
- string,
- TrustLevel
- >;
- if (parsed) {
- Object.assign(userConfig, parsed);
- }
- }
- } catch (error: unknown) {
- errors.push({
- message: getErrorMessage(error),
- path: userPath,
- });
- }
-
- return new LoadedTrustedFolders(
- { path: userPath, config: userConfig },
- errors,
- );
-}
-
-export function saveTrustedFolders(
- trustedFoldersFile: TrustedFoldersFile,
-): void {
- try {
- // Ensure the directory exists
- const dirPath = path.dirname(trustedFoldersFile.path);
- if (!fs.existsSync(dirPath)) {
- fs.mkdirSync(dirPath, { recursive: true });
- }
-
- fs.writeFileSync(
- trustedFoldersFile.path,
- JSON.stringify(trustedFoldersFile.config, null, 2),
- 'utf-8',
- );
- } catch (error) {
- console.error('Error saving trusted folders file:', error);
- }
-}
-
-export function isWorkspaceTrusted(settings: Settings): boolean | undefined {
- const folderTrustFeature = settings.folderTrustFeature ?? false;
- const folderTrustSetting = settings.folderTrust ?? true;
- const folderTrustEnabled = folderTrustFeature && folderTrustSetting;
-
- if (!folderTrustEnabled) {
- return true;
- }
-
- const { rules, errors } = loadTrustedFolders();
-
- if (errors.length > 0) {
- for (const error of errors) {
- console.error(
- `Error loading trusted folders config from ${error.path}: ${error.message}`,
- );
- }
- }
-
- const trustedPaths: string[] = [];
- const untrustedPaths: string[] = [];
-
- for (const rule of rules) {
- switch (rule.trustLevel) {
- case TrustLevel.TRUST_FOLDER:
- trustedPaths.push(rule.path);
- break;
- case TrustLevel.TRUST_PARENT:
- trustedPaths.push(path.dirname(rule.path));
- break;
- case TrustLevel.DO_NOT_TRUST:
- untrustedPaths.push(rule.path);
- break;
- default:
- // Do nothing for unknown trust levels.
- break;
- }
- }
-
- const cwd = process.cwd();
-
- for (const trustedPath of trustedPaths) {
- if (isWithinRoot(cwd, trustedPath)) {
- return true;
- }
- }
-
- for (const untrustedPath of untrustedPaths) {
- if (path.normalize(cwd) === path.normalize(untrustedPath)) {
- return false;
- }
- }
-
- return undefined;
-}
diff --git a/packages/cli/src/nonInteractiveCli.test.ts b/packages/cli/src/nonInteractiveCli.test.ts
deleted file mode 100644
index 4f5335d..0000000
--- a/packages/cli/src/nonInteractiveCli.test.ts
+++ /dev/null
@@ -1,277 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import {
- Config,
- executeToolCall,
- ToolRegistry,
- ToolErrorType,
- shutdownTelemetry,
- AnusEventType,
- ServerAnusStreamEvent,
-} from '@anus-dev/anus-core';
-import { Part } from '@google/genai';
-import { runNonInteractive } from './nonInteractiveCli.js';
-import { vi } from 'vitest';
-
-// Mock core modules
-vi.mock('@anus-dev/anus-core', async (importOriginal) => {
- const original = await importOriginal();
- return {
- ...original,
- executeToolCall: vi.fn(),
- shutdownTelemetry: vi.fn(),
- isTelemetrySdkInitialized: vi.fn().mockReturnValue(true),
- };
-});
-
-describe('runNonInteractive', () => {
- let mockConfig: Config;
- let mockToolRegistry: ToolRegistry;
- let mockCoreExecuteToolCall: vi.Mock;
- let mockShutdownTelemetry: vi.Mock;
- let consoleErrorSpy: vi.SpyInstance;
- let processExitSpy: vi.SpyInstance;
- let processStdoutSpy: vi.SpyInstance;
- let mockAnusClient: {
- sendMessageStream: vi.Mock;
- };
-
- beforeEach(() => {
- mockCoreExecuteToolCall = vi.mocked(executeToolCall);
- mockShutdownTelemetry = vi.mocked(shutdownTelemetry);
-
- consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
- processExitSpy = vi
- .spyOn(process, 'exit')
- .mockImplementation((() => {}) as (code?: number) => never);
- processStdoutSpy = vi
- .spyOn(process.stdout, 'write')
- .mockImplementation(() => true);
-
- mockToolRegistry = {
- getTool: vi.fn(),
- getFunctionDeclarations: vi.fn().mockReturnValue([]),
- } as unknown as ToolRegistry;
-
- mockAnusClient = {
- sendMessageStream: vi.fn(),
- };
-
- mockConfig = {
- initialize: vi.fn().mockResolvedValue(undefined),
- getAnusClient: vi.fn().mockReturnValue(mockAnusClient),
- getToolRegistry: vi.fn().mockResolvedValue(mockToolRegistry),
- getMaxSessionTurns: vi.fn().mockReturnValue(10),
- getIdeMode: vi.fn().mockReturnValue(false),
- getFullContext: vi.fn().mockReturnValue(false),
- getContentGeneratorConfig: vi.fn().mockReturnValue({}),
- getDebugMode: vi.fn().mockReturnValue(false),
- } as unknown as Config;
- });
-
- afterEach(() => {
- vi.restoreAllMocks();
- });
-
- async function* createStreamFromEvents(
- events: ServerAnusStreamEvent[],
- ): AsyncGenerator {
- for (const event of events) {
- yield event;
- }
- }
-
- it('should process input and write text output', async () => {
- const events: ServerAnusStreamEvent[] = [
- { type: AnusEventType.Content, value: 'Hello' },
- { type: AnusEventType.Content, value: ' World' },
- ];
- mockAnusClient.sendMessageStream.mockReturnValue(
- createStreamFromEvents(events),
- );
-
- await runNonInteractive(mockConfig, 'Test input', 'prompt-id-1');
-
- expect(mockAnusClient.sendMessageStream).toHaveBeenCalledWith(
- [{ text: 'Test input' }],
- expect.any(AbortSignal),
- 'prompt-id-1',
- );
- expect(processStdoutSpy).toHaveBeenCalledWith('Hello');
- expect(processStdoutSpy).toHaveBeenCalledWith(' World');
- expect(processStdoutSpy).toHaveBeenCalledWith('\n');
- expect(mockShutdownTelemetry).toHaveBeenCalled();
- });
-
- it('should handle a single tool call and respond', async () => {
- const toolCallEvent: ServerAnusStreamEvent = {
- type: AnusEventType.ToolCallRequest,
- value: {
- callId: 'tool-1',
- name: 'testTool',
- args: { arg1: 'value1' },
- isClientInitiated: false,
- prompt_id: 'prompt-id-2',
- },
- };
- const toolResponse: Part[] = [{ text: 'Tool response' }];
- mockCoreExecuteToolCall.mockResolvedValue({ responseParts: toolResponse });
-
- const firstCallEvents: ServerAnusStreamEvent[] = [toolCallEvent];
- const secondCallEvents: ServerAnusStreamEvent[] = [
- { type: AnusEventType.Content, value: 'Final answer' },
- ];
-
- mockAnusClient.sendMessageStream
- .mockReturnValueOnce(createStreamFromEvents(firstCallEvents))
- .mockReturnValueOnce(createStreamFromEvents(secondCallEvents));
-
- await runNonInteractive(mockConfig, 'Use a tool', 'prompt-id-2');
-
- expect(mockAnusClient.sendMessageStream).toHaveBeenCalledTimes(2);
- expect(mockCoreExecuteToolCall).toHaveBeenCalledWith(
- mockConfig,
- expect.objectContaining({ name: 'testTool' }),
- mockToolRegistry,
- expect.any(AbortSignal),
- );
- expect(mockAnusClient.sendMessageStream).toHaveBeenNthCalledWith(
- 2,
- [{ text: 'Tool response' }],
- expect.any(AbortSignal),
- 'prompt-id-2',
- );
- expect(processStdoutSpy).toHaveBeenCalledWith('Final answer');
- expect(processStdoutSpy).toHaveBeenCalledWith('\n');
- });
-
- it('should handle error during tool execution and should send error back to the model', async () => {
- const toolCallEvent: ServerAnusStreamEvent = {
- type: AnusEventType.ToolCallRequest,
- value: {
- callId: 'tool-1',
- name: 'errorTool',
- args: {},
- isClientInitiated: false,
- prompt_id: 'prompt-id-3',
- },
- };
- mockCoreExecuteToolCall.mockResolvedValue({
- error: new Error('Execution failed'),
- errorType: ToolErrorType.EXECUTION_FAILED,
- responseParts: {
- functionResponse: {
- name: 'errorTool',
- response: {
- output: 'Error: Execution failed',
- },
- },
- },
- resultDisplay: 'Execution failed',
- });
- const finalResponse: ServerAnusStreamEvent[] = [
- {
- type: AnusEventType.Content,
- value: 'Sorry, let me try again.',
- },
- ];
- mockAnusClient.sendMessageStream
- .mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
- .mockReturnValueOnce(createStreamFromEvents(finalResponse));
-
- await runNonInteractive(mockConfig, 'Trigger tool error', 'prompt-id-3');
-
- expect(mockCoreExecuteToolCall).toHaveBeenCalled();
- expect(consoleErrorSpy).toHaveBeenCalledWith(
- 'Error executing tool errorTool: Execution failed',
- );
- expect(processExitSpy).not.toHaveBeenCalled();
- expect(mockAnusClient.sendMessageStream).toHaveBeenCalledTimes(2);
- expect(mockAnusClient.sendMessageStream).toHaveBeenNthCalledWith(
- 2,
- [
- {
- functionResponse: {
- name: 'errorTool',
- response: {
- output: 'Error: Execution failed',
- },
- },
- },
- ],
- expect.any(AbortSignal),
- 'prompt-id-3',
- );
- expect(processStdoutSpy).toHaveBeenCalledWith('Sorry, let me try again.');
- });
-
- it('should exit with error if sendMessageStream throws initially', async () => {
- const apiError = new Error('API connection failed');
- mockAnusClient.sendMessageStream.mockImplementation(() => {
- throw apiError;
- });
-
- await runNonInteractive(mockConfig, 'Initial fail', 'prompt-id-4');
-
- expect(consoleErrorSpy).toHaveBeenCalledWith(
- '[API Error: API connection failed]',
- );
- expect(processExitSpy).toHaveBeenCalledWith(1);
- });
-
- it('should not exit if a tool is not found, and should send error back to model', async () => {
- const toolCallEvent: ServerAnusStreamEvent = {
- type: AnusEventType.ToolCallRequest,
- value: {
- callId: 'tool-1',
- name: 'nonexistentTool',
- args: {},
- isClientInitiated: false,
- prompt_id: 'prompt-id-5',
- },
- };
- mockCoreExecuteToolCall.mockResolvedValue({
- error: new Error('Tool "nonexistentTool" not found in registry.'),
- resultDisplay: 'Tool "nonexistentTool" not found in registry.',
- });
- const finalResponse: ServerAnusStreamEvent[] = [
- {
- type: AnusEventType.Content,
- value: "Sorry, I can't find that tool.",
- },
- ];
-
- mockAnusClient.sendMessageStream
- .mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
- .mockReturnValueOnce(createStreamFromEvents(finalResponse));
-
- await runNonInteractive(
- mockConfig,
- 'Trigger tool not found',
- 'prompt-id-5',
- );
-
- expect(mockCoreExecuteToolCall).toHaveBeenCalled();
- expect(consoleErrorSpy).toHaveBeenCalledWith(
- 'Error executing tool nonexistentTool: Tool "nonexistentTool" not found in registry.',
- );
- expect(processExitSpy).not.toHaveBeenCalled();
- expect(mockAnusClient.sendMessageStream).toHaveBeenCalledTimes(2);
- expect(processStdoutSpy).toHaveBeenCalledWith(
- "Sorry, I can't find that tool.",
- );
- });
-
- it('should exit when max session turns are exceeded', async () => {
- vi.mocked(mockConfig.getMaxSessionTurns).mockReturnValue(0);
- await runNonInteractive(mockConfig, 'Trigger loop', 'prompt-id-6');
- expect(consoleErrorSpy).toHaveBeenCalledWith(
- '\n Reached max session turns for this session. Increase the number of turns by specifying maxSessionTurns in settings.json.',
- );
- });
-});
diff --git a/packages/cli/src/nonInteractiveCli.ts b/packages/cli/src/nonInteractiveCli.ts
deleted file mode 100644
index f04ce27..0000000
--- a/packages/cli/src/nonInteractiveCli.ts
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import {
- Config,
- ToolCallRequestInfo,
- executeToolCall,
- ToolRegistry,
- shutdownTelemetry,
- isTelemetrySdkInitialized,
- AnusEventType,
- parseAndFormatApiError,
-} from '@anus-dev/anus-core';
-import { Content, Part, FunctionCall } from '@google/genai';
-
-import { ConsolePatcher } from './ui/utils/ConsolePatcher.js';
-
-export async function runNonInteractive(
- config: Config,
- input: string,
- prompt_id: string,
-): Promise {
- const consolePatcher = new ConsolePatcher({
- stderr: true,
- debugMode: config.getDebugMode(),
- });
-
- try {
- consolePatcher.patch();
- // Handle EPIPE errors when the output is piped to a command that closes early.
- process.stdout.on('error', (err: NodeJS.ErrnoException) => {
- if (err.code === 'EPIPE') {
- // Exit gracefully if the pipe is closed.
- process.exit(0);
- }
- });
-
- const anusClient = config.getAnusClient();
- const toolRegistry: ToolRegistry = await config.getToolRegistry();
-
- const abortController = new AbortController();
- let currentMessages: Content[] = [
- { role: 'user', parts: [{ text: input }] },
- ];
- let turnCount = 0;
- while (true) {
- turnCount++;
- if (
- config.getMaxSessionTurns() >= 0 &&
- turnCount > config.getMaxSessionTurns()
- ) {
- console.error(
- '\n Reached max session turns for this session. Increase the number of turns by specifying maxSessionTurns in settings.json.',
- );
- return;
- }
- const functionCalls: FunctionCall[] = [];
-
- const responseStream = anusClient.sendMessageStream(
- currentMessages[0]?.parts || [],
- abortController.signal,
- prompt_id,
- );
-
- for await (const event of responseStream) {
- if (abortController.signal.aborted) {
- console.error('Operation cancelled.');
- return;
- }
-
- if (event.type === AnusEventType.Content) {
- process.stdout.write(event.value);
- } else if (event.type === AnusEventType.ToolCallRequest) {
- const toolCallRequest = event.value;
- const fc: FunctionCall = {
- name: toolCallRequest.name,
- args: toolCallRequest.args,
- id: toolCallRequest.callId,
- };
- functionCalls.push(fc);
- }
- }
-
- if (functionCalls.length > 0) {
- const toolResponseParts: Part[] = [];
-
- for (const fc of functionCalls) {
- const callId = fc.id ?? `${fc.name}-${Date.now()}`;
- const requestInfo: ToolCallRequestInfo = {
- callId,
- name: fc.name as string,
- args: (fc.args ?? {}) as Record,
- isClientInitiated: false,
- prompt_id,
- };
-
- const toolResponse = await executeToolCall(
- config,
- requestInfo,
- toolRegistry,
- abortController.signal,
- );
-
- if (toolResponse.error) {
- console.error(
- `Error executing tool ${fc.name}: ${toolResponse.resultDisplay || toolResponse.error.message}`,
- );
- }
-
- if (toolResponse.responseParts) {
- const parts = Array.isArray(toolResponse.responseParts)
- ? toolResponse.responseParts
- : [toolResponse.responseParts];
- for (const part of parts) {
- if (typeof part === 'string') {
- toolResponseParts.push({ text: part });
- } else if (part) {
- toolResponseParts.push(part);
- }
- }
- }
- }
- currentMessages = [{ role: 'user', parts: toolResponseParts }];
- } else {
- process.stdout.write('\n'); // Ensure a final newline
- return;
- }
- }
- } catch (error) {
- console.error(
- parseAndFormatApiError(
- error,
- config.getContentGeneratorConfig()?.authType,
- ),
- );
- process.exit(1);
- } finally {
- consolePatcher.cleanup();
- if (isTelemetrySdkInitialized()) {
- await shutdownTelemetry(config);
- }
- }
-}
diff --git a/packages/cli/src/patches/is-in-ci.ts b/packages/cli/src/patches/is-in-ci.ts
deleted file mode 100644
index 968c183..0000000
--- a/packages/cli/src/patches/is-in-ci.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-// This is a replacement for the `is-in-ci` package that always returns false.
-// We are doing this to avoid the issue where `ink` does not render the UI
-// when it detects that it is running in a CI environment.
-// This is safe because `ink` (and thus `is-in-ci`) is only used in the
-// interactive code path of the CLI.
-// See issue #1563 for more details.
-
-const isInCi = false;
-
-// eslint-disable-next-line import/no-default-export
-export default isInCi;
diff --git a/packages/cli/src/services/BuiltinCommandLoader.test.ts b/packages/cli/src/services/BuiltinCommandLoader.test.ts
deleted file mode 100644
index f2cd926..0000000
--- a/packages/cli/src/services/BuiltinCommandLoader.test.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-vi.mock('../ui/commands/aboutCommand.js', async () => {
- const { CommandKind } = await import('../ui/commands/types.js');
- return {
- aboutCommand: {
- name: 'about',
- description: 'About the CLI',
- kind: CommandKind.BUILT_IN,
- },
- };
-});
-
-vi.mock('../ui/commands/ideCommand.js', () => ({ ideCommand: vi.fn() }));
-vi.mock('../ui/commands/restoreCommand.js', () => ({
- restoreCommand: vi.fn(),
-}));
-
-import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
-import { BuiltinCommandLoader } from './BuiltinCommandLoader.js';
-import { Config } from '@anus-dev/anus-core';
-import { CommandKind } from '../ui/commands/types.js';
-
-import { ideCommand } from '../ui/commands/ideCommand.js';
-import { restoreCommand } from '../ui/commands/restoreCommand.js';
-
-vi.mock('../ui/commands/authCommand.js', () => ({ authCommand: {} }));
-vi.mock('../ui/commands/bugCommand.js', () => ({ bugCommand: {} }));
-vi.mock('../ui/commands/chatCommand.js', () => ({ chatCommand: {} }));
-vi.mock('../ui/commands/clearCommand.js', () => ({ clearCommand: {} }));
-vi.mock('../ui/commands/compressCommand.js', () => ({ compressCommand: {} }));
-vi.mock('../ui/commands/corgiCommand.js', () => ({ corgiCommand: {} }));
-vi.mock('../ui/commands/docsCommand.js', () => ({ docsCommand: {} }));
-vi.mock('../ui/commands/editorCommand.js', () => ({ editorCommand: {} }));
-vi.mock('../ui/commands/extensionsCommand.js', () => ({
- extensionsCommand: {},
-}));
-vi.mock('../ui/commands/helpCommand.js', () => ({ helpCommand: {} }));
-vi.mock('../ui/commands/memoryCommand.js', () => ({ memoryCommand: {} }));
-vi.mock('../ui/commands/privacyCommand.js', () => ({ privacyCommand: {} }));
-vi.mock('../ui/commands/quitCommand.js', () => ({ quitCommand: {} }));
-vi.mock('../ui/commands/statsCommand.js', () => ({ statsCommand: {} }));
-vi.mock('../ui/commands/themeCommand.js', () => ({ themeCommand: {} }));
-vi.mock('../ui/commands/toolsCommand.js', () => ({ toolsCommand: {} }));
-vi.mock('../ui/commands/mcpCommand.js', () => ({
- mcpCommand: {
- name: 'mcp',
- description: 'MCP command',
- kind: 'BUILT_IN',
- },
-}));
-
-describe('BuiltinCommandLoader', () => {
- let mockConfig: Config;
-
- const ideCommandMock = ideCommand as Mock;
- const restoreCommandMock = restoreCommand as Mock;
-
- beforeEach(() => {
- vi.clearAllMocks();
- mockConfig = { some: 'config' } as unknown as Config;
-
- ideCommandMock.mockReturnValue({
- name: 'ide',
- description: 'IDE command',
- kind: CommandKind.BUILT_IN,
- });
- restoreCommandMock.mockReturnValue({
- name: 'restore',
- description: 'Restore command',
- kind: CommandKind.BUILT_IN,
- });
- });
-
- it('should correctly pass the config object to command factory functions', async () => {
- const loader = new BuiltinCommandLoader(mockConfig);
- await loader.loadCommands(new AbortController().signal);
-
- expect(ideCommandMock).toHaveBeenCalledTimes(1);
- expect(ideCommandMock).toHaveBeenCalledWith(mockConfig);
- expect(restoreCommandMock).toHaveBeenCalledTimes(1);
- expect(restoreCommandMock).toHaveBeenCalledWith(mockConfig);
- });
-
- it('should filter out null command definitions returned by factories', async () => {
- // Override the mock's behavior for this specific test.
- ideCommandMock.mockReturnValue(null);
- const loader = new BuiltinCommandLoader(mockConfig);
- const commands = await loader.loadCommands(new AbortController().signal);
-
- // The 'ide' command should be filtered out.
- const ideCmd = commands.find((c) => c.name === 'ide');
- expect(ideCmd).toBeUndefined();
-
- // Other commands should still be present.
- const aboutCmd = commands.find((c) => c.name === 'about');
- expect(aboutCmd).toBeDefined();
- });
-
- it('should handle a null config gracefully when calling factories', async () => {
- const loader = new BuiltinCommandLoader(null);
- await loader.loadCommands(new AbortController().signal);
- expect(ideCommandMock).toHaveBeenCalledTimes(1);
- expect(ideCommandMock).toHaveBeenCalledWith(null);
- expect(restoreCommandMock).toHaveBeenCalledTimes(1);
- expect(restoreCommandMock).toHaveBeenCalledWith(null);
- });
-
- it('should return a list of all loaded commands', async () => {
- const loader = new BuiltinCommandLoader(mockConfig);
- const commands = await loader.loadCommands(new AbortController().signal);
-
- const aboutCmd = commands.find((c) => c.name === 'about');
- expect(aboutCmd).toBeDefined();
- expect(aboutCmd?.kind).toBe(CommandKind.BUILT_IN);
-
- const ideCmd = commands.find((c) => c.name === 'ide');
- expect(ideCmd).toBeDefined();
-
- const mcpCmd = commands.find((c) => c.name === 'mcp');
- expect(mcpCmd).toBeDefined();
- });
-});
diff --git a/packages/cli/src/services/BuiltinCommandLoader.ts b/packages/cli/src/services/BuiltinCommandLoader.ts
deleted file mode 100644
index 999afb8..0000000
--- a/packages/cli/src/services/BuiltinCommandLoader.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { ICommandLoader } from './types.js';
-import { SlashCommand } from '../ui/commands/types.js';
-import { Config } from '@anus-dev/anus-core';
-import { aboutCommand } from '../ui/commands/aboutCommand.js';
-import { authCommand } from '../ui/commands/authCommand.js';
-import { bugCommand } from '../ui/commands/bugCommand.js';
-import { chatCommand } from '../ui/commands/chatCommand.js';
-import { clearCommand } from '../ui/commands/clearCommand.js';
-import { compressCommand } from '../ui/commands/compressCommand.js';
-import { copyCommand } from '../ui/commands/copyCommand.js';
-import { corgiCommand } from '../ui/commands/corgiCommand.js';
-import { docsCommand } from '../ui/commands/docsCommand.js';
-import { directoryCommand } from '../ui/commands/directoryCommand.js';
-import { editorCommand } from '../ui/commands/editorCommand.js';
-import { extensionsCommand } from '../ui/commands/extensionsCommand.js';
-import { helpCommand } from '../ui/commands/helpCommand.js';
-import { ideCommand } from '../ui/commands/ideCommand.js';
-import { initCommand } from '../ui/commands/initCommand.js';
-import { mcpCommand } from '../ui/commands/mcpCommand.js';
-import { memoryCommand } from '../ui/commands/memoryCommand.js';
-import { privacyCommand } from '../ui/commands/privacyCommand.js';
-import { quitCommand } from '../ui/commands/quitCommand.js';
-import { restoreCommand } from '../ui/commands/restoreCommand.js';
-import { statsCommand } from '../ui/commands/statsCommand.js';
-import { themeCommand } from '../ui/commands/themeCommand.js';
-import { toolsCommand } from '../ui/commands/toolsCommand.js';
-import { settingsCommand } from '../ui/commands/settingsCommand.js';
-import { vimCommand } from '../ui/commands/vimCommand.js';
-import { setupGithubCommand } from '../ui/commands/setupGithubCommand.js';
-import { terminalSetupCommand } from '../ui/commands/terminalSetupCommand.js';
-
-/**
- * Loads the core, hard-coded slash commands that are an integral part
- * of the ANUS application.
- */
-export class BuiltinCommandLoader implements ICommandLoader {
- constructor(private config: Config | null) {}
-
- /**
- * Gathers all raw built-in command definitions, injects dependencies where
- * needed (e.g., config) and filters out any that are not available.
- *
- * @param _signal An AbortSignal (unused for this synchronous loader).
- * @returns A promise that resolves to an array of `SlashCommand` objects.
- */
- async loadCommands(_signal: AbortSignal): Promise {
- const allDefinitions: Array = [
- aboutCommand,
- authCommand,
- bugCommand,
- chatCommand,
- clearCommand,
- compressCommand,
- copyCommand,
- corgiCommand,
- docsCommand,
- directoryCommand,
- editorCommand,
- extensionsCommand,
- helpCommand,
- ideCommand(this.config),
- initCommand,
- mcpCommand,
- memoryCommand,
- privacyCommand,
- quitCommand,
- restoreCommand(this.config),
- statsCommand,
- themeCommand,
- toolsCommand,
- settingsCommand,
- vimCommand,
- setupGithubCommand,
- terminalSetupCommand,
- ];
-
- return allDefinitions.filter((cmd): cmd is SlashCommand => cmd !== null);
- }
-}
diff --git a/packages/cli/src/services/CommandService.test.ts b/packages/cli/src/services/CommandService.test.ts
deleted file mode 100644
index 3774eb1..0000000
--- a/packages/cli/src/services/CommandService.test.ts
+++ /dev/null
@@ -1,353 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
-import { CommandService } from './CommandService.js';
-import { type ICommandLoader } from './types.js';
-import { CommandKind, type SlashCommand } from '../ui/commands/types.js';
-
-const createMockCommand = (name: string, kind: CommandKind): SlashCommand => ({
- name,
- description: `Description for ${name}`,
- kind,
- action: vi.fn(),
-});
-
-const mockCommandA = createMockCommand('command-a', CommandKind.BUILT_IN);
-const mockCommandB = createMockCommand('command-b', CommandKind.BUILT_IN);
-const mockCommandC = createMockCommand('command-c', CommandKind.FILE);
-const mockCommandB_Override = createMockCommand('command-b', CommandKind.FILE);
-
-class MockCommandLoader implements ICommandLoader {
- private commandsToLoad: SlashCommand[];
-
- constructor(commandsToLoad: SlashCommand[]) {
- this.commandsToLoad = commandsToLoad;
- }
-
- loadCommands = vi.fn(
- async (): Promise => Promise.resolve(this.commandsToLoad),
- );
-}
-
-describe('CommandService', () => {
- beforeEach(() => {
- vi.spyOn(console, 'debug').mockImplementation(() => {});
- });
-
- afterEach(() => {
- vi.restoreAllMocks();
- });
-
- it('should load commands from a single loader', async () => {
- const mockLoader = new MockCommandLoader([mockCommandA, mockCommandB]);
- const service = await CommandService.create(
- [mockLoader],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
-
- expect(mockLoader.loadCommands).toHaveBeenCalledTimes(1);
- expect(commands).toHaveLength(2);
- expect(commands).toEqual(
- expect.arrayContaining([mockCommandA, mockCommandB]),
- );
- });
-
- it('should aggregate commands from multiple loaders', async () => {
- const loader1 = new MockCommandLoader([mockCommandA]);
- const loader2 = new MockCommandLoader([mockCommandC]);
- const service = await CommandService.create(
- [loader1, loader2],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
-
- expect(loader1.loadCommands).toHaveBeenCalledTimes(1);
- expect(loader2.loadCommands).toHaveBeenCalledTimes(1);
- expect(commands).toHaveLength(2);
- expect(commands).toEqual(
- expect.arrayContaining([mockCommandA, mockCommandC]),
- );
- });
-
- it('should override commands from earlier loaders with those from later loaders', async () => {
- const loader1 = new MockCommandLoader([mockCommandA, mockCommandB]);
- const loader2 = new MockCommandLoader([
- mockCommandB_Override,
- mockCommandC,
- ]);
- const service = await CommandService.create(
- [loader1, loader2],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
-
- expect(commands).toHaveLength(3); // Should be A, C, and the overridden B.
-
- // The final list should contain the override from the *last* loader.
- const commandB = commands.find((cmd) => cmd.name === 'command-b');
- expect(commandB).toBeDefined();
- expect(commandB?.kind).toBe(CommandKind.FILE); // Verify it's the overridden version.
- expect(commandB).toEqual(mockCommandB_Override);
-
- // Ensure the other commands are still present.
- expect(commands).toEqual(
- expect.arrayContaining([
- mockCommandA,
- mockCommandC,
- mockCommandB_Override,
- ]),
- );
- });
-
- it('should handle loaders that return an empty array of commands gracefully', async () => {
- const loader1 = new MockCommandLoader([mockCommandA]);
- const emptyLoader = new MockCommandLoader([]);
- const loader3 = new MockCommandLoader([mockCommandB]);
- const service = await CommandService.create(
- [loader1, emptyLoader, loader3],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
-
- expect(emptyLoader.loadCommands).toHaveBeenCalledTimes(1);
- expect(commands).toHaveLength(2);
- expect(commands).toEqual(
- expect.arrayContaining([mockCommandA, mockCommandB]),
- );
- });
-
- it('should load commands from successful loaders even if one fails', async () => {
- const successfulLoader = new MockCommandLoader([mockCommandA]);
- const failingLoader = new MockCommandLoader([]);
- const error = new Error('Loader failed');
- vi.spyOn(failingLoader, 'loadCommands').mockRejectedValue(error);
-
- const service = await CommandService.create(
- [successfulLoader, failingLoader],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
- expect(commands).toHaveLength(1);
- expect(commands).toEqual([mockCommandA]);
- expect(console.debug).toHaveBeenCalledWith(
- 'A command loader failed:',
- error,
- );
- });
-
- it('getCommands should return a readonly array that cannot be mutated', async () => {
- const service = await CommandService.create(
- [new MockCommandLoader([mockCommandA])],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
-
- // Expect it to throw a TypeError at runtime because the array is frozen.
- expect(() => {
- // @ts-expect-error - Testing immutability is intentional here.
- commands.push(mockCommandB);
- }).toThrow();
-
- // Verify the original array was not mutated.
- expect(service.getCommands()).toHaveLength(1);
- });
-
- it('should pass the abort signal to all loaders', async () => {
- const controller = new AbortController();
- const signal = controller.signal;
-
- const loader1 = new MockCommandLoader([mockCommandA]);
- const loader2 = new MockCommandLoader([mockCommandB]);
-
- await CommandService.create([loader1, loader2], signal);
-
- expect(loader1.loadCommands).toHaveBeenCalledTimes(1);
- expect(loader1.loadCommands).toHaveBeenCalledWith(signal);
- expect(loader2.loadCommands).toHaveBeenCalledTimes(1);
- expect(loader2.loadCommands).toHaveBeenCalledWith(signal);
- });
-
- it('should rename extension commands when they conflict', async () => {
- const builtinCommand = createMockCommand('deploy', CommandKind.BUILT_IN);
- const userCommand = createMockCommand('sync', CommandKind.FILE);
- const extensionCommand1 = {
- ...createMockCommand('deploy', CommandKind.FILE),
- extensionName: 'firebase',
- description: '[firebase] Deploy to Firebase',
- };
- const extensionCommand2 = {
- ...createMockCommand('sync', CommandKind.FILE),
- extensionName: 'git-helper',
- description: '[git-helper] Sync with remote',
- };
-
- const mockLoader1 = new MockCommandLoader([builtinCommand]);
- const mockLoader2 = new MockCommandLoader([
- userCommand,
- extensionCommand1,
- extensionCommand2,
- ]);
-
- const service = await CommandService.create(
- [mockLoader1, mockLoader2],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
- expect(commands).toHaveLength(4);
-
- // Built-in command keeps original name
- const deployBuiltin = commands.find(
- (cmd) => cmd.name === 'deploy' && !cmd.extensionName,
- );
- expect(deployBuiltin).toBeDefined();
- expect(deployBuiltin?.kind).toBe(CommandKind.BUILT_IN);
-
- // Extension command conflicting with built-in gets renamed
- const deployExtension = commands.find(
- (cmd) => cmd.name === 'firebase.deploy',
- );
- expect(deployExtension).toBeDefined();
- expect(deployExtension?.extensionName).toBe('firebase');
-
- // User command keeps original name
- const syncUser = commands.find(
- (cmd) => cmd.name === 'sync' && !cmd.extensionName,
- );
- expect(syncUser).toBeDefined();
- expect(syncUser?.kind).toBe(CommandKind.FILE);
-
- // Extension command conflicting with user command gets renamed
- const syncExtension = commands.find(
- (cmd) => cmd.name === 'git-helper.sync',
- );
- expect(syncExtension).toBeDefined();
- expect(syncExtension?.extensionName).toBe('git-helper');
- });
-
- it('should handle user/project command override correctly', async () => {
- const builtinCommand = createMockCommand('help', CommandKind.BUILT_IN);
- const userCommand = createMockCommand('help', CommandKind.FILE);
- const projectCommand = createMockCommand('deploy', CommandKind.FILE);
- const userDeployCommand = createMockCommand('deploy', CommandKind.FILE);
-
- const mockLoader1 = new MockCommandLoader([builtinCommand]);
- const mockLoader2 = new MockCommandLoader([
- userCommand,
- userDeployCommand,
- projectCommand,
- ]);
-
- const service = await CommandService.create(
- [mockLoader1, mockLoader2],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
- expect(commands).toHaveLength(2);
-
- // User command overrides built-in
- const helpCommand = commands.find((cmd) => cmd.name === 'help');
- expect(helpCommand).toBeDefined();
- expect(helpCommand?.kind).toBe(CommandKind.FILE);
-
- // Project command overrides user command (last wins)
- const deployCommand = commands.find((cmd) => cmd.name === 'deploy');
- expect(deployCommand).toBeDefined();
- expect(deployCommand?.kind).toBe(CommandKind.FILE);
- });
-
- it('should handle secondary conflicts when renaming extension commands', async () => {
- // User has both /deploy and /gcp.deploy commands
- const userCommand1 = createMockCommand('deploy', CommandKind.FILE);
- const userCommand2 = createMockCommand('gcp.deploy', CommandKind.FILE);
-
- // Extension also has a deploy command that will conflict with user's /deploy
- const extensionCommand = {
- ...createMockCommand('deploy', CommandKind.FILE),
- extensionName: 'gcp',
- description: '[gcp] Deploy to Google Cloud',
- };
-
- const mockLoader = new MockCommandLoader([
- userCommand1,
- userCommand2,
- extensionCommand,
- ]);
-
- const service = await CommandService.create(
- [mockLoader],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
- expect(commands).toHaveLength(3);
-
- // Original user command keeps its name
- const deployUser = commands.find(
- (cmd) => cmd.name === 'deploy' && !cmd.extensionName,
- );
- expect(deployUser).toBeDefined();
-
- // User's dot notation command keeps its name
- const gcpDeployUser = commands.find(
- (cmd) => cmd.name === 'gcp.deploy' && !cmd.extensionName,
- );
- expect(gcpDeployUser).toBeDefined();
-
- // Extension command gets renamed with suffix due to secondary conflict
- const deployExtension = commands.find(
- (cmd) => cmd.name === 'gcp.deploy1' && cmd.extensionName === 'gcp',
- );
- expect(deployExtension).toBeDefined();
- expect(deployExtension?.description).toBe('[gcp] Deploy to Google Cloud');
- });
-
- it('should handle multiple secondary conflicts with incrementing suffixes', async () => {
- // User has /deploy, /gcp.deploy, and /gcp.deploy1
- const userCommand1 = createMockCommand('deploy', CommandKind.FILE);
- const userCommand2 = createMockCommand('gcp.deploy', CommandKind.FILE);
- const userCommand3 = createMockCommand('gcp.deploy1', CommandKind.FILE);
-
- // Extension has a deploy command
- const extensionCommand = {
- ...createMockCommand('deploy', CommandKind.FILE),
- extensionName: 'gcp',
- description: '[gcp] Deploy to Google Cloud',
- };
-
- const mockLoader = new MockCommandLoader([
- userCommand1,
- userCommand2,
- userCommand3,
- extensionCommand,
- ]);
-
- const service = await CommandService.create(
- [mockLoader],
- new AbortController().signal,
- );
-
- const commands = service.getCommands();
- expect(commands).toHaveLength(4);
-
- // Extension command gets renamed with suffix 2 due to multiple conflicts
- const deployExtension = commands.find(
- (cmd) => cmd.name === 'gcp.deploy2' && cmd.extensionName === 'gcp',
- );
- expect(deployExtension).toBeDefined();
- expect(deployExtension?.description).toBe('[gcp] Deploy to Google Cloud');
- });
-});
diff --git a/packages/cli/src/services/CommandService.ts b/packages/cli/src/services/CommandService.ts
deleted file mode 100644
index d0bed41..0000000
--- a/packages/cli/src/services/CommandService.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { SlashCommand } from '../ui/commands/types.js';
-import { ICommandLoader } from './types.js';
-
-/**
- * Orchestrates the discovery and loading of all slash commands for the CLI.
- *
- * This service operates on a provider-based loader pattern. It is initialized
- * with an array of `ICommandLoader` instances, each responsible for fetching
- * commands from a specific source (e.g., built-in code, local files).
- *
- * The CommandService is responsible for invoking these loaders, aggregating their
- * results, and resolving any name conflicts. This architecture allows the command
- * system to be extended with new sources without modifying the service itself.
- */
-export class CommandService {
- /**
- * Private constructor to enforce the use of the async factory.
- * @param commands A readonly array of the fully loaded and de-duplicated commands.
- */
- private constructor(private readonly commands: readonly SlashCommand[]) {}
-
- /**
- * Asynchronously creates and initializes a new CommandService instance.
- *
- * This factory method orchestrates the entire command loading process. It
- * runs all provided loaders in parallel, aggregates their results, handles
- * name conflicts for extension commands by renaming them, and then returns a
- * fully constructed `CommandService` instance.
- *
- * Conflict resolution:
- * - Extension commands that conflict with existing commands are renamed to
- * `extensionName.commandName`
- * - Non-extension commands (built-in, user, project) override earlier commands
- * with the same name based on loader order
- *
- * @param loaders An array of objects that conform to the `ICommandLoader`
- * interface. Built-in commands should come first, followed by FileCommandLoader.
- * @param signal An AbortSignal to cancel the loading process.
- * @returns A promise that resolves to a new, fully initialized `CommandService` instance.
- */
- static async create(
- loaders: ICommandLoader[],
- signal: AbortSignal,
- ): Promise {
- const results = await Promise.allSettled(
- loaders.map((loader) => loader.loadCommands(signal)),
- );
-
- const allCommands: SlashCommand[] = [];
- for (const result of results) {
- if (result.status === 'fulfilled') {
- allCommands.push(...result.value);
- } else {
- console.debug('A command loader failed:', result.reason);
- }
- }
-
- const commandMap = new Map();
- for (const cmd of allCommands) {
- let finalName = cmd.name;
-
- // Extension commands get renamed if they conflict with existing commands
- if (cmd.extensionName && commandMap.has(cmd.name)) {
- let renamedName = `${cmd.extensionName}.${cmd.name}`;
- let suffix = 1;
-
- // Keep trying until we find a name that doesn't conflict
- while (commandMap.has(renamedName)) {
- renamedName = `${cmd.extensionName}.${cmd.name}${suffix}`;
- suffix++;
- }
-
- finalName = renamedName;
- }
-
- commandMap.set(finalName, {
- ...cmd,
- name: finalName,
- });
- }
-
- const finalCommands = Object.freeze(Array.from(commandMap.values()));
- return new CommandService(finalCommands);
- }
-
- /**
- * Retrieves the currently loaded and de-duplicated list of slash commands.
- *
- * This method is a safe accessor for the service's state. It returns a
- * readonly array, preventing consumers from modifying the service's internal state.
- *
- * @returns A readonly, unified array of available `SlashCommand` objects.
- */
- getCommands(): readonly SlashCommand[] {
- return this.commands;
- }
-}
diff --git a/packages/cli/src/services/FileCommandLoader.test.ts b/packages/cli/src/services/FileCommandLoader.test.ts
deleted file mode 100644
index 275ac28..0000000
--- a/packages/cli/src/services/FileCommandLoader.test.ts
+++ /dev/null
@@ -1,1002 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import * as path from 'node:path';
-import {
- Config,
- getProjectCommandsDir,
- getUserCommandsDir,
-} from '@anus-dev/anus-core';
-import mock from 'mock-fs';
-import { FileCommandLoader } from './FileCommandLoader.js';
-import { assert, vi } from 'vitest';
-import { createMockCommandContext } from '../test-utils/mockCommandContext.js';
-import {
- SHELL_INJECTION_TRIGGER,
- SHORTHAND_ARGS_PLACEHOLDER,
-} from './prompt-processors/types.js';
-import {
- ConfirmationRequiredError,
- ShellProcessor,
-} from './prompt-processors/shellProcessor.js';
-import { DefaultArgumentProcessor } from './prompt-processors/argumentProcessor.js';
-import { CommandContext } from '../ui/commands/types.js';
-
-const mockShellProcess = vi.hoisted(() => vi.fn());
-vi.mock('./prompt-processors/shellProcessor.js', () => ({
- ShellProcessor: vi.fn().mockImplementation(() => ({
- process: mockShellProcess,
- })),
- ConfirmationRequiredError: class extends Error {
- constructor(
- message: string,
- public commandsToConfirm: string[],
- ) {
- super(message);
- this.name = 'ConfirmationRequiredError';
- }
- },
-}));
-
-vi.mock('./prompt-processors/argumentProcessor.js', async (importOriginal) => {
- const original =
- await importOriginal<
- typeof import('./prompt-processors/argumentProcessor.js')
- >();
- return {
- DefaultArgumentProcessor: vi
- .fn()
- .mockImplementation(() => new original.DefaultArgumentProcessor()),
- };
-});
-vi.mock('@anus-dev/anus-core', async (importOriginal) => {
- const original = await importOriginal();
- return {
- ...original,
- isCommandAllowed: vi.fn(),
- ShellExecutionService: {
- execute: vi.fn(),
- },
- };
-});
-
-describe('FileCommandLoader', () => {
- const signal: AbortSignal = new AbortController().signal;
-
- beforeEach(() => {
- vi.clearAllMocks();
- mockShellProcess.mockImplementation(
- (prompt: string, context: CommandContext) => {
- const userArgsRaw = context?.invocation?.args || '';
- const processedPrompt = prompt.replaceAll(
- SHORTHAND_ARGS_PLACEHOLDER,
- userArgsRaw,
- );
- return Promise.resolve(processedPrompt);
- },
- );
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('loads a single command from a file', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'test.toml': 'prompt = "This is a test prompt"',
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(1);
- const command = commands[0];
- expect(command).toBeDefined();
- expect(command.name).toBe('test');
-
- const result = await command.action?.(
- createMockCommandContext({
- invocation: {
- raw: '/test',
- name: 'test',
- args: '',
- },
- }),
- '',
- );
- if (result?.type === 'submit_prompt') {
- expect(result.content).toBe('This is a test prompt');
- } else {
- assert.fail('Incorrect action type');
- }
- });
-
- // Symlink creation on Windows requires special permissions that are not
- // available in the standard CI environment. Therefore, we skip these tests
- // on Windows to prevent CI failures. The core functionality is still
- // validated on Linux and macOS.
- const itif = (condition: boolean) => (condition ? it : it.skip);
-
- itif(process.platform !== 'win32')(
- 'loads commands from a symlinked directory',
- async () => {
- const userCommandsDir = getUserCommandsDir();
- const realCommandsDir = '/real/commands';
- mock({
- [realCommandsDir]: {
- 'test.toml': 'prompt = "This is a test prompt"',
- },
- // Symlink the user commands directory to the real one
- [userCommandsDir]: mock.symlink({
- path: realCommandsDir,
- }),
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(1);
- const command = commands[0];
- expect(command).toBeDefined();
- expect(command.name).toBe('test');
- },
- );
-
- itif(process.platform !== 'win32')(
- 'loads commands from a symlinked subdirectory',
- async () => {
- const userCommandsDir = getUserCommandsDir();
- const realNamespacedDir = '/real/namespaced-commands';
- mock({
- [userCommandsDir]: {
- namespaced: mock.symlink({
- path: realNamespacedDir,
- }),
- },
- [realNamespacedDir]: {
- 'my-test.toml': 'prompt = "This is a test prompt"',
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(1);
- const command = commands[0];
- expect(command).toBeDefined();
- expect(command.name).toBe('namespaced:my-test');
- },
- );
-
- it('loads multiple commands', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'test1.toml': 'prompt = "Prompt 1"',
- 'test2.toml': 'prompt = "Prompt 2"',
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(2);
- });
-
- it('creates deeply nested namespaces correctly', async () => {
- const userCommandsDir = getUserCommandsDir();
-
- mock({
- [userCommandsDir]: {
- gcp: {
- pipelines: {
- 'run.toml': 'prompt = "run pipeline"',
- },
- },
- },
- });
- const mockConfig = {
- getProjectRoot: vi.fn(() => '/path/to/project'),
- getExtensions: vi.fn(() => []),
- } as unknown as Config;
- const loader = new FileCommandLoader(mockConfig);
- const commands = await loader.loadCommands(signal);
- expect(commands).toHaveLength(1);
- expect(commands[0]!.name).toBe('gcp:pipelines:run');
- });
-
- it('creates namespaces from nested directories', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- git: {
- 'commit.toml': 'prompt = "git commit prompt"',
- },
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(1);
- const command = commands[0];
- expect(command).toBeDefined();
- expect(command.name).toBe('git:commit');
- });
-
- it('returns both user and project commands in order', async () => {
- const userCommandsDir = getUserCommandsDir();
- const projectCommandsDir = getProjectCommandsDir(process.cwd());
- mock({
- [userCommandsDir]: {
- 'test.toml': 'prompt = "User prompt"',
- },
- [projectCommandsDir]: {
- 'test.toml': 'prompt = "Project prompt"',
- },
- });
-
- const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
- getExtensions: vi.fn(() => []),
- } as unknown as Config;
- const loader = new FileCommandLoader(mockConfig);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(2);
- const userResult = await commands[0].action?.(
- createMockCommandContext({
- invocation: {
- raw: '/test',
- name: 'test',
- args: '',
- },
- }),
- '',
- );
- if (userResult?.type === 'submit_prompt') {
- expect(userResult.content).toBe('User prompt');
- } else {
- assert.fail('Incorrect action type for user command');
- }
- const projectResult = await commands[1].action?.(
- createMockCommandContext({
- invocation: {
- raw: '/test',
- name: 'test',
- args: '',
- },
- }),
- '',
- );
- if (projectResult?.type === 'submit_prompt') {
- expect(projectResult.content).toBe('Project prompt');
- } else {
- assert.fail('Incorrect action type for project command');
- }
- });
-
- it('ignores files with TOML syntax errors', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'invalid.toml': 'this is not valid toml',
- 'good.toml': 'prompt = "This one is fine"',
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(1);
- expect(commands[0].name).toBe('good');
- });
-
- it('ignores files that are semantically invalid (missing prompt)', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'no_prompt.toml': 'description = "This file is missing a prompt"',
- 'good.toml': 'prompt = "This one is fine"',
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(1);
- expect(commands[0].name).toBe('good');
- });
-
- it('handles filename edge cases correctly', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'test.v1.toml': 'prompt = "Test prompt"',
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
- const command = commands[0];
- expect(command).toBeDefined();
- expect(command.name).toBe('test.v1');
- });
-
- it('handles file system errors gracefully', async () => {
- mock({}); // Mock an empty file system
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
- expect(commands).toHaveLength(0);
- });
-
- it('uses a default description if not provided', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'test.toml': 'prompt = "Test prompt"',
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
- const command = commands[0];
- expect(command).toBeDefined();
- expect(command.description).toBe('Custom command from test.toml');
- });
-
- it('uses the provided description', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'test.toml': 'prompt = "Test prompt"\ndescription = "My test command"',
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
- const command = commands[0];
- expect(command).toBeDefined();
- expect(command.description).toBe('My test command');
- });
-
- it('should sanitize colons in filenames to prevent namespace conflicts', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'legacy:command.toml': 'prompt = "This is a legacy command"',
- },
- });
-
- const loader = new FileCommandLoader(null);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(1);
- const command = commands[0];
- expect(command).toBeDefined();
-
- // Verify that the ':' in the filename was replaced with an '_'
- expect(command.name).toBe('legacy_command');
- });
-
- describe('Processor Instantiation Logic', () => {
- it('instantiates only DefaultArgumentProcessor if no {{args}} or !{} are present', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'simple.toml': `prompt = "Just a regular prompt"`,
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- await loader.loadCommands(signal);
-
- expect(ShellProcessor).not.toHaveBeenCalled();
- expect(DefaultArgumentProcessor).toHaveBeenCalledTimes(1);
- });
-
- it('instantiates only ShellProcessor if {{args}} is present (but not !{})', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'args.toml': `prompt = "Prompt with {{args}}"`,
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- await loader.loadCommands(signal);
-
- expect(ShellProcessor).toHaveBeenCalledTimes(1);
- expect(DefaultArgumentProcessor).not.toHaveBeenCalled();
- });
-
- it('instantiates ShellProcessor and DefaultArgumentProcessor if !{} is present (but not {{args}})', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'shell.toml': `prompt = "Prompt with !{cmd}"`,
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- await loader.loadCommands(signal);
-
- expect(ShellProcessor).toHaveBeenCalledTimes(1);
- expect(DefaultArgumentProcessor).toHaveBeenCalledTimes(1);
- });
-
- it('instantiates only ShellProcessor if both {{args}} and !{} are present', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'both.toml': `prompt = "Prompt with {{args}} and !{cmd}"`,
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- await loader.loadCommands(signal);
-
- expect(ShellProcessor).toHaveBeenCalledTimes(1);
- expect(DefaultArgumentProcessor).not.toHaveBeenCalled();
- });
- });
-
- describe('Extension Command Loading', () => {
- it('loads commands from active extensions', async () => {
- const userCommandsDir = getUserCommandsDir();
- const projectCommandsDir = getProjectCommandsDir(process.cwd());
- const extensionDir = path.join(
- process.cwd(),
- '.anus/extensions/test-ext',
- );
-
- mock({
- [userCommandsDir]: {
- 'user.toml': 'prompt = "User command"',
- },
- [projectCommandsDir]: {
- 'project.toml': 'prompt = "Project command"',
- },
- [extensionDir]: {
- 'anus-extension.json': JSON.stringify({
- name: 'test-ext',
- version: '1.0.0',
- }),
- commands: {
- 'ext.toml': 'prompt = "Extension command"',
- },
- },
- });
-
- const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
- getExtensions: vi.fn(() => [
- {
- name: 'test-ext',
- version: '1.0.0',
- isActive: true,
- path: extensionDir,
- },
- ]),
- } as unknown as Config;
- const loader = new FileCommandLoader(mockConfig);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(3);
- const commandNames = commands.map((cmd) => cmd.name);
- expect(commandNames).toEqual(['user', 'project', 'ext']);
-
- const extCommand = commands.find((cmd) => cmd.name === 'ext');
- expect(extCommand?.extensionName).toBe('test-ext');
- expect(extCommand?.description).toMatch(/^\[test-ext\]/);
- });
-
- it('extension commands have extensionName metadata for conflict resolution', async () => {
- const userCommandsDir = getUserCommandsDir();
- const projectCommandsDir = getProjectCommandsDir(process.cwd());
- const extensionDir = path.join(
- process.cwd(),
- '.anus/extensions/test-ext',
- );
-
- mock({
- [extensionDir]: {
- 'anus-extension.json': JSON.stringify({
- name: 'test-ext',
- version: '1.0.0',
- }),
- commands: {
- 'deploy.toml': 'prompt = "Extension deploy command"',
- },
- },
- [userCommandsDir]: {
- 'deploy.toml': 'prompt = "User deploy command"',
- },
- [projectCommandsDir]: {
- 'deploy.toml': 'prompt = "Project deploy command"',
- },
- });
-
- const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
- getExtensions: vi.fn(() => [
- {
- name: 'test-ext',
- version: '1.0.0',
- isActive: true,
- path: extensionDir,
- },
- ]),
- } as unknown as Config;
- const loader = new FileCommandLoader(mockConfig);
- const commands = await loader.loadCommands(signal);
-
- // Return all commands, even duplicates
- expect(commands).toHaveLength(3);
-
- expect(commands[0].name).toBe('deploy');
- expect(commands[0].extensionName).toBeUndefined();
- const result0 = await commands[0].action?.(
- createMockCommandContext({
- invocation: {
- raw: '/deploy',
- name: 'deploy',
- args: '',
- },
- }),
- '',
- );
- expect(result0?.type).toBe('submit_prompt');
- if (result0?.type === 'submit_prompt') {
- expect(result0.content).toBe('User deploy command');
- }
-
- expect(commands[1].name).toBe('deploy');
- expect(commands[1].extensionName).toBeUndefined();
- const result1 = await commands[1].action?.(
- createMockCommandContext({
- invocation: {
- raw: '/deploy',
- name: 'deploy',
- args: '',
- },
- }),
- '',
- );
- expect(result1?.type).toBe('submit_prompt');
- if (result1?.type === 'submit_prompt') {
- expect(result1.content).toBe('Project deploy command');
- }
-
- expect(commands[2].name).toBe('deploy');
- expect(commands[2].extensionName).toBe('test-ext');
- expect(commands[2].description).toMatch(/^\[test-ext\]/);
- const result2 = await commands[2].action?.(
- createMockCommandContext({
- invocation: {
- raw: '/deploy',
- name: 'deploy',
- args: '',
- },
- }),
- '',
- );
- expect(result2?.type).toBe('submit_prompt');
- if (result2?.type === 'submit_prompt') {
- expect(result2.content).toBe('Extension deploy command');
- }
- });
-
- it('only loads commands from active extensions', async () => {
- const extensionDir1 = path.join(
- process.cwd(),
- '.anus/extensions/active-ext',
- );
- const extensionDir2 = path.join(
- process.cwd(),
- '.anus/extensions/inactive-ext',
- );
-
- mock({
- [extensionDir1]: {
- 'anus-extension.json': JSON.stringify({
- name: 'active-ext',
- version: '1.0.0',
- }),
- commands: {
- 'active.toml': 'prompt = "Active extension command"',
- },
- },
- [extensionDir2]: {
- 'anus-extension.json': JSON.stringify({
- name: 'inactive-ext',
- version: '1.0.0',
- }),
- commands: {
- 'inactive.toml': 'prompt = "Inactive extension command"',
- },
- },
- });
-
- const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
- getExtensions: vi.fn(() => [
- {
- name: 'active-ext',
- version: '1.0.0',
- isActive: true,
- path: extensionDir1,
- },
- {
- name: 'inactive-ext',
- version: '1.0.0',
- isActive: false,
- path: extensionDir2,
- },
- ]),
- } as unknown as Config;
- const loader = new FileCommandLoader(mockConfig);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(1);
- expect(commands[0].name).toBe('active');
- expect(commands[0].extensionName).toBe('active-ext');
- expect(commands[0].description).toMatch(/^\[active-ext\]/);
- });
-
- it('handles missing extension commands directory gracefully', async () => {
- const extensionDir = path.join(
- process.cwd(),
- '.anus/extensions/no-commands',
- );
-
- mock({
- [extensionDir]: {
- 'anus-extension.json': JSON.stringify({
- name: 'no-commands',
- version: '1.0.0',
- }),
- // No commands directory
- },
- });
-
- const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
- getExtensions: vi.fn(() => [
- {
- name: 'no-commands',
- version: '1.0.0',
- isActive: true,
- path: extensionDir,
- },
- ]),
- } as unknown as Config;
- const loader = new FileCommandLoader(mockConfig);
- const commands = await loader.loadCommands(signal);
- expect(commands).toHaveLength(0);
- });
-
- it('handles nested command structure in extensions', async () => {
- const extensionDir = path.join(process.cwd(), '.anus/extensions/a');
-
- mock({
- [extensionDir]: {
- 'anus-extension.json': JSON.stringify({
- name: 'a',
- version: '1.0.0',
- }),
- commands: {
- b: {
- 'c.toml': 'prompt = "Nested command from extension a"',
- d: {
- 'e.toml': 'prompt = "Deeply nested command"',
- },
- },
- 'simple.toml': 'prompt = "Simple command"',
- },
- },
- });
-
- const mockConfig = {
- getProjectRoot: vi.fn(() => process.cwd()),
- getExtensions: vi.fn(() => [
- { name: 'a', version: '1.0.0', isActive: true, path: extensionDir },
- ]),
- } as unknown as Config;
- const loader = new FileCommandLoader(mockConfig);
- const commands = await loader.loadCommands(signal);
-
- expect(commands).toHaveLength(3);
-
- const commandNames = commands.map((cmd) => cmd.name).sort();
- expect(commandNames).toEqual(['b:c', 'b:d:e', 'simple']);
-
- const nestedCmd = commands.find((cmd) => cmd.name === 'b:c');
- expect(nestedCmd?.extensionName).toBe('a');
- expect(nestedCmd?.description).toMatch(/^\[a\]/);
- expect(nestedCmd).toBeDefined();
- const result = await nestedCmd!.action?.(
- createMockCommandContext({
- invocation: {
- raw: '/b:c',
- name: 'b:c',
- args: '',
- },
- }),
- '',
- );
- if (result?.type === 'submit_prompt') {
- expect(result.content).toBe('Nested command from extension a');
- } else {
- assert.fail('Incorrect action type');
- }
- });
- });
-
- describe('Argument Handling Integration (via ShellProcessor)', () => {
- it('correctly processes a command with {{args}}', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'shorthand.toml':
- 'prompt = "The user wants to: {{args}}"\ndescription = "Shorthand test"',
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- const commands = await loader.loadCommands(signal);
- const command = commands.find((c) => c.name === 'shorthand');
- expect(command).toBeDefined();
-
- const result = await command!.action?.(
- createMockCommandContext({
- invocation: {
- raw: '/shorthand do something cool',
- name: 'shorthand',
- args: 'do something cool',
- },
- }),
- 'do something cool',
- );
- expect(result?.type).toBe('submit_prompt');
- if (result?.type === 'submit_prompt') {
- expect(result.content).toBe('The user wants to: do something cool');
- }
- });
- });
-
- describe('Default Argument Processor Integration', () => {
- it('correctly processes a command without {{args}}', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'model_led.toml':
- 'prompt = "This is the instruction."\ndescription = "Default processor test"',
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- const commands = await loader.loadCommands(signal);
- const command = commands.find((c) => c.name === 'model_led');
- expect(command).toBeDefined();
-
- const result = await command!.action?.(
- createMockCommandContext({
- invocation: {
- raw: '/model_led 1.2.0 added "a feature"',
- name: 'model_led',
- args: '1.2.0 added "a feature"',
- },
- }),
- '1.2.0 added "a feature"',
- );
- expect(result?.type).toBe('submit_prompt');
- if (result?.type === 'submit_prompt') {
- const expectedContent =
- 'This is the instruction.\n\n/model_led 1.2.0 added "a feature"';
- expect(result.content).toBe(expectedContent);
- }
- });
- });
-
- describe('Shell Processor Integration', () => {
- it('instantiates ShellProcessor if {{args}} is present (even without shell trigger)', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'args_only.toml': `prompt = "Hello {{args}}"`,
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- await loader.loadCommands(signal);
-
- expect(ShellProcessor).toHaveBeenCalledWith('args_only');
- });
- it('instantiates ShellProcessor if the trigger is present', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'shell.toml': `prompt = "Run this: ${SHELL_INJECTION_TRIGGER}echo hello}"`,
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- await loader.loadCommands(signal);
-
- expect(ShellProcessor).toHaveBeenCalledWith('shell');
- });
-
- it('does not instantiate ShellProcessor if no triggers ({{args}} or !{}) are present', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'regular.toml': `prompt = "Just a regular prompt"`,
- },
- });
-
- const loader = new FileCommandLoader(null as unknown as Config);
- await loader.loadCommands(signal);
-
- expect(ShellProcessor).not.toHaveBeenCalled();
- });
-
- it('returns a "submit_prompt" action if shell processing succeeds', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'shell.toml': `prompt = "Run !{echo 'hello'}"`,
- },
- });
- mockShellProcess.mockResolvedValue('Run hello');
-
- const loader = new FileCommandLoader(null as unknown as Config);
- const commands = await loader.loadCommands(signal);
- const command = commands.find((c) => c.name === 'shell');
- expect(command).toBeDefined();
-
- const result = await command!.action!(
- createMockCommandContext({
- invocation: { raw: '/shell', name: 'shell', args: '' },
- }),
- '',
- );
-
- expect(result?.type).toBe('submit_prompt');
- if (result?.type === 'submit_prompt') {
- expect(result.content).toBe('Run hello');
- }
- });
-
- it('returns a "confirm_shell_commands" action if shell processing requires it', async () => {
- const userCommandsDir = getUserCommandsDir();
- const rawInvocation = '/shell rm -rf /';
- mock({
- [userCommandsDir]: {
- 'shell.toml': `prompt = "Run !{rm -rf /}"`,
- },
- });
-
- // Mock the processor to throw the specific error
- const error = new ConfirmationRequiredError('Confirmation needed', [
- 'rm -rf /',
- ]);
- mockShellProcess.mockRejectedValue(error);
-
- const loader = new FileCommandLoader(null as unknown as Config);
- const commands = await loader.loadCommands(signal);
- const command = commands.find((c) => c.name === 'shell');
- expect(command).toBeDefined();
-
- const result = await command!.action!(
- createMockCommandContext({
- invocation: { raw: rawInvocation, name: 'shell', args: 'rm -rf /' },
- }),
- 'rm -rf /',
- );
-
- expect(result?.type).toBe('confirm_shell_commands');
- if (result?.type === 'confirm_shell_commands') {
- expect(result.commandsToConfirm).toEqual(['rm -rf /']);
- expect(result.originalInvocation.raw).toBe(rawInvocation);
- }
- });
-
- it('re-throws other errors from the processor', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- 'shell.toml': `prompt = "Run !{something}"`,
- },
- });
-
- const genericError = new Error('Something else went wrong');
- mockShellProcess.mockRejectedValue(genericError);
-
- const loader = new FileCommandLoader(null as unknown as Config);
- const commands = await loader.loadCommands(signal);
- const command = commands.find((c) => c.name === 'shell');
- expect(command).toBeDefined();
-
- await expect(
- command!.action!(
- createMockCommandContext({
- invocation: { raw: '/shell', name: 'shell', args: '' },
- }),
- '',
- ),
- ).rejects.toThrow('Something else went wrong');
- });
- it('assembles the processor pipeline in the correct order (Shell -> Default)', async () => {
- const userCommandsDir = getUserCommandsDir();
- mock({
- [userCommandsDir]: {
- // This prompt uses !{} but NOT {{args}}, so both processors should be active.
- 'pipeline.toml': `
- prompt = "Shell says: ${SHELL_INJECTION_TRIGGER}echo foo}."
- `,
- },
- });
-
- const defaultProcessMock = vi
- .fn()
- .mockImplementation((p) => Promise.resolve(`${p}-default-processed`));
-
- mockShellProcess.mockImplementation((p) =>
- Promise.resolve(`${p}-shell-processed`),
- );
-
- vi.mocked(DefaultArgumentProcessor).mockImplementation(
- () =>
- ({
- process: defaultProcessMock,
- }) as unknown as DefaultArgumentProcessor,
- );
-
- const loader = new FileCommandLoader(null as unknown as Config);
- const commands = await loader.loadCommands(signal);
- const command = commands.find((c) => c.name === 'pipeline');
- expect(command).toBeDefined();
-
- const result = await command!.action!(
- createMockCommandContext({
- invocation: {
- raw: '/pipeline bar',
- name: 'pipeline',
- args: 'bar',
- },
- }),
- 'bar',
- );
-
- expect(mockShellProcess.mock.invocationCallOrder[0]).toBeLessThan(
- defaultProcessMock.mock.invocationCallOrder[0],
- );
-
- // Verify the flow of the prompt through the processors
- // 1. Shell processor runs first
- expect(mockShellProcess).toHaveBeenCalledWith(
- expect.stringContaining(SHELL_INJECTION_TRIGGER),
- expect.any(Object),
- );
- // 2. Default processor runs second
- expect(defaultProcessMock).toHaveBeenCalledWith(
- expect.stringContaining('-shell-processed'),
- expect.any(Object),
- );
-
- if (result?.type === 'submit_prompt') {
- expect(result.content).toContain('-shell-processed-default-processed');
- } else {
- assert.fail('Incorrect action type');
- }
- });
- });
-});
diff --git a/packages/cli/src/services/FileCommandLoader.ts b/packages/cli/src/services/FileCommandLoader.ts
deleted file mode 100644
index f12ba0f..0000000
--- a/packages/cli/src/services/FileCommandLoader.ts
+++ /dev/null
@@ -1,290 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { promises as fs } from 'fs';
-import path from 'path';
-import toml from '@iarna/toml';
-import { glob } from 'glob';
-import { z } from 'zod';
-import {
- Config,
- getProjectCommandsDir,
- getUserCommandsDir,
-} from '@anus-dev/anus-core';
-import { ICommandLoader } from './types.js';
-import {
- CommandContext,
- CommandKind,
- SlashCommand,
- SlashCommandActionReturn,
-} from '../ui/commands/types.js';
-import { DefaultArgumentProcessor } from './prompt-processors/argumentProcessor.js';
-import {
- IPromptProcessor,
- SHORTHAND_ARGS_PLACEHOLDER,
- SHELL_INJECTION_TRIGGER,
-} from './prompt-processors/types.js';
-import {
- ConfirmationRequiredError,
- ShellProcessor,
-} from './prompt-processors/shellProcessor.js';
-
-interface CommandDirectory {
- path: string;
- extensionName?: string;
-}
-
-/**
- * Defines the Zod schema for a command definition file. This serves as the
- * single source of truth for both validation and type inference.
- */
-const TomlCommandDefSchema = z.object({
- prompt: z.string({
- required_error: "The 'prompt' field is required.",
- invalid_type_error: "The 'prompt' field must be a string.",
- }),
- description: z.string().optional(),
-});
-
-/**
- * Discovers and loads custom slash commands from .toml files in both the
- * user's global config directory and the current project's directory.
- *
- * This loader is responsible for:
- * - Recursively scanning command directories.
- * - Parsing and validating TOML files.
- * - Adapting valid definitions into executable SlashCommand objects.
- * - Handling file system errors and malformed files gracefully.
- */
-export class FileCommandLoader implements ICommandLoader {
- private readonly projectRoot: string;
-
- constructor(private readonly config: Config | null) {
- this.projectRoot = config?.getProjectRoot() || process.cwd();
- }
-
- /**
- * Loads all commands from user, project, and extension directories.
- * Returns commands in order: user → project → extensions (alphabetically).
- *
- * Order is important for conflict resolution in CommandService:
- * - User/project commands (without extensionName) use "last wins" strategy
- * - Extension commands (with extensionName) get renamed if conflicts exist
- *
- * @param signal An AbortSignal to cancel the loading process.
- * @returns A promise that resolves to an array of all loaded SlashCommands.
- */
- async loadCommands(signal: AbortSignal): Promise {
- const allCommands: SlashCommand[] = [];
- const globOptions = {
- nodir: true,
- dot: true,
- signal,
- follow: true,
- };
-
- // Load commands from each directory
- const commandDirs = this.getCommandDirectories();
- for (const dirInfo of commandDirs) {
- try {
- const files = await glob('**/*.toml', {
- ...globOptions,
- cwd: dirInfo.path,
- });
-
- const commandPromises = files.map((file) =>
- this.parseAndAdaptFile(
- path.join(dirInfo.path, file),
- dirInfo.path,
- dirInfo.extensionName,
- ),
- );
-
- const commands = (await Promise.all(commandPromises)).filter(
- (cmd): cmd is SlashCommand => cmd !== null,
- );
-
- // Add all commands without deduplication
- allCommands.push(...commands);
- } catch (error) {
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
- console.error(
- `[FileCommandLoader] Error loading commands from ${dirInfo.path}:`,
- error,
- );
- }
- }
- }
-
- return allCommands;
- }
-
- /**
- * Get all command directories in order for loading.
- * User commands → Project commands → Extension commands
- * This order ensures extension commands can detect all conflicts.
- */
- private getCommandDirectories(): CommandDirectory[] {
- const dirs: CommandDirectory[] = [];
-
- // 1. User commands
- dirs.push({ path: getUserCommandsDir() });
-
- // 2. Project commands (override user commands)
- dirs.push({ path: getProjectCommandsDir(this.projectRoot) });
-
- // 3. Extension commands (processed last to detect all conflicts)
- if (this.config) {
- const activeExtensions = this.config
- .getExtensions()
- .filter((ext) => ext.isActive)
- .sort((a, b) => a.name.localeCompare(b.name)); // Sort alphabetically for deterministic loading
-
- const extensionCommandDirs = activeExtensions.map((ext) => ({
- path: path.join(ext.path, 'commands'),
- extensionName: ext.name,
- }));
-
- dirs.push(...extensionCommandDirs);
- }
-
- return dirs;
- }
-
- /**
- * Parses a single .toml file and transforms it into a SlashCommand object.
- * @param filePath The absolute path to the .toml file.
- * @param baseDir The root command directory for name calculation.
- * @param extensionName Optional extension name to prefix commands with.
- * @returns A promise resolving to a SlashCommand, or null if the file is invalid.
- */
- private async parseAndAdaptFile(
- filePath: string,
- baseDir: string,
- extensionName?: string,
- ): Promise {
- let fileContent: string;
- try {
- fileContent = await fs.readFile(filePath, 'utf-8');
- } catch (error: unknown) {
- console.error(
- `[FileCommandLoader] Failed to read file ${filePath}:`,
- error instanceof Error ? error.message : String(error),
- );
- return null;
- }
-
- let parsed: unknown;
- try {
- parsed = toml.parse(fileContent);
- } catch (error: unknown) {
- console.error(
- `[FileCommandLoader] Failed to parse TOML file ${filePath}:`,
- error instanceof Error ? error.message : String(error),
- );
- return null;
- }
-
- const validationResult = TomlCommandDefSchema.safeParse(parsed);
-
- if (!validationResult.success) {
- console.error(
- `[FileCommandLoader] Skipping invalid command file: ${filePath}. Validation errors:`,
- validationResult.error.flatten(),
- );
- return null;
- }
-
- const validDef = validationResult.data;
-
- const relativePathWithExt = path.relative(baseDir, filePath);
- const relativePath = relativePathWithExt.substring(
- 0,
- relativePathWithExt.length - 5, // length of '.toml'
- );
- const baseCommandName = relativePath
- .split(path.sep)
- // Sanitize each path segment to prevent ambiguity. Since ':' is our
- // namespace separator, we replace any literal colons in filenames
- // with underscores to avoid naming conflicts.
- .map((segment) => segment.replaceAll(':', '_'))
- .join(':');
-
- // Add extension name tag for extension commands
- const defaultDescription = `Custom command from ${path.basename(filePath)}`;
- let description = validDef.description || defaultDescription;
- if (extensionName) {
- description = `[${extensionName}] ${description}`;
- }
-
- const processors: IPromptProcessor[] = [];
- const usesArgs = validDef.prompt.includes(SHORTHAND_ARGS_PLACEHOLDER);
- const usesShellInjection = validDef.prompt.includes(
- SHELL_INJECTION_TRIGGER,
- );
-
- // Interpolation (Shell Execution and Argument Injection)
- // If the prompt uses either shell injection OR argument placeholders,
- // we must use the ShellProcessor.
- if (usesShellInjection || usesArgs) {
- processors.push(new ShellProcessor(baseCommandName));
- }
-
- // Default Argument Handling
- // If NO explicit argument injection ({{args}}) was used, we append the raw invocation.
- if (!usesArgs) {
- processors.push(new DefaultArgumentProcessor());
- }
-
- return {
- name: baseCommandName,
- description,
- kind: CommandKind.FILE,
- extensionName,
- action: async (
- context: CommandContext,
- _args: string,
- ): Promise => {
- if (!context.invocation) {
- console.error(
- `[FileCommandLoader] Critical error: Command '${baseCommandName}' was executed without invocation context.`,
- );
- return {
- type: 'submit_prompt',
- content: validDef.prompt, // Fallback to unprocessed prompt
- };
- }
-
- try {
- let processedPrompt = validDef.prompt;
- for (const processor of processors) {
- processedPrompt = await processor.process(processedPrompt, context);
- }
-
- return {
- type: 'submit_prompt',
- content: processedPrompt,
- };
- } catch (e) {
- // Check if it's our specific error type
- if (e instanceof ConfirmationRequiredError) {
- // Halt and request confirmation from the UI layer.
- return {
- type: 'confirm_shell_commands',
- commandsToConfirm: e.commandsToConfirm,
- originalInvocation: {
- raw: context.invocation.raw,
- },
- };
- }
- // Re-throw other errors to be handled by the global error handler.
- throw e;
- }
- },
- };
- }
-}
diff --git a/packages/cli/src/services/McpPromptLoader.ts b/packages/cli/src/services/McpPromptLoader.ts
deleted file mode 100644
index 27ed056..0000000
--- a/packages/cli/src/services/McpPromptLoader.ts
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import {
- Config,
- getErrorMessage,
- getMCPServerPrompts,
-} from '@anus-dev/anus-core';
-import {
- CommandContext,
- CommandKind,
- SlashCommand,
- SlashCommandActionReturn,
-} from '../ui/commands/types.js';
-import { ICommandLoader } from './types.js';
-import { PromptArgument } from '@modelcontextprotocol/sdk/types.js';
-
-/**
- * Discovers and loads executable slash commands from prompts exposed by
- * Model-Context-Protocol (MCP) servers.
- */
-export class McpPromptLoader implements ICommandLoader {
- constructor(private readonly config: Config | null) {}
-
- /**
- * Loads all available prompts from all configured MCP servers and adapts
- * them into executable SlashCommand objects.
- *
- * @param _signal An AbortSignal (unused for this synchronous loader).
- * @returns A promise that resolves to an array of loaded SlashCommands.
- */
- loadCommands(_signal: AbortSignal): Promise {
- const promptCommands: SlashCommand[] = [];
- if (!this.config) {
- return Promise.resolve([]);
- }
- const mcpServers = this.config.getMcpServers() || {};
- for (const serverName in mcpServers) {
- const prompts = getMCPServerPrompts(this.config, serverName) || [];
- for (const prompt of prompts) {
- const commandName = `${prompt.name}`;
- const newPromptCommand: SlashCommand = {
- name: commandName,
- description: prompt.description || `Invoke prompt ${prompt.name}`,
- kind: CommandKind.MCP_PROMPT,
- subCommands: [
- {
- name: 'help',
- description: 'Show help for this prompt',
- kind: CommandKind.MCP_PROMPT,
- action: async (): Promise => {
- if (!prompt.arguments || prompt.arguments.length === 0) {
- return {
- type: 'message',
- messageType: 'info',
- content: `Prompt "${prompt.name}" has no arguments.`,
- };
- }
-
- let helpMessage = `Arguments for "${prompt.name}":\n\n`;
- if (prompt.arguments && prompt.arguments.length > 0) {
- helpMessage += `You can provide arguments by name (e.g., --argName="value") or by position.\n\n`;
- helpMessage += `e.g., ${prompt.name} ${prompt.arguments?.map((_) => `"foo"`)} is equivalent to ${prompt.name} ${prompt.arguments?.map((arg) => `--${arg.name}="foo"`)}\n\n`;
- }
- for (const arg of prompt.arguments) {
- helpMessage += ` --${arg.name}\n`;
- if (arg.description) {
- helpMessage += ` ${arg.description}\n`;
- }
- helpMessage += ` (required: ${
- arg.required ? 'yes' : 'no'
- })\n\n`;
- }
- return {
- type: 'message',
- messageType: 'info',
- content: helpMessage,
- };
- },
- },
- ],
- action: async (
- context: CommandContext,
- args: string,
- ): Promise => {
- if (!this.config) {
- return {
- type: 'message',
- messageType: 'error',
- content: 'Config not loaded.',
- };
- }
-
- const promptInputs = this.parseArgs(args, prompt.arguments);
- if (promptInputs instanceof Error) {
- return {
- type: 'message',
- messageType: 'error',
- content: promptInputs.message,
- };
- }
-
- try {
- const mcpServers = this.config.getMcpServers() || {};
- const mcpServerConfig = mcpServers[serverName];
- if (!mcpServerConfig) {
- return {
- type: 'message',
- messageType: 'error',
- content: `MCP server config not found for '${serverName}'.`,
- };
- }
- const result = await prompt.invoke(promptInputs);
-
- if (result['error']) {
- return {
- type: 'message',
- messageType: 'error',
- content: `Error invoking prompt: ${result['error']}`,
- };
- }
-
- if (!result.messages?.[0]?.content?.['text']) {
- return {
- type: 'message',
- messageType: 'error',
- content:
- 'Received an empty or invalid prompt response from the server.',
- };
- }
-
- return {
- type: 'submit_prompt',
- content: JSON.stringify(result.messages[0].content.text),
- };
- } catch (error) {
- return {
- type: 'message',
- messageType: 'error',
- content: `Error: ${getErrorMessage(error)}`,
- };
- }
- },
- completion: async (_: CommandContext, partialArg: string) => {
- if (!prompt || !prompt.arguments) {
- return [];
- }
-
- const suggestions: string[] = [];
- const usedArgNames = new Set(
- (partialArg.match(/--([^=]+)/g) || []).map((s) => s.substring(2)),
- );
-
- for (const arg of prompt.arguments) {
- if (!usedArgNames.has(arg.name)) {
- suggestions.push(`--${arg.name}=""`);
- }
- }
-
- return suggestions;
- },
- };
- promptCommands.push(newPromptCommand);
- }
- }
- return Promise.resolve(promptCommands);
- }
-
- private parseArgs(
- userArgs: string,
- promptArgs: PromptArgument[] | undefined,
- ): Record | Error {
- const argValues: { [key: string]: string } = {};
- const promptInputs: Record = {};
-
- // arg parsing: --key="value" or --key=value
- const namedArgRegex = /--([^=]+)=(?:"((?:\\.|[^"\\])*)"|([^ ]*))/g;
- let match;
- const remainingArgs: string[] = [];
- let lastIndex = 0;
-
- while ((match = namedArgRegex.exec(userArgs)) !== null) {
- const key = match[1];
- const value = match[2] ?? match[3]; // Quoted or unquoted value
- argValues[key] = value;
- // Capture text between matches as potential positional args
- if (match.index > lastIndex) {
- remainingArgs.push(userArgs.substring(lastIndex, match.index).trim());
- }
- lastIndex = namedArgRegex.lastIndex;
- }
-
- // Capture any remaining text after the last named arg
- if (lastIndex < userArgs.length) {
- remainingArgs.push(userArgs.substring(lastIndex).trim());
- }
-
- const positionalArgs = remainingArgs.join(' ').split(/ +/);
-
- if (!promptArgs) {
- return promptInputs;
- }
- for (const arg of promptArgs) {
- if (argValues[arg.name]) {
- promptInputs[arg.name] = argValues[arg.name];
- }
- }
-
- const unfilledArgs = promptArgs.filter(
- (arg) => arg.required && !promptInputs[arg.name],
- );
-
- const missingArgs: string[] = [];
- for (let i = 0; i < unfilledArgs.length; i++) {
- if (positionalArgs.length > i && positionalArgs[i]) {
- promptInputs[unfilledArgs[i].name] = positionalArgs[i];
- } else {
- missingArgs.push(unfilledArgs[i].name);
- }
- }
-
- if (missingArgs.length > 0) {
- const missingArgNames = missingArgs.map((name) => `--${name}`).join(', ');
- return new Error(`Missing required argument(s): ${missingArgNames}`);
- }
- return promptInputs;
- }
-}
diff --git a/packages/cli/src/services/prompt-processors/argumentProcessor.test.ts b/packages/cli/src/services/prompt-processors/argumentProcessor.test.ts
deleted file mode 100644
index 194acf1..0000000
--- a/packages/cli/src/services/prompt-processors/argumentProcessor.test.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { DefaultArgumentProcessor } from './argumentProcessor.js';
-import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
-import { describe, it, expect } from 'vitest';
-
-describe('Argument Processors', () => {
- describe('DefaultArgumentProcessor', () => {
- const processor = new DefaultArgumentProcessor();
-
- it('should append the full command if args are provided', async () => {
- const prompt = 'Parse the command.';
- const context = createMockCommandContext({
- invocation: {
- raw: '/mycommand arg1 "arg two"',
- name: 'mycommand',
- args: 'arg1 "arg two"',
- },
- });
- const result = await processor.process(prompt, context);
- expect(result).toBe('Parse the command.\n\n/mycommand arg1 "arg two"');
- });
-
- it('should NOT append the full command if no args are provided', async () => {
- const prompt = 'Parse the command.';
- const context = createMockCommandContext({
- invocation: {
- raw: '/mycommand',
- name: 'mycommand',
- args: '',
- },
- });
- const result = await processor.process(prompt, context);
- expect(result).toBe('Parse the command.');
- });
- });
-});
diff --git a/packages/cli/src/services/prompt-processors/argumentProcessor.ts b/packages/cli/src/services/prompt-processors/argumentProcessor.ts
deleted file mode 100644
index e96f2ea..0000000
--- a/packages/cli/src/services/prompt-processors/argumentProcessor.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { IPromptProcessor } from './types.js';
-import { CommandContext } from '../../ui/commands/types.js';
-
-/**
- * Appends the user's full command invocation to the prompt if arguments are
- * provided, allowing the model to perform its own argument parsing.
- *
- * This processor is only used if the prompt does NOT contain {{args}}.
- */
-export class DefaultArgumentProcessor implements IPromptProcessor {
- async process(prompt: string, context: CommandContext): Promise {
- if (context.invocation!.args) {
- return `${prompt}\n\n${context.invocation!.raw}`;
- }
- return prompt;
- }
-}
diff --git a/packages/cli/src/services/prompt-processors/shellProcessor.test.ts b/packages/cli/src/services/prompt-processors/shellProcessor.test.ts
deleted file mode 100644
index 318af8d..0000000
--- a/packages/cli/src/services/prompt-processors/shellProcessor.test.ts
+++ /dev/null
@@ -1,752 +0,0 @@
-/**
- * @license
- * Copyright 2025 Google LLC
- * Modifications Copyright 2025 ANUS Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
-import { ConfirmationRequiredError, ShellProcessor } from './shellProcessor.js';
-import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
-import { CommandContext } from '../../ui/commands/types.js';
-import { ApprovalMode, Config } from '@anus-dev/anus-core';
-import os from 'os';
-import { quote } from 'shell-quote';
-
-// Helper function to determine the expected escaped string based on the current OS,
-// mirroring the logic in the actual `escapeShellArg` implementation. This makes
-// our tests robust and platform-agnostic.
-function getExpectedEscapedArgForPlatform(arg: string): string {
- if (os.platform() === 'win32') {
- const comSpec = (process.env['ComSpec'] || 'cmd.exe').toLowerCase();
- const isPowerShell =
- comSpec.endsWith('powershell.exe') || comSpec.endsWith('pwsh.exe');
-
- if (isPowerShell) {
- return `'${arg.replace(/'/g, "''")}'`;
- } else {
- return `"${arg.replace(/"/g, '""')}"`;
- }
- } else {
- return quote([arg]);
- }
-}
-
-const mockCheckCommandPermissions = vi.hoisted(() => vi.fn());
-const mockShellExecute = vi.hoisted(() => vi.fn());
-
-vi.mock('@anus-dev/anus-core', async (importOriginal) => {
- const original = await importOriginal