From 38e8e5f3302bfa73c1dc95bd42d075fcb69b6f43 Mon Sep 17 00:00:00 2001 From: Bob Date: Wed, 18 Feb 2026 00:00:33 +0100 Subject: [PATCH] chore: add npm-first docs, lint/format tooling, and CI workflows --- .editorconfig | 12 + .github/workflows/ci.yml | 36 + .github/workflows/release.yml | 39 + .prettierignore | 3 + .prettierrc | 6 + AGENTS.md | 21 +- CONTRIBUTING.md | 15 + README.md | 135 +-- docs/2026-02-17-agent-registry.md | 51 ++ docs/2026-02-17-architecture.md | 69 ++ docs/2026-02-17-session-management.md | 83 ++ eslint.config.js | 36 + package-lock.json | 1203 ++++++++++++++++++++++++- package.json | 12 +- references/acp-sdk-example-client.ts | 8 +- references/openclaw-acp-client.ts | 65 +- src/cli.ts | 53 +- src/client.ts | 13 +- src/permissions.ts | 9 +- src/session.ts | 10 +- src/types.ts | 6 +- 21 files changed, 1685 insertions(+), 200 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 CONTRIBUTING.md create mode 100644 docs/2026-02-17-agent-registry.md create mode 100644 docs/2026-02-17-architecture.md create mode 100644 docs/2026-02-17-session-management.md create mode 100644 eslint.config.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1014ba7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bf09867 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + checks: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npm run typecheck + + - name: Lint + run: npm run lint + + - name: Format check + run: npm run format:check + + - name: Build + run: npm run build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..67cc4e2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +name: Release + +on: + push: + tags: + - "v*" + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Publish to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..e728995 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +dist/ +node_modules/ +*.tgz diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..4ecc68d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 88, + "singleQuote": false, + "trailingComma": "all", + "semi": true +} diff --git a/AGENTS.md b/AGENTS.md index 9917210..ec81663 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -63,13 +63,14 @@ Built-in friendly names map to commands: ```ts const AGENT_REGISTRY: Record = { - codex: 'npx @zed-industries/codex-acp', - claude: 'npx @zed-industries/claude-agent-acp', - gemini: 'gemini', + codex: "npx @zed-industries/codex-acp", + claude: "npx @zed-industries/claude-agent-acp", + gemini: "gemini", }; ``` Rules: + - Known names resolve automatically. - Unknown names are treated as raw commands. - Escape hatch: `--agent ` sets a raw command explicitly. @@ -140,14 +141,14 @@ Refactored the auth module to use async/await. All 42 tests passing. ## Exit Codes -| Code | Meaning | -|------|---------| -| 0 | Success | -| 1 | Agent/protocol error | -| 2 | CLI usage error | -| 3 | Timeout | +| Code | Meaning | +| ---- | ---------------------------------------- | +| 0 | Success | +| 1 | Agent/protocol error | +| 2 | CLI usage error | +| 3 | Timeout | | 4 | Permission denied (all options rejected) | -| 130 | Interrupted (Ctrl+C) | +| 130 | Interrupted (Ctrl+C) | ## Tech Stack diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cb887ce --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# Contributing + +1. Fork the repository. +2. Create a branch for your change. +3. Make your changes with focused commits. +4. Run checks locally: + +```bash +npm run typecheck +npm run lint +npm run format:check +npm run build +``` + +5. Open a pull request to `main` with a clear summary. diff --git a/README.md b/README.md index b057bc6..bb213da 100644 --- a/README.md +++ b/README.md @@ -1,139 +1,60 @@ # acpx -Headless CLI client for the [Agent Client Protocol (ACP)](https://agentclientprotocol.com) — talk to coding agents from the command line. +Headless CLI client for the [Agent Client Protocol (ACP)](https://agentclientprotocol.com). -```bash -# Conversational prompt (persistent session, auto-resume by agent+cwd) -acpx codex "fix the tests" - -# Explicit prompt verb (same behavior) -acpx codex prompt "fix the tests" - -# One-shot execution (no saved session) -acpx codex exec "what does this repo do" - -# Named session -acpx codex -s backend "fix the API" - -# Session management -acpx codex sessions -acpx codex sessions close -acpx codex sessions close backend -``` - -## Why? - -ACP adapters exist for every major coding agent ([Codex](https://github.com/zed-industries/codex-acp), [Claude Code](https://github.com/zed-industries/claude-code-acp), [Gemini CLI](https://github.com/google-gemini/gemini-cli), etc.) but every ACP client is a GUI app or editor plugin. - -`acpx` is the missing piece: a simple CLI that lets **agents talk to agents** (or humans script agents) over structured ACP instead of scraping terminal output. +`acpx` is built for scriptable, session-aware agent usage from the terminal. ## Install ```bash -npm install -g acpx -# or -npx acpx codex "hello" +npm i -g acpx ``` -### Prerequisites +`acpx` manages persistent sessions, so prefer a global install. Avoid `npx acpx ...` for normal use. -You need an ACP-compatible agent installed: +## Agent prerequisites + +Install at least one ACP-compatible agent adapter: ```bash -# Codex ACP adapter npm install -g @zed-industries/codex-acp - -# Claude ACP adapter npm install -g @zed-industries/claude-agent-acp - -# Gemini CLI (native ACP support) npm install -g @google/gemini-cli ``` -## Usage - -### Command grammar +## Core usage ```bash -acpx [prompt] -acpx exec -acpx sessions [list|close] +acpx codex 'fix the tests' # implicit prompt, auto-resume session +acpx codex prompt 'fix the tests' # same, explicit +acpx codex exec 'what does this repo do' # one-shot, no session +acpx codex -s backend 'fix the API' # named session +acpx codex sessions # list sessions +acpx claude 'refactor auth' # different agent ``` -`prompt` is the default verb, so `acpx codex "..."` and `acpx codex prompt "..."` are equivalent. +## Session behavior -### Built-in agent registry +- Default mode is conversational: prompts use a saved session scoped to `(agent command, cwd)`. +- `-s ` switches to a named session scoped to `(agent command, cwd, name)`. +- `exec` is fire-and-forget: temporary session, prompt once, then discard. -Friendly names are resolved automatically: +Session files are stored in `~/.acpx/sessions/`. -- `codex` -> `npx @zed-industries/codex-acp` -- `claude` -> `npx @zed-industries/claude-agent-acp` -- `gemini` -> `gemini` +## Built-in agents and custom servers -Unknown agent names are treated as raw commands. You can also use the explicit escape hatch: +Built-ins: + +- `codex` +- `claude` +- `gemini` + +Use `--agent` as an escape hatch for custom ACP servers: ```bash -acpx --agent ./my-custom-server "do something" +acpx --agent ./my-custom-acp-server 'do something' ``` -### Session behavior - -- `prompt` always uses a saved session. -- Sessions auto-resume by `(agent command, cwd)`. -- `-s, --session ` uses a named session for that `(agent command, cwd)`. -- `exec` is fire-and-forget (temporary session, not saved). - -Examples: - -```bash -acpx codex "fix the tests" -acpx codex -s backend "fix the API" -acpx claude "refactor auth" -acpx gemini "add logging" -``` - -### Default agent shortcuts - -If agent is omitted, default agent is `codex`: - -```bash -acpx prompt "fix tests" -acpx exec "summarize this repo" -acpx sessions -``` - -### Global options (before agent name) - -```text ---agent Raw ACP agent command (escape hatch) ---cwd Working directory (default: .) ---approve-all Auto-approve all permission requests ---approve-reads Auto-approve reads/searches, prompt for writes ---deny-all Deny all permission requests ---format Output format: text (default), json, quiet ---timeout Maximum time to wait for agent response ---verbose Enable debug output on stderr -``` - -### Output formats - -| Format | Flag | Description | -|--------|------|-------------| -| text | `--format text` | Human-readable streaming (default) | -| json | `--format json` | Structured ndjson for machines | -| quiet | `--format quiet` | Final text output only | - -## How it works - -``` -┌─────────┐ stdio/ndjson ┌──────────────┐ wraps ┌─────────┐ -│ acpx │ ◄──────────────────► │ ACP adapter │ ◄───────────► │ Agent │ -│ (client)│ ACP protocol │ (codex-acp) │ │ (Codex) │ -└─────────┘ └──────────────┘ └─────────┘ -``` - -acpx spawns the ACP adapter as a child process, communicates over JSON-RPC/ndjson over stdio, and streams structured events (tool calls, text, permissions). - ## License Apache-2.0 diff --git a/docs/2026-02-17-agent-registry.md b/docs/2026-02-17-agent-registry.md new file mode 100644 index 0000000..10b440e --- /dev/null +++ b/docs/2026-02-17-agent-registry.md @@ -0,0 +1,51 @@ +--- +title: acpx Agent Registry +description: Built-in agent mappings, name resolution rules, and custom adapter usage with --agent. +author: Bob +date: 2026-02-17 +--- + +## Built-in registry + +`src/agent-registry.ts` defines friendly names: + +- `codex -> npx @zed-industries/codex-acp` +- `claude -> npx @zed-industries/claude-agent-acp` +- `gemini -> gemini` + +Default agent is `codex`. + +## Resolution behavior + +When you run `acpx ...`: + +1. agent token is normalized (trim + lowercase) +2. if it matches a built-in key, `acpx` uses the mapped command +3. if it does not match, `acpx` treats it as a raw command + +This means custom names work without any registry file edits. + +## `--agent` escape hatch + +`--agent ` forces a raw adapter command and bypasses positional agent resolution. + +Example: + +```bash +acpx --agent ./my-custom-acp-server 'summarize this repo' +``` + +Rules: + +- do not combine a positional agent with `--agent` +- the command string is parsed into executable + args before spawn +- the chosen command is what session scoping uses + +## Practical guidance + +Use built-ins for common adapters (`codex`, `claude`, `gemini`). +Use `--agent` when you need: + +- local development adapters +- pinned binaries/scripts +- non-standard ACP servers diff --git a/docs/2026-02-17-architecture.md b/docs/2026-02-17-architecture.md new file mode 100644 index 0000000..a0fb1ad --- /dev/null +++ b/docs/2026-02-17-architecture.md @@ -0,0 +1,69 @@ +--- +title: acpx Architecture +description: Internal architecture and runtime flow from CLI command to ACP session updates. +author: Bob +date: 2026-02-17 +--- + +## Overview + +`acpx` is a CLI client that speaks ACP over stdio. + +Data path: + +`CLI command -> AcpClient -> ndjson/stdio -> ACP adapter -> coding agent` + +The CLI never scrapes terminal text from an interactive UI. It talks structured ACP JSON-RPC messages directly. + +## Core components + +- `src/cli.ts`: command grammar, flags, output mode selection, and top-level command handling. +- `src/client.ts`: ACP transport and protocol methods. Spawns the adapter process and connects with `ClientSideConnection` + `ndJsonStream`. +- `src/session.ts`: session persistence, resume/create logic, timeout/interrupt handling, and lifecycle cleanup. +- `src/permissions.ts`: permission policy (`approve-all`, `approve-reads`, `deny-all`) and interactive fallback. +- `src/output.ts`: streaming text/json/quiet output formatters. + +## Protocol flow + +Typical prompt flow: + +1. `initialize` +2. `newSession` or `loadSession` +3. `prompt` +4. stream `sessionUpdate` notifications until done + +Details: + +- `initialize` advertises client capabilities (`fs.readTextFile`, `fs.writeTextFile`, `terminal`). +- If a saved session exists and agent supports it, `loadSession` is attempted. +- If load fails with not-found style errors, `acpx` falls back to `newSession`. +- Prompt responses and notifications are streamed through the active formatter. + +## Session persistence + +Session metadata is stored in `~/.acpx/sessions/*.json`. + +Each record includes: + +- stable file/session id +- ACP session id +- agent command +- cwd +- optional named session +- timestamps (`createdAt`, `lastUsedAt`) +- adapter process pid (when known) +- protocol/version capabilities snapshot + +This lets `acpx` resume conversational context by default. + +## Permission handling + +Permission requests come in through ACP `requestPermission` callbacks. + +Modes: + +- `approve-all`: auto-approve first allow option +- `approve-reads`: auto-approve read/search; prompt for others +- `deny-all`: reject when possible + +`acpx` tracks permission stats (requested/approved/denied/cancelled) and uses them for exit-code behavior. diff --git a/docs/2026-02-17-session-management.md b/docs/2026-02-17-session-management.md new file mode 100644 index 0000000..ec9fae8 --- /dev/null +++ b/docs/2026-02-17-session-management.md @@ -0,0 +1,83 @@ +--- +title: acpx Session Management +description: How acpx resumes, names, stores, and closes sessions including pid tracking and subprocess lifecycle. +author: Bob +date: 2026-02-17 +--- + +## Session model + +`acpx` is conversational by default. + +Session lookup is scoped by: + +- agent command +- cwd +- optional session name (`-s `) + +No `-s` means the default cwd session for that agent command. + +## Auto-resume behavior + +For prompt commands: + +1. `findSession` searches stored records by `(agentCommand, cwd, name?)`. +2. If no record exists, `createSession` creates ACP session + record. +3. `sendSession` starts a fresh adapter process and tries `loadSession`. +4. If load is unsupported or fails with known not-found/invalid errors, it falls back to `newSession`. +5. After prompt completes, record metadata is updated and re-written. + +## Named sessions + +`-s backend` creates a parallel conversation stream for the same agent and cwd. + +Example: + +- default session: `acpx codex 'fix tests'` +- named session: `acpx codex -s backend 'fix API'` + +Both can coexist because names are part of the scope key. + +## Session files + +Stored under `~/.acpx/sessions/` as JSON files. + +Record fields include: + +- `id` +- `sessionId` +- `agentCommand` +- `cwd` +- `name` (optional) +- `createdAt`, `lastUsedAt` +- `pid` (adapter process pid, optional) +- `protocolVersion`, `agentCapabilities` (optional) + +Writes are done via temp file + rename for safer updates. + +## loadSession protocol flow + +Resume path in `sendSession`: + +1. start ACP client process +2. initialize protocol +3. `loadSession(sessionId, cwd, mcpServers: [])` +4. suppress replayed updates during load +5. wait for session-update drain +6. send new prompt + +If resume fails with a fallback-eligible error, `newSession` is used and stored `sessionId` is replaced. + +## PID tracking and process lifecycle + +`acpx` stores the adapter pid in each session record to help with cleanup and diagnostics. + +Lifecycle behavior: + +- each command launches a new adapter subprocess +- records track pid of the latest process used +- `closeSession` tries to terminate the stored pid if still alive and likely matches expected command +- process termination uses `SIGTERM` then `SIGKILL` fallback +- signal handling (`SIGINT`, `SIGTERM`) closes client resources before exit + +This keeps session files and local processes in sync while remaining robust to stale pids. diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..b8e99bd --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,36 @@ +import js from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { + ignores: ["dist/**", "node_modules/**", "*.tgz"], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ["src/**/*.ts"], + languageOptions: { + globals: { + ...globals.node, + }, + }, + rules: { + "@typescript-eslint/no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], + "@typescript-eslint/consistent-type-imports": [ + "error", + { + prefer: "type-imports", + fixStyle: "inline-type-imports", + }, + ], + "prefer-promise-reject-errors": "off", + }, + }, +); diff --git a/package-lock.json b/package-lock.json index bf84587..5de36f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,10 +16,15 @@ "acpx": "dist/cli.js" }, "devDependencies": { + "@eslint/js": "^10.0.1", "@types/node": "^22.0.0", + "eslint": "^10.0.0", + "globals": "^17.3.0", + "prettier": "^3.8.1", "tsup": "^8.0.0", "tsx": "^4.0.0", - "typescript": "^5.7.0" + "typescript": "^5.7.0", + "typescript-eslint": "^8.56.0" }, "engines": { "node": ">=18" @@ -476,6 +481,196 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/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" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.1.tgz", + "integrity": "sha512-uVSdg/V4dfQmTjJzR0szNczjOH/J+FyUMMjYtr07xFRXR7EDf9i1qdxrD0VusZH9knj1/ecxzCQQxyic5NzAiA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.1", + "debug": "^4.3.1", + "minimatch": "^10.1.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz", + "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz", + "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.1.tgz", + "integrity": "sha512-P9cq2dpr+LU8j3qbLygLcSZrl2/ds/pUpfnHNNuk5HW7mnngHs+6WSq5C9mO3rqRX8A1poxqLTC9cu0KOyJlBg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz", + "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "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/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -865,6 +1060,13 @@ "win32" ] }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -872,6 +1074,13 @@ "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/node": { "version": "22.19.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", @@ -882,6 +1091,269 @@ "undici-types": "~6.21.0" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", + "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/type-utils": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.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.56.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.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.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", + "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", + "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.0", + "@typescript-eslint/types": "^8.56.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", + "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.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.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", + "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", + "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.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", + "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", + "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", + "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.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", + "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.56.0", + "@typescript-eslint/tsconfig-utils": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.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/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@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/utils": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", + "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", + "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -895,6 +1367,33 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -902,6 +1401,32 @@ "dev": true, "license": "MIT" }, + "node_modules/balanced-match": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.2.tgz", + "integrity": "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jackspeak": "^4.2.3" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", + "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/bundle-require": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", @@ -970,6 +1495,21 @@ "node": "^14.18.0 || >=16.10.0" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -988,6 +1528,13 @@ } } }, + "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/esbuild": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", @@ -1030,6 +1577,192 @@ "@esbuild/win32-x64": "0.27.3" } }, + "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": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.0.tgz", + "integrity": "sha512-O0piBKY36YSJhlFSG8p9VUdPV/SxxS4FYDWVpr/9GJuMaepzwlf4J8I4ov1b+ySQfDTPhc3DtLaxcT1fN0yqCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.0", + "@eslint/config-helpers": "^0.5.2", + "@eslint/core": "^1.1.0", + "@eslint/plugin-kit": "^0.6.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.0", + "eslint-visitor-keys": "^5.0.0", + "espree": "^11.1.0", + "esquery": "^1.7.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", + "minimatch": "^10.1.1", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.0.tgz", + "integrity": "sha512-CkWE42hOJsNj9FJRaoMX9waUFYhqY4jmyLFdAdzZr6VaCg3ynLYx4WnOdkaIifGfH4gsUcBTn4OZbHXkpLD0FQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", + "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.0.tgz", + "integrity": "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1048,6 +1781,36 @@ } } }, + "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/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/fix-dts-default-cjs-exports": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", @@ -1060,6 +1823,27 @@ "rollup": "^4.34.8" } }, + "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/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1088,6 +1872,98 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", + "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/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-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/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/joycon": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", @@ -1098,6 +1974,51 @@ "node": ">=10" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/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/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/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -1128,6 +2049,22 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -1138,6 +2075,22 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/minimatch": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.1.tgz", + "integrity": "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mlly": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", @@ -1170,6 +2123,13 @@ "thenify-all": "^1.0.0" } }, + "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/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1180,6 +2140,76 @@ "node": ">=0.10.0" } }, + "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/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/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -1272,6 +2302,42 @@ } } }, + "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.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -1351,6 +2417,42 @@ "fsevents": "~2.3.2" } }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -1451,6 +2553,19 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -1531,6 +2646,19 @@ "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/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -1545,6 +2673,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.0.tgz", + "integrity": "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.56.0", + "@typescript-eslint/parser": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/utils": "8.56.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/ufo": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", @@ -1559,6 +2711,55 @@ "dev": true, "license": "MIT" }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", diff --git a/package.json b/package.json index 143265b..113975b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,10 @@ "build": "tsup src/cli.ts --format esm --dts --clean", "prepack": "npm run build", "dev": "tsx src/cli.ts", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint src --max-warnings=0", + "format": "prettier --write .", + "format:check": "prettier --check ." }, "keywords": [ "acp", @@ -40,9 +43,14 @@ "commander": "^13.0.0" }, "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^22.0.0", + "eslint": "^10.0.0", + "globals": "^17.3.0", + "prettier": "^3.8.1", "tsup": "^8.0.0", "tsx": "^4.0.0", "typescript": "^5.7.0", - "@types/node": "^22.0.0" + "typescript-eslint": "^8.56.0" } } diff --git a/references/acp-sdk-example-client.ts b/references/acp-sdk-example-client.ts index d4418e9..08c4f62 100644 --- a/references/acp-sdk-example-client.ts +++ b/references/acp-sdk-example-client.ts @@ -110,9 +110,7 @@ async function main() { // Create streams to communicate with the agent const input = Writable.toWeb(agentProcess.stdin!); - const output = Readable.toWeb( - agentProcess.stdout!, - ) as ReadableStream; + const output = Readable.toWeb(agentProcess.stdout!) as ReadableStream; // Create the client connection const client = new ExampleClient(); @@ -131,9 +129,7 @@ async function main() { }, }); - console.log( - `✅ Connected to agent (protocol v${initResult.protocolVersion})`, - ); + console.log(`✅ Connected to agent (protocol v${initResult.protocolVersion})`); // Create a new session const sessionResult = await connection.newSession({ diff --git a/references/openclaw-acp-client.ts b/references/openclaw-acp-client.ts index 80cbda6..634c4c6 100644 --- a/references/openclaw-acp-client.ts +++ b/references/openclaw-acp-client.ts @@ -69,14 +69,19 @@ function resolveToolKindForPermission( params: RequestPermissionRequest, toolName: string | undefined, ): string | undefined { - const toolCall = params.toolCall as unknown as { kind?: unknown; title?: unknown } | undefined; - const kindRaw = typeof toolCall?.kind === "string" ? toolCall.kind.trim().toLowerCase() : ""; + const toolCall = params.toolCall as unknown as + | { kind?: unknown; title?: unknown } + | undefined; + const kindRaw = + typeof toolCall?.kind === "string" ? toolCall.kind.trim().toLowerCase() : ""; if (kindRaw) { return kindRaw; } const name = toolName ?? - parseToolNameFromTitle(typeof toolCall?.title === "string" ? toolCall.title : undefined); + parseToolNameFromTitle( + typeof toolCall?.title === "string" ? toolCall.title : undefined, + ); if (!name) { return undefined; } @@ -98,7 +103,11 @@ function resolveToolKindForPermission( if (normalized.includes("fetch") || normalized.includes("http")) { return "fetch"; } - if (normalized.includes("write") || normalized.includes("edit") || normalized.includes("patch")) { + if ( + normalized.includes("write") || + normalized.includes("edit") || + normalized.includes("patch") + ) { return "edit"; } if (normalized.includes("delete") || normalized.includes("remove")) { @@ -107,19 +116,30 @@ function resolveToolKindForPermission( if (normalized.includes("move") || normalized.includes("rename")) { return "move"; } - if (normalized.includes("exec") || normalized.includes("run") || normalized.includes("bash")) { + if ( + normalized.includes("exec") || + normalized.includes("run") || + normalized.includes("bash") + ) { return "execute"; } return "other"; } -function resolveToolNameForPermission(params: RequestPermissionRequest): string | undefined { +function resolveToolNameForPermission( + params: RequestPermissionRequest, +): string | undefined { const toolCall = params.toolCall; const toolMeta = asRecord(toolCall?._meta); const rawInput = asRecord(toolCall?.rawInput); const fromMeta = readFirstStringValue(toolMeta, ["toolName", "tool_name", "name"]); - const fromRawInput = readFirstStringValue(rawInput, ["tool", "toolName", "tool_name", "name"]); + const fromRawInput = readFirstStringValue(rawInput, [ + "tool", + "toolName", + "tool_name", + "name", + ]); const fromTitle = parseToolNameFromTitle(toolCall?.title); return normalizeToolName(fromMeta ?? fromRawInput ?? fromTitle ?? ""); } @@ -145,9 +165,14 @@ function cancelledPermission(): RequestPermissionResponse { return { outcome: { outcome: "cancelled" } }; } -function promptUserPermission(toolName: string | undefined, toolTitle?: string): Promise { +function promptUserPermission( + toolName: string | undefined, + toolTitle?: string, +): Promise { if (!process.stdin.isTTY || !process.stderr.isTTY) { - console.error(`[permission denied] ${toolName ?? "unknown"}: non-interactive terminal`); + console.error( + `[permission denied] ${toolName ?? "unknown"}: non-interactive terminal`, + ); return Promise.resolve(false); } return new Promise((resolve) => { @@ -179,7 +204,9 @@ function promptUserPermission(toolName: string | undefined, toolTitle?: string): : (toolName ?? "unknown tool"); rl.question(`\n[permission] Allow "${label}"? (y/N) `, (answer) => { const approved = answer.trim().toLowerCase() === "y"; - console.error(`[permission ${approved ? "approved" : "denied"}] ${toolName ?? "unknown"}`); + console.error( + `[permission ${approved ? "approved" : "denied"}] ${toolName ?? "unknown"}`, + ); finish(approved); }); }); @@ -317,17 +344,23 @@ function printSessionUpdate(notification: SessionNotification): void { } } -export async function createAcpClient(opts: AcpClientOptions = {}): Promise { +export async function createAcpClient( + opts: AcpClientOptions = {}, +): Promise { const cwd = opts.cwd ?? process.cwd(); const verbose = Boolean(opts.verbose); - const log = verbose ? (msg: string) => console.error(`[acp-client] ${msg}`) : () => {}; + const log = verbose + ? (msg: string) => console.error(`[acp-client] ${msg}`) + : () => {}; ensureOpenClawCliOnPath(); const serverArgs = buildServerArgs(opts); const entryPath = resolveSelfEntryPath(); - const serverCommand = opts.serverCommand ?? (entryPath ? process.execPath : "openclaw"); - const effectiveArgs = opts.serverCommand || !entryPath ? serverArgs : [entryPath, ...serverArgs]; + const serverCommand = + opts.serverCommand ?? (entryPath ? process.execPath : "openclaw"); + const effectiveArgs = + opts.serverCommand || !entryPath ? serverArgs : [entryPath, ...serverArgs]; log(`spawning: ${serverCommand} ${effectiveArgs.join(" ")}`); @@ -379,7 +412,9 @@ export async function createAcpClient(opts: AcpClientOptions = {}): Promise { +export async function runAcpClientInteractive( + opts: AcpClientOptions = {}, +): Promise { const { client, agent, sessionId } = await createAcpClient(opts); const rl = readline.createInterface({ diff --git a/src/cli.ts b/src/cli.ts index fca95c0..96ec720 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -49,7 +49,7 @@ const TOP_LEVEL_VERBS = new Set(["prompt", "exec", "sessions", "help"]); function parseOutputFormat(value: string): OutputFormat { if (!OUTPUT_FORMATS.includes(value as OutputFormat)) { throw new InvalidArgumentError( - `Invalid format \"${value}\". Expected one of: ${OUTPUT_FORMATS.join(", ")}`, + `Invalid format "${value}". Expected one of: ${OUTPUT_FORMATS.join(", ")}`, ); } return value as OutputFormat; @@ -207,10 +207,7 @@ function resolveAgentInvocation( }; } -function printSessionsByFormat( - sessions: SessionRecord[], - format: OutputFormat, -): void { +function printSessionsByFormat(sessions: SessionRecord[], format: OutputFormat): void { if (format === "json") { process.stdout.write(`${JSON.stringify(sessions)}\n`); return; @@ -235,10 +232,7 @@ function printSessionsByFormat( } } -function printClosedSessionByFormat( - record: SessionRecord, - format: OutputFormat, -): void { +function printClosedSessionByFormat(record: SessionRecord, format: OutputFormat): void { if (format === "json") { process.stdout.write( `${JSON.stringify({ @@ -287,7 +281,7 @@ async function handlePrompt( }); if (globalFlags.verbose) { - const scope = flags.session ? `named session \"${flags.session}\"` : "cwd session"; + const scope = flags.session ? `named session "${flags.session}"` : "cwd session"; process.stderr.write(`[acpx] created ${scope}: ${record.id}\n`); } } @@ -361,13 +355,11 @@ async function handleSessionsClose( if (!record) { if (sessionName) { throw new Error( - `No named session \"${sessionName}\" for cwd ${agent.cwd} and agent ${agent.agentName}`, + `No named session "${sessionName}" for cwd ${agent.cwd} and agent ${agent.agentName}`, ); } - throw new Error( - `No cwd session for ${agent.cwd} and agent ${agent.agentName}`, - ); + throw new Error(`No cwd session for ${agent.cwd} and agent ${agent.agentName}`); } const closed = await closeSession(record.id); @@ -559,30 +551,31 @@ async function main(): Promise { registerAgentCommand(program, scan.token); } - program - .argument("[prompt...]", "Prompt text") - .action(async function (this: Command, promptParts: string[]) { - if (promptParts.length === 0 && process.stdin.isTTY) { - this.outputHelp(); - return; - } + program.argument("[prompt...]", "Prompt text").action(async function ( + this: Command, + promptParts: string[], + ) { + if (promptParts.length === 0 && process.stdin.isTTY) { + this.outputHelp(); + return; + } - await handlePrompt(undefined, promptParts, {}, this); - }); + await handlePrompt(undefined, promptParts, {}, this); + }); program.addHelpText( "after", ` Examples: - acpx codex \"fix the tests\" - acpx codex prompt \"fix the tests\" - acpx codex exec \"what does this repo do\" - acpx codex -s backend \"fix the API\" + acpx codex "fix the tests" + acpx codex prompt "fix the tests" + acpx codex exec "what does this repo do" + acpx codex -s backend "fix the API" acpx codex sessions acpx codex sessions close backend - acpx claude \"refactor auth\" - acpx gemini \"add logging\" - acpx --agent ./my-custom-server \"do something\"`, + acpx claude "refactor auth" + acpx gemini "add logging" + acpx --agent ./my-custom-server "do something"`, ); program.exitOverride((error) => { diff --git a/src/client.ts b/src/client.ts index de5833d..43f0b5b 100644 --- a/src/client.ts +++ b/src/client.ts @@ -22,11 +22,7 @@ import { type WriteTextFileRequest, type WriteTextFileResponse, } from "@agentclientprotocol/sdk"; -import { - spawn, - type ChildProcess, - type ChildProcessByStdio, -} from "node:child_process"; +import { spawn, type ChildProcess, type ChildProcessByStdio } from "node:child_process"; import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; @@ -145,9 +141,7 @@ function asAbsoluteCwd(cwd: string): string { return path.resolve(cwd); } -function toEnvObject( - env: CreateTerminalRequest["env"], -): NodeJS.ProcessEnv | undefined { +function toEnvObject(env: CreateTerminalRequest["env"]): NodeJS.ProcessEnv | undefined { if (!env || env.length === 0) { return undefined; } @@ -488,7 +482,8 @@ export class AcpClient { throw new Error(`Unknown terminal: ${params.terminalId}`); } - const hasExitStatus = terminal.exitCode !== undefined || terminal.signal !== undefined; + const hasExitStatus = + terminal.exitCode !== undefined || terminal.signal !== undefined; return { output: terminal.output.toString("utf8"), diff --git a/src/permissions.ts b/src/permissions.ts index ce39fb6..0f416c8 100644 --- a/src/permissions.ts +++ b/src/permissions.ts @@ -77,9 +77,7 @@ function isAutoApprovedReadKind(kind: ToolKind | undefined): boolean { return kind === "read" || kind === "search"; } -async function promptForPermission( - params: RequestPermissionRequest, -): Promise { +async function promptForPermission(params: RequestPermissionRequest): Promise { if (!process.stdin.isTTY || !process.stderr.isTTY) { return false; } @@ -160,10 +158,7 @@ export function classifyPermissionDecision( return "cancelled"; } - if ( - selectedOption.kind === "allow_once" || - selectedOption.kind === "allow_always" - ) { + if (selectedOption.kind === "allow_once" || selectedOption.kind === "allow_always") { return "approved"; } diff --git a/src/session.ts b/src/session.ts index ecc2ec3..58a65c3 100644 --- a/src/session.ts +++ b/src/session.ts @@ -66,10 +66,7 @@ async function ensureSessionDir(): Promise { await fs.mkdir(SESSION_BASE_DIR, { recursive: true }); } -async function withTimeout( - promise: Promise, - timeoutMs?: number, -): Promise { +async function withTimeout(promise: Promise, timeoutMs?: number): Promise { if (!timeoutMs || timeoutMs <= 0) { return promise; } @@ -309,10 +306,7 @@ function isProcessAlive(pid: number | undefined): boolean { } } -async function waitForProcessExit( - pid: number, - timeoutMs: number, -): Promise { +async function waitForProcessExit(pid: number, timeoutMs: number): Promise { const deadline = Date.now() + Math.max(0, timeoutMs); while (Date.now() <= deadline) { if (!isProcessAlive(pid)) { diff --git a/src/types.ts b/src/types.ts index 4e984e1..b27d745 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,11 +18,7 @@ export type ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES]; export const OUTPUT_FORMATS = ["text", "json", "quiet"] as const; export type OutputFormat = (typeof OUTPUT_FORMATS)[number]; -export const PERMISSION_MODES = [ - "approve-all", - "approve-reads", - "deny-all", -] as const; +export const PERMISSION_MODES = ["approve-all", "approve-reads", "deny-all"] as const; export type PermissionMode = (typeof PERMISSION_MODES)[number]; export type PermissionStats = {