diff --git a/.gitignore b/.gitignore index 7819ddd..4203bb6 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ tmp/ # Environment .env .env.* + diff --git a/Makefile b/Makefile index beb7a09..70a3286 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help build run test test-integration lint docker-build docker-up setup-keys migrate clean +.PHONY: help build run test test-integration lint docker-build docker-up setup-keys migrate clean cli-install cli-build cli-dev cli-test BINARY := zeroid CMD := ./cmd/zeroid @@ -41,6 +41,18 @@ setup-keys: ## Generate ECDSA P-256 + RSA 2048 signing keys migrate: ## Run migrations (starts server, applies, exits) go run $(CMD) -config zeroid.yaml +cli-install: ## Install CLI dependencies + cd cli && npm install + +cli-build: cli-install ## Build the zid CLI + cd cli && npm run build + +cli-dev: cli-install ## Run CLI from source (no build needed) + cd cli && npx tsx src/index.ts $(ARGS) + +cli-test: cli-install ## Run CLI tests + cd cli && npm test + clean: ## Remove binary, keys, and docker volumes rm -f $(BINARY) rm -rf $(KEYS_DIR) diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000..5e9eee0 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +*.js.map diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 0000000..f334afc --- /dev/null +++ b/cli/README.md @@ -0,0 +1,384 @@ +# zid — ZeroID CLI + +Command-line interface for [ZeroID](https://zeroid.io) — agent identity for AI systems. + +```bash +npm install -g @highflame/zid +# or without installing: +npx @highflame/zid +``` + +--- + +## Quick start + +```bash +# Register your first agent — writes .env.zeroid and saves a local profile +zid init --name "github-mcp-server" --type mcp_server + +# Verify a token your server received +zid token verify eyJhbGc... + +# Decode any JWT to inspect its claims (no network call) +zid token decode eyJhbGc... +``` + +--- + +## Authentication + +`zid` authenticates using an API key tied to an agent identity. There are two ways to provide it: + +**Environment variables** (recommended for CI/CD): +```bash +export ZID_API_KEY=zid_sk_... +export ZID_ACCOUNT_ID=acct_123 +export ZID_PROJECT_ID=proj_456 +export ZID_BASE_URL=https://api.zeroid.io # optional, default shown +``` + +**Local profile** (set automatically by `zid init`, stored in `~/.config/zid/config.json`): +```bash +zid config use-profile prod # switch active profile +zid config list-profiles # list all profiles +``` + +Environment variables take precedence over the config file. Most commands also accept `--profile ` to select a non-default profile explicitly. + +--- + +## Commands + +### `zid init` + +Register a new agent, write its API key to `.env.zeroid`, and save a local profile. + +```bash +zid init --name "github-mcp-server" --type mcp_server +zid init --name "code-reviewer" --type agent --sub-type code_agent --framework langchain +zid init --name "my-agent" --save-profile staging +``` + +| Flag | Description | Default | +|---|---|---| +| `--name ` | Human-readable agent name | required | +| `--id ` | External ID (your own identifier) | same as `--name` | +| `--type ` | `agent` \| `application` \| `mcp_server` \| `service` | `agent` | +| `--sub-type ` | `orchestrator` \| `tool_agent` \| `code_agent` \| `autonomous` \| ... | — | +| `--framework ` | Framework name, e.g. `langchain`, `mcp` | — | +| `--description ` | Short description | — | +| `--save-profile ` | Profile name to save credentials under | `default` | +| `--profile ` | Profile to use for the parent account/project | active profile | +| `--json` | Output raw JSON | — | + +After running, the API key is written to `.env.zeroid` in the current directory. Add it to `.gitignore`. + +--- + +### `zid token issue` + +Issue a short-lived access token for the authenticated agent. + +```bash +zid token issue +zid token issue --scope "repo:read" +zid token issue --scope "repo:read pr:write" --json +``` + +| Flag | Description | Default | +|---|---|---| +| `--scope ` | Space-separated scopes to request | all allowed scopes | +| `--profile ` | Profile to use | active profile | +| `--json` | Output raw JSON | — | + +**Output:** +``` +✓ Token issued + access_token: eyJhbGc... + token_type: Bearer + expires_in: 900s +``` + +--- + +### `zid token decode` + +Decode a JWT and display its claims. No network call, no signature check — useful for inspecting any token. + +```bash +zid token decode eyJhbGc... +pbpaste | zid token decode # read from stdin +zid token decode eyJhbGc... --json # raw JSON output +``` + +Reads from stdin if no argument is given, so it works in pipelines: +```bash +zid token issue --json | jq -r '.access_token' | zid token decode +``` + +| Flag | Description | +|---|---| +| `--json` | Output `{ header, payload }` as raw JSON | + +**Output (human-readable):** +``` +Header + alg: ES256 + kid: key-2025-01 + +Payload + sub: wimse:agent:acct_123/proj_456/github-mcp-server + iss: https://api.zeroid.io + identity_type: agent + trust_level: first_party + grant_type: api_key + iat: 2026-03-29T10:00:00.000Z (5m ago) + exp: 2026-03-29T10:15:00.000Z (in 10m) + scopes: repo:read pr:write +``` + +Expired tokens are shown with the `exp` line in red. + +--- + +### `zid token verify` + +Verify a JWT against the live JWKS endpoint. Confirms the signature is valid and the token has not expired. + +```bash +zid token verify eyJhbGc... +zid token verify eyJhbGc... --json +``` + +| Flag | Description | +|---|---| +| `--profile ` | Profile to use (determines the JWKS base URL) | +| `--json` | Output verified identity claims as raw JSON | + +**Exit codes:** + +| Code | Meaning | +|---|---| +| `0` | Valid | +| `1` | Invalid (bad signature, malformed, network error) | +| `2` | Expired | + +Shell scripts can branch on exit codes: +```bash +if zid token verify "$TOKEN"; then + echo "token ok" +elif [ $? -eq 2 ]; then + echo "token expired" +fi +``` + +--- + +### `zid token revoke` + +Revoke a token immediately. + +```bash +zid token revoke eyJhbGc... +``` + +| Flag | Description | +|---|---| +| `--profile ` | Profile to use | +| `--json` | Output raw JSON response | + +--- + +### `zid agents list` + +List all registered agents for the current tenant. + +```bash +zid agents list +zid agents list --type mcp_server +zid agents list --json | jq '.[].wimse_uri' +``` + +| Flag | Description | Default | +|---|---|---| +| `--type ` | Filter by identity type | all types | +| `--limit ` | Max results | `50` | +| `--profile ` | Profile to use | active profile | +| `--json` | Output raw JSON array | — | + +**Output:** +``` +┌──────────────────────┬────────────┬─────────────┬────────┬──────────┐ +│ NAME │ TYPE │ TRUST │ STATUS │ CREATED │ +├──────────────────────┼────────────┼─────────────┼────────┼──────────┤ +│ github-mcp-server │ mcp_server │ first_party │ active │ 2h ago │ +│ code-reviewer │ agent │ first_party │ active │ 5m ago │ +└──────────────────────┴────────────┴─────────────┴────────┴──────────┘ + +2 agent(s) +``` + +--- + +### `zid agents get ` + +Get a single agent by its identity ID. + +```bash +zid agents get agt_abc123 +zid agents get agt_abc123 --json +``` + +| Flag | Description | +|---|---| +| `--profile ` | Profile to use | +| `--json` | Output raw JSON | + +--- + +### `zid agents rotate-key ` + +Revoke the agent's current API key and issue a new one. + +```bash +zid agents rotate-key agt_abc123 +``` + +The new API key is printed once and not stored — save it immediately. + +| Flag | Description | +|---|---| +| `--profile ` | Profile to use | +| `--json` | Output raw JSON | + +--- + +### `zid agents deactivate ` + +Suspend an agent. Its tokens will be rejected until it is re-activated. Does not delete the agent. + +```bash +zid agents deactivate agt_abc123 +zid agents activate agt_abc123 +``` + +| Flag | Description | +|---|---| +| `--profile ` | Profile to use | +| `--json` | Output raw JSON | + +--- + +### `zid creds list` + +List issued credentials (JWTs) for an agent. + +```bash +zid creds list --agent agt_abc123 +zid creds list --agent agt_abc123 --active # non-revoked only +zid creds list --agent agt_abc123 --json +``` + +| Flag | Description | +|---|---| +| `--agent ` | Agent identity ID (required) | +| `--active` | Show only non-revoked credentials | +| `--profile ` | Profile to use | +| `--json` | Output raw JSON array | + +**Output:** +``` +┌──────────────┬────────┬──────────────────────┬─────────┬──────────┐ +│ ID │ STATUS │ SCOPES │ EXPIRES │ ISSUED │ +├──────────────┼────────┼──────────────────────┼─────────┼──────────┤ +│ cred_xyz789 │ active │ repo:read pr:write │ 10m ago │ 25m ago │ +└──────────────┴────────┴──────────────────────┴─────────┴──────────┘ + +1 credential(s) +``` + +--- + +### `zid signal` + +Ingest a Continuous Access Evaluation (CAE) signal for an agent. Signals can trigger token revocation or other policy actions depending on your ZeroID configuration. + +```bash +zid signal \ + --agent agt_abc123 \ + --type anomalous_behavior \ + --severity high \ + --source "security-monitor" \ + --reason "unexpected outbound call to external endpoint" +``` + +| Flag | Description | Required | +|---|---|---| +| `--agent ` | Agent identity ID | yes | +| `--type ` | Signal type (see below) | yes | +| `--severity ` | `low` \| `medium` \| `high` \| `critical` | yes | +| `--source ` | Origin of the signal, e.g. `siem`, `monitor` | yes | +| `--reason ` | Human-readable reason, stored in `payload.reason` | no | +| `--profile ` | Profile to use | no | +| `--json` | Output raw JSON | no | + +**Signal types:** + +| Type | When to use | +|---|---| +| `anomalous_behavior` | Unexpected or out-of-policy actions | +| `policy_violation` | Confirmed policy breach | +| `credential_change` | Key or secret rotation outside normal flow | +| `session_revoked` | Session ended by external system | +| `ip_change` | Agent calling from unexpected network location | +| `owner_change` | Ownership of the agent transferred | +| `retirement` | Agent decommissioned | + +--- + +### `zid config` + +Manage CLI profiles. + +```bash +zid config list-profiles # list all profiles, marking the active one +zid config use-profile prod # switch the active profile +``` + +Profiles are stored in `~/.config/zid/config.json`. + +--- + +## Global flags + +All commands that make API calls support: + +| Flag | Description | +|---|---| +| `--profile ` | Use a specific named profile | +| `--json` | Output machine-readable JSON (disables table/colored output) | + +--- + +## Development + +```bash +# Install dependencies +cd cli && npm install + +# Run from source (no build needed) +npm run dev -- init --name "test-agent" + +# Build +npm run build + +# Type check +npm run typecheck +``` + +Or via the root Makefile: +```bash +make cli-install +make cli-build +make cli-dev ARGS="token decode eyJhbGc..." +``` diff --git a/cli/eslint.config.js b/cli/eslint.config.js new file mode 100644 index 0000000..4f8ffe5 --- /dev/null +++ b/cli/eslint.config.js @@ -0,0 +1,30 @@ +import tseslint from "typescript-eslint"; + +export default tseslint.config( + ...tseslint.configs.recommended, + { + rules: { + // Allow explicit `any` in tests and when interfacing with Commander (opts: unknown) + "@typescript-eslint/no-explicit-any": "warn", + // Allow non-null assertion — used in decode.ts with a length guard above + "@typescript-eslint/no-non-null-assertion": "off", + // Require type annotations on exported functions but not internal helpers + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + // These are useful but too noisy for a CLI that casts Commander opts + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + }, + }, + { + // Relax rules further in test files + files: ["tests/**/*.ts"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + }, + }, + { + ignores: ["dist/**", "node_modules/**"], + }, +); diff --git a/cli/package-lock.json b/cli/package-lock.json new file mode 100644 index 0000000..10118d7 --- /dev/null +++ b/cli/package-lock.json @@ -0,0 +1,4449 @@ +{ + "name": "@highflame/zid", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@highflame/zid", + "version": "0.1.0", + "dependencies": { + "@highflame/sdk": "^0.3.9", + "chalk": "^5.4.1", + "cli-table3": "^0.6.5", + "commander": "^13.1.0" + }, + "bin": { + "zid": "dist/index.js" + }, + "devDependencies": { + "@types/node": "^20", + "eslint": "^9", + "msw": "^2", + "tsup": "^8", + "tsx": "^4", + "typescript": "^5.4", + "typescript-eslint": "^8", + "vitest": "^2" + }, + "engines": { + "node": ">=18" + } + }, + "../../../highflame-sdk/javascript": { + "extraneous": true + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "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.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "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.1", + "minimatch": "^3.1.5", + "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/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "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.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@highflame/sdk": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@highflame/sdk/-/sdk-0.3.9.tgz", + "integrity": "sha512-9rlYanCOgsHwXbWX/Eg5SCbUyTKm69i7DWH8WkjSnDl9DDRwp22wB8RNzFEGdX/xGmp4AYan7eI2jZBjLvOsHw==", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.4.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "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/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "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.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.41.3", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.3.tgz", + "integrity": "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==", + "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/@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/@rollup/rollup-android-arm-eabi": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/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": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "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/@typescript-eslint/eslint-plugin": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", + "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/type-utils": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "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.57.2", + "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.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", + "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "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.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", + "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.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" + }, + "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.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "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.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", + "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "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.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "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.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "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": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" + }, + "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.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "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/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-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "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/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/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/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "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/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/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": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "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==", + "dev": true, + "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/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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-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/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/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/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "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.5", + "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-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/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/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.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/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/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.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", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/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", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "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.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "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": "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/graphql": { + "version": "16.13.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz", + "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/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/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/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-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-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-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/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/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "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", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "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/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/msw": { + "version": "2.12.14", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.14.tgz", + "integrity": "sha512-4KXa4nVBIBjbDbd7vfQNuQ25eFxug0aropCQFoI0JdOBuJWamkT1yLVIWReFI8SiTRc+H1hKzaNk+cLk2N9rtQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@inquirer/confirm": "^5.0.0", + "@mswjs/interceptors": "^0.41.2", + "@open-draft/deferred-promise": "^2.2.0", + "@types/statuses": "^2.0.6", + "cookie": "^1.0.2", + "graphql": "^16.12.0", + "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", + "rettime": "^0.10.1", + "statuses": "^2.0.2", + "strict-event-emitter": "^0.5.1", + "tough-cookie": "^6.0.0", + "type-fest": "^5.2.0", + "until-async": "^3.0.2", + "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/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.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/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==", + "dev": true, + "license": "MIT", + "engines": { + "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/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/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/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/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/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/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/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "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/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "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/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", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/rettime": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.10.1.tgz", + "integrity": "sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "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.60.0", + "@rollup/rollup-android-arm64": "4.60.0", + "@rollup/rollup-darwin-arm64": "4.60.0", + "@rollup/rollup-darwin-x64": "4.60.0", + "@rollup/rollup-freebsd-arm64": "4.60.0", + "@rollup/rollup-freebsd-x64": "4.60.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", + "@rollup/rollup-linux-arm64-gnu": "4.60.0", + "@rollup/rollup-linux-arm64-musl": "4.60.0", + "@rollup/rollup-linux-loong64-gnu": "4.60.0", + "@rollup/rollup-linux-loong64-musl": "4.60.0", + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", + "@rollup/rollup-linux-ppc64-musl": "4.60.0", + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", + "@rollup/rollup-linux-riscv64-musl": "4.60.0", + "@rollup/rollup-linux-s390x-gnu": "4.60.0", + "@rollup/rollup-linux-x64-gnu": "4.60.0", + "@rollup/rollup-linux-x64-musl": "4.60.0", + "@rollup/rollup-openbsd-x64": "4.60.0", + "@rollup/rollup-openharmony-arm64": "4.60.0", + "@rollup/rollup-win32-arm64-msvc": "4.60.0", + "@rollup/rollup-win32-ia32-msvc": "4.60.0", + "@rollup/rollup-win32-x64-gnu": "4.60.0", + "@rollup/rollup-win32-x64-msvc": "4.60.0", + "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/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "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/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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": "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/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-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "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/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.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", + "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.27" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", + "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "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.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-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": "5.5.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz", + "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", + "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.57.2", + "@typescript-eslint/parser": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2" + }, + "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", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/until-async": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", + "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/kettanaito" + } + }, + "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/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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/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/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "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" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "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/yoctocolors-cjs": { + "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, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 0000000..00a3aba --- /dev/null +++ b/cli/package.json @@ -0,0 +1,40 @@ +{ + "name": "@highflame/zid", + "version": "0.1.0", + "description": "CLI for ZeroID — agent identity for AI systems", + "type": "module", + "bin": { + "zid": "./dist/index.js" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup src/index.ts --format esm --dts --clean", + "dev": "tsx src/index.ts", + "test": "vitest run", + "test:watch": "vitest", + "typecheck": "tsc --noEmit", + "lint": "eslint src tests", + "lint:fix": "eslint src tests --fix" + }, + "dependencies": { + "@highflame/sdk": "^0.3.9", + "chalk": "^5.4.1", + "cli-table3": "^0.6.5", + "commander": "^13.1.0" + }, + "devDependencies": { + "@types/node": "^20", + "eslint": "^9", + "msw": "^2", + "tsup": "^8", + "tsx": "^4", + "typescript": "^5.4", + "typescript-eslint": "^8", + "vitest": "^2" + }, + "engines": { + "node": ">=18" + } +} diff --git a/cli/src/commands/agents/deactivate.ts b/cli/src/commands/agents/deactivate.ts new file mode 100644 index 0000000..86192ab --- /dev/null +++ b/cli/src/commands/agents/deactivate.ts @@ -0,0 +1,42 @@ +/** + * zid agents deactivate — deactivate an agent (soft, reversible). + * zid agents activate — re-activate a deactivated agent. + */ + +import { Command } from "commander"; +import { makeClient } from "../../lib/client.js"; +import { handleError, printJSON, printSuccess } from "../../lib/output.js"; + +export function registerDeactivate(agentsCmd: Command): void { + agentsCmd + .command("deactivate ") + .description("Deactivate an agent (reversible — does not delete)") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (id: string, opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const agent = await client.agents.deactivate(id); + if (opts.json) { printJSON(agent); return; } + printSuccess(`Agent ${agent.name} deactivated`); + } catch (err) { + handleError(err); + } + }); + + agentsCmd + .command("activate ") + .description("Activate a previously deactivated agent") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (id: string, opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const agent = await client.agents.activate(id); + if (opts.json) { printJSON(agent); return; } + printSuccess(`Agent ${agent.name} activated`); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/agents/get.ts b/cli/src/commands/agents/get.ts new file mode 100644 index 0000000..6375329 --- /dev/null +++ b/cli/src/commands/agents/get.ts @@ -0,0 +1,42 @@ +/** + * zid agents get — get an agent by ID. + */ + +import { Command } from "commander"; +import chalk from "chalk"; +import { makeClient } from "../../lib/client.js"; +import { handleError, printJSON } from "../../lib/output.js"; + +export function registerGet(agentsCmd: Command): void { + agentsCmd + .command("get ") + .description("Get an agent by ID") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (id: string, opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const agent = await client.agents.get(id); + + if (opts.json) { + printJSON(agent); + return; + } + + console.log(chalk.bold(`\n${agent.name}`)); + console.log(` ID: ${agent.id}`); + console.log(` WIMSE URI: ${agent.wimse_uri}`); + console.log(` External ID: ${agent.external_id}`); + console.log(` Type: ${agent.identity_type}`); + console.log(` Sub-type: ${agent.sub_type || "-"}`); + console.log(` Trust: ${agent.trust_level}`); + console.log(` Status: ${agent.status}`); + console.log(` Framework: ${agent.framework || "-"}`); + console.log(` Description: ${agent.description || "-"}`); + console.log(` Created: ${agent.created_at}`); + console.log(); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/agents/list.ts b/cli/src/commands/agents/list.ts new file mode 100644 index 0000000..a2bb41b --- /dev/null +++ b/cli/src/commands/agents/list.ts @@ -0,0 +1,55 @@ +/** + * zid agents list — list registered agents. + * + * Usage: + * zid agents list + * zid agents list --json | jq '.[].wimse_uri' + */ + +import { Command } from "commander"; +import { makeClient } from "../../lib/client.js"; +import { handleError, printJSON, printTable, relativeTime } from "../../lib/output.js"; + +export function registerList(agentsCmd: Command): void { + agentsCmd + .command("list") + .description("List registered agents") + .option("--type ", "Filter by identity_type") + .option("--limit ", "Max results", "50") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON array") + .action(async (opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const limit = parseInt(opts.limit as string, 10); + const result = await client.agents.list({ + identity_type: opts.type as string | undefined, + limit: Number.isNaN(limit) ? 50 : limit, + }); + + if (opts.json) { + printJSON(result.agents); + return; + } + + if (result.agents.length === 0) { + console.log("No agents found."); + return; + } + + printTable( + ["NAME", "TYPE", "TRUST", "STATUS", "CREATED"], + result.agents.map((a) => [ + a.name, + a.identity_type, + a.trust_level, + a.status, + relativeTime(a.created_at), + ]), + ); + console.log(`\n${result.agents.length} agent(s)`); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/agents/rotate-key.ts b/cli/src/commands/agents/rotate-key.ts new file mode 100644 index 0000000..edd982f --- /dev/null +++ b/cli/src/commands/agents/rotate-key.ts @@ -0,0 +1,32 @@ +/** + * zid agents rotate-key — rotate an agent's API key. + */ + +import { Command } from "commander"; +import { makeClient } from "../../lib/client.js"; +import { handleError, printJSON, printSuccess, printWarning } from "../../lib/output.js"; + +export function registerRotateKey(agentsCmd: Command): void { + agentsCmd + .command("rotate-key ") + .description("Rotate an agent's API key — revokes old key and issues a new one") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (id: string, opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const result = await client.agents.rotateKey(id); + + if (opts.json) { + printJSON(result); + return; + } + + printSuccess(`API key rotated for agent ${result.identity.name}`); + console.log(` New API key: ${result.api_key}`); + printWarning("Store the new API key securely — it will not be shown again."); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/config.ts b/cli/src/commands/config.ts new file mode 100644 index 0000000..aa215b6 --- /dev/null +++ b/cli/src/commands/config.ts @@ -0,0 +1,47 @@ +/** + * zid config — manage CLI profiles. + * + * Usage: + * zid config use-profile prod + * zid config list-profiles + */ + +import { Command } from "commander"; +import { useProfile, listProfiles } from "../lib/config.js"; +import { handleError, printSuccess } from "../lib/output.js"; + +export function registerConfig(program: Command): void { + const configCmd = program + .command("config") + .description("Manage CLI profiles"); + + configCmd + .command("use-profile ") + .description("Switch the active profile") + .action((name: string) => { + try { + useProfile(name); + printSuccess(`Switched to profile "${name}"`); + } catch (err) { + handleError(err); + } + }); + + configCmd + .command("list-profiles") + .description("List all configured profiles") + .action(() => { + try { + const profiles = listProfiles(); + if (profiles.length === 0) { + console.log('No profiles configured. Run "zid init" to get started.'); + return; + } + for (const p of profiles) { + console.log(` ${p.active ? "* " : " "}${p.name}`); + } + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/creds/list.ts b/cli/src/commands/creds/list.ts new file mode 100644 index 0000000..bf640b0 --- /dev/null +++ b/cli/src/commands/creds/list.ts @@ -0,0 +1,50 @@ +/** + * zid creds list --agent — list credentials for an agent. + */ + +import { Command } from "commander"; +import { makeClient } from "../../lib/client.js"; +import { handleError, printJSON, printTable, relativeTime } from "../../lib/output.js"; + +export function registerCredsList(credsCmd: Command): void { + credsCmd + .command("list") + .description("List credentials for an agent") + .requiredOption("--agent ", "Agent identity ID") + .option("--active", "Show only non-revoked credentials") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const result = await client.credentials.list(opts.agent as string); + const creds = opts.active + ? result.credentials.filter((c) => !c.is_revoked) + : result.credentials; + + if (opts.json) { + printJSON(creds); + return; + } + + if (creds.length === 0) { + console.log("No credentials found."); + return; + } + + printTable( + ["ID", "STATUS", "SCOPES", "EXPIRES", "ISSUED"], + creds.map((c) => [ + c.id, + c.is_revoked ? "revoked" : "active", + c.scopes.join(" ") || "-", + relativeTime(c.expires_at), + relativeTime(c.issued_at), + ]), + ); + console.log(`\n${creds.length} credential(s)`); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/init.ts b/cli/src/commands/init.ts new file mode 100644 index 0000000..5cfc528 --- /dev/null +++ b/cli/src/commands/init.ts @@ -0,0 +1,72 @@ +/** + * zid init — register a new agent and write the api_key to .env.zeroid. + * + * Usage: + * zid init --name "github-mcp-server" --type mcp_server --scope "repo:read pr:write" + */ + +import { Command } from "commander"; +import type { IdentityType, SubType } from "@highflame/sdk"; +import { makeClient } from "../lib/client.js"; +import { setProfile, writeEnvFile } from "../lib/config.js"; +import { handleError, printJSON, printSuccess, printWarning } from "../lib/output.js"; + +export function registerInit(program: Command): void { + program + .command("init") + .description("Register a new agent and write credentials to .env.zeroid") + .requiredOption("--name ", "Human-readable agent name") + .requiredOption("--owner ", "User ID of the agent owner") + .option("--id ", "External ID (defaults to --name)") + .option( + "--type ", + "Identity type: agent | application | mcp_server | service", + "agent", + ) + .option("--sub-type ", "Sub-type: orchestrator | tool_agent | code_agent | ...") + .option("--framework ", "Framework name (e.g. langchain, mcp)") + .option("--description ", "Short description") + .option("--profile ", "Config profile to use") + .option("--save-profile ", "Save credentials under this profile name", "default") + .option("--json", "Output raw JSON") + .action(async (opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const result = await client.agents.register({ + name: opts.name as string, + external_id: (opts.id as string | undefined) ?? (opts.name as string), + identity_type: opts.type as IdentityType, + sub_type: opts.subType as SubType | undefined, + framework: opts.framework as string | undefined, + description: opts.description as string | undefined, + created_by: opts.owner as string + }); + + // Side effects always happen regardless of output format. + writeEnvFile(result.api_key); + setProfile(opts.saveProfile as string, { + base_url: client.baseUrl, + account_id: result.identity.account_id, + project_id: result.identity.project_id, + api_key: result.api_key, + }); + + if (opts.json) { + printJSON(result); + return; + } + + printSuccess(`Agent registered: ${result.identity.name}`); + console.log(` WIMSE URI: ${result.identity.wimse_uri}`); + console.log(` ID: ${result.identity.id}`); + console.log(` Type: ${result.identity.identity_type}`); + console.log(` Trust: ${result.identity.trust_level}`); + console.log(` API key: ${result.api_key}`); + printSuccess("API key written to .env.zeroid"); + printSuccess(`Profile "${opts.saveProfile as string}" saved to ~/.config/zid/config.json`); + printWarning("Store the API key securely — it will not be shown again."); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/signal.ts b/cli/src/commands/signal.ts new file mode 100644 index 0000000..1ec3d4e --- /dev/null +++ b/cli/src/commands/signal.ts @@ -0,0 +1,55 @@ +/** + * zid signal — ingest a CAE signal for an agent. + * + * Usage: + * zid signal --agent --type anomalous_behavior --severity high \ + * --source "security-monitor" --reason "unexpected outbound call" + */ + +import { Command } from "commander"; +import type { SignalType, SignalSeverity } from "@highflame/sdk"; +import { makeClient } from "../lib/client.js"; +import { handleError, printJSON, printSuccess } from "../lib/output.js"; + +export function registerSignal(program: Command): void { + program + .command("signal") + .description("Ingest a CAE signal for an agent") + .requiredOption("--agent ", "Agent identity ID") + .requiredOption( + "--type ", + "Signal type: credential_change | session_revoked | ip_change | anomalous_behavior | policy_violation | retirement | owner_change", + ) + .requiredOption("--severity ", "Severity: low | medium | high | critical") + .requiredOption("--source ", "Source of the signal (e.g. zid-cli, siem, monitor)") + .option("--reason ", "Human-readable reason (stored in payload.reason)") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const payload: Record | undefined = + opts.reason ? { reason: opts.reason as string } : undefined; + + const signal = await client.signals.ingest({ + identity_id: opts.agent as string, + signal_type: opts.type as SignalType, + severity: opts.severity as SignalSeverity, + source: opts.source as string, + payload, + }); + + if (opts.json) { + printJSON(signal); + return; + } + + printSuccess(`Signal ingested (${signal.id})`); + console.log(` Type: ${signal.signal_type}`); + console.log(` Severity: ${signal.severity}`); + console.log(` Agent: ${signal.identity_id}`); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/token/decode.ts b/cli/src/commands/token/decode.ts new file mode 100644 index 0000000..bf9d285 --- /dev/null +++ b/cli/src/commands/token/decode.ts @@ -0,0 +1,158 @@ +/** + * zid token decode — decode a JWT and display claims (no signature verification). + * + * Accepts the token as an argument or from stdin: + * zid token decode eyJhbGc... + * pbpaste | zid token decode + */ + +import { Command } from "commander"; +import chalk from "chalk"; +import { handleError, printJSON, relativeTime } from "../../lib/output.js"; + +function _base64urlDecode(s: string): string { + return Buffer.from(s, "base64url").toString("utf8"); +} + +function _decodeJWT(token: string): { header: Record; payload: Record } { + const parts = token.trim().split("."); + if (parts.length !== 3) { + throw new Error("Malformed JWT: expected 3 parts"); + } + return { + header: JSON.parse(_base64urlDecode(parts[0]!)) as Record, + payload: JSON.parse(_base64urlDecode(parts[1]!)) as Record, + }; +} + + +function _formatTime(epoch: unknown): string { + if (typeof epoch !== "number") return String(epoch); + const d = new Date(epoch * 1000); + return `${d.toISOString()} (${relativeTime(d.toISOString())})`; +} + +export function registerDecode(tokenCmd: Command): void { + tokenCmd + .command("decode [jwt]") + .description("Decode a JWT and display its claims (no signature check)") + .option("--json", "Output raw JSON") + .action(async (jwt: string | undefined, opts) => { + try { + let token = jwt; + if (!token) { + // Read from stdin if no argument given. + const chunks: Buffer[] = []; + for await (const chunk of process.stdin) { + chunks.push(chunk as Buffer); + } + token = Buffer.concat(chunks).toString("utf8").trim(); + } + if (!token) throw new Error("No JWT provided"); + + const { header, payload } = _decodeJWT(token); + + if (opts.json) { + printJSON({ header, payload }); + return; + } + + console.log(chalk.bold("\nHeader")); + console.log(` alg: ${header["alg"] ?? "-"}`); + console.log(` kid: ${header["kid"] ?? "-"}`); + + console.log(chalk.bold("\nPayload")); + const str = (k: string) => (typeof payload[k] === "string" ? (payload[k] as string) : "-"); + const num = (k: string) => (typeof payload[k] === "number" ? (payload[k] as number) : undefined); + + // Track every key printed so anything else falls through to "Custom claims". + // COL is the width of the longest fixed claim key ("delegation_depth" = 16 chars). + const COL = 16; + const printed = new Set(); + const print = (key: string, value: string) => { + printed.add(key); + console.log(` ${key.padEnd(COL)}: ${value}`); + }; + + print("sub", str("sub")); + print("iss", str("iss")); + print("jti", str("jti")); + if (payload["aud"] !== undefined) { + const aud = Array.isArray(payload["aud"]) ? (payload["aud"] as string[]).join(", ") : str("aud"); + print("aud", aud); + } + print("account_id", str("account_id")); + print("project_id", str("project_id")); + if (payload["external_id"] !== undefined) print("external_id", str("external_id")); + if (payload["name"] !== undefined) print("name", str("name")); + if (payload["status"] !== undefined) print("status", str("status")); + print("identity_type", str("identity_type")); + if (payload["sub_type"] !== undefined) print("sub_type", str("sub_type")); + print("trust_level", str("trust_level")); + print("grant_type", str("grant_type")); + if (payload["owner_user_id"] !== undefined) print("owner_user_id", str("owner_user_id")); + if (payload["framework"] !== undefined) print("framework", str("framework")); + if (payload["version"] !== undefined) print("version", str("version")); + if (payload["publisher"] !== undefined) print("publisher", str("publisher")); + + const nbf = num("nbf"); + const iat = num("iat"); + const exp = num("exp"); + if (nbf !== undefined) print("nbf", _formatTime(nbf)); + if (iat !== undefined) print("iat", _formatTime(iat)); + if (exp !== undefined) { + const expired = exp < Date.now() / 1000; + const label = _formatTime(exp); + print("exp", expired ? chalk.red(label) : chalk.green(label)); + } + + if (payload["scopes"] !== undefined) { + const scopes = Array.isArray(payload["scopes"]) ? (payload["scopes"] as string[]).join(" ") : str("scopes"); + print("scopes", scopes); + } + + const depth = num("delegation_depth"); + if (depth !== undefined) print("delegation_depth", String(depth)); + + if (payload["act"] && typeof payload["act"] === "object") { + const act = payload["act"] as Record; + print("act", `{ sub: ${act["sub"] ?? "-"} }`); + } + + if (payload["session_id"] !== undefined) print("session_id", str("session_id")); + if (payload["task_id"] !== undefined) print("task_id", str("task_id")); + if (payload["task_type"] !== undefined) print("task_type", str("task_type")); + if (payload["workspace"] !== undefined) print("workspace", str("workspace")); + if (payload["environment"] !== undefined) print("environment", str("environment")); + + if (payload["allowed_tools"] !== undefined) { + const tools = Array.isArray(payload["allowed_tools"]) + ? (payload["allowed_tools"] as string[]).join(", ") + : String(payload["allowed_tools"]); + print("allowed_tools", tools); + } + + if (payload["capabilities"] !== undefined) { + const caps = Array.isArray(payload["capabilities"]) + ? (payload["capabilities"] as string[]).join(", ") + : JSON.stringify(payload["capabilities"]); + print("capabilities", caps); + } + + // Print any remaining claims not shown above. + const extra = Object.entries(payload).filter(([k]) => !printed.has(k)); + if (extra.length > 0) { + console.log(chalk.bold("\nCustom claims")); + let maxLen = 0; + for (const [k] of extra) if (k.length > maxLen) maxLen = k.length; + for (const [k, v] of extra) { + const val = typeof v === "string" ? v : JSON.stringify(v); + console.log(` ${k.padEnd(maxLen)}: ${val}`); + } + } + console.log(); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/token/issue.ts b/cli/src/commands/token/issue.ts new file mode 100644 index 0000000..57aaad2 --- /dev/null +++ b/cli/src/commands/token/issue.ts @@ -0,0 +1,45 @@ +/** + * zid token issue — issue a short-lived token using the profile's api_key. + * + * Usage: + * zid token issue + * zid token issue --scope "repo:read" + */ + +import { Command } from "commander"; +import { requireProfile } from "../../lib/config.js"; +import { makeClientFromProfile } from "../../lib/client.js"; +import { handleError, printJSON, printSuccess } from "../../lib/output.js"; + +export function registerIssue(tokenCmd: Command): void { + tokenCmd + .command("issue") + .description("Issue a token for the authenticated agent (api_key grant)") + .option("--scope ", "Space-separated scopes to request", "") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (opts) => { + try { + const profile = requireProfile(opts.profile as string | undefined); + const client = makeClientFromProfile(profile); + const token = await client.tokens.issue({ + grant_type: "api_key", + api_key: profile.api_key, + scope: (opts.scope as string).trim() || undefined, + }); + + if (opts.json) { + printJSON(token); + return; + } + + printSuccess("Token issued"); + console.log(` access_token: ${token.access_token}`); + console.log(` token_type: ${token.token_type}`); + console.log(` expires_in: ${token.expires_in}s`); + console.log(); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/token/revoke.ts b/cli/src/commands/token/revoke.ts new file mode 100644 index 0000000..cbc2e6d --- /dev/null +++ b/cli/src/commands/token/revoke.ts @@ -0,0 +1,30 @@ +/** + * zid token revoke — revoke a token. + */ + +import { Command } from "commander"; +import { makeClient } from "../../lib/client.js"; +import { handleError, printJSON, printSuccess } from "../../lib/output.js"; + +export function registerTokenRevoke(tokenCmd: Command): void { + tokenCmd + .command("revoke ") + .description("Revoke a token") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (jwt: string, opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const result = await client.tokens.revoke(jwt); + + if (opts.json) { + printJSON(result); + return; + } + + printSuccess("Token revoked"); + } catch (err) { + handleError(err); + } + }); +} diff --git a/cli/src/commands/token/verify.ts b/cli/src/commands/token/verify.ts new file mode 100644 index 0000000..79df940 --- /dev/null +++ b/cli/src/commands/token/verify.ts @@ -0,0 +1,59 @@ +/** + * zid token verify — verify a JWT against the live JWKS. + * + * Exit codes: + * 0 — valid + * 1 — invalid / error + * 2 — expired + */ + +import { Command } from "commander"; +import chalk from "chalk"; +import { ZeroIDError } from "@highflame/sdk"; +import { makeClient } from "../../lib/client.js"; +import { handleError, printJSON } from "../../lib/output.js"; + +export function registerVerify(tokenCmd: Command): void { + tokenCmd + .command("verify ") + .description("Verify a JWT against the live JWKS (exit 0 = valid, 1 = invalid, 2 = expired)") + .option("--profile ", "Config profile to use") + .option("--json", "Output raw JSON") + .action(async (jwt: string, opts) => { + try { + const client = makeClient(opts.profile as string | undefined); + const identity = await client.tokens.verify(jwt); + + if (opts.json) { + printJSON(identity); + } else { + console.log(chalk.green("✓") + " Token is valid\n"); + console.log(` sub: ${identity.sub}`); + console.log(` identity_type: ${identity.identity_type ?? "-"}`); + console.log(` trust_level: ${identity.trust_level ?? "-"}`); + console.log(` account_id: ${identity.account_id}`); + console.log(` project_id: ${identity.project_id}`); + if (identity.scopes?.length) { + console.log(` scopes: ${identity.scopes.join(" ")}`); + } + if (identity.exp > 0) { + const exp = new Date(identity.exp * 1000); + const remaining = Math.round((identity.exp - Date.now() / 1000) / 60); + console.log(` expires: ${exp.toISOString()} (${remaining}m remaining)`); + } + const delegator = identity.delegatedBy(); + if (delegator) { + console.log(` delegated_by: ${delegator}`); + } + console.log(); + } + } catch (err: unknown) { + if (err instanceof ZeroIDError && err.code === "token_expired") { + console.error(chalk.red("✗") + " Token has expired"); + process.exit(2); + } + handleError(err); + } + process.exit(0); + }); +} diff --git a/cli/src/index.ts b/cli/src/index.ts new file mode 100644 index 0000000..9837ebd --- /dev/null +++ b/cli/src/index.ts @@ -0,0 +1,51 @@ +#!/usr/bin/env node +/** + * zid — ZeroID CLI + * + * Entry point. Registers all commands and parses argv. + */ + +import { Command } from "commander"; +import { registerInit } from "./commands/init.js"; +import { registerDecode } from "./commands/token/decode.js"; +import { registerVerify } from "./commands/token/verify.js"; +import { registerIssue } from "./commands/token/issue.js"; +import { registerTokenRevoke } from "./commands/token/revoke.js"; +import { registerList } from "./commands/agents/list.js"; +import { registerGet } from "./commands/agents/get.js"; +import { registerRotateKey } from "./commands/agents/rotate-key.js"; +import { registerDeactivate } from "./commands/agents/deactivate.js"; +import { registerCredsList } from "./commands/creds/list.js"; +import { registerSignal } from "./commands/signal.js"; +import { registerConfig } from "./commands/config.js"; + +const program = new Command(); + +program + .name("zid") + .description("ZeroID CLI — agent identity for AI systems") + .version("0.1.0"); + +registerInit(program); +registerSignal(program); +registerConfig(program); + +const tokenCmd = program.command("token").description("Token operations"); +registerIssue(tokenCmd); +registerDecode(tokenCmd); +registerVerify(tokenCmd); +registerTokenRevoke(tokenCmd); + +const agentsCmd = program.command("agents").description("Agent registry operations"); +registerList(agentsCmd); +registerGet(agentsCmd); +registerRotateKey(agentsCmd); +registerDeactivate(agentsCmd); + +const credsCmd = program.command("creds").description("Credential operations"); +registerCredsList(credsCmd); + +program.parseAsync(process.argv).catch((err: unknown) => { + console.error(err instanceof Error ? err.message : String(err)); + process.exit(1); +}); diff --git a/cli/src/lib/client.ts b/cli/src/lib/client.ts new file mode 100644 index 0000000..8b884be --- /dev/null +++ b/cli/src/lib/client.ts @@ -0,0 +1,19 @@ +/** + * Builds a ZeroIDClient from the active profile or env vars. + */ + +import { ZeroIDClient } from "@highflame/sdk"; +import { requireProfile, type Profile } from "./config.js"; + +export function makeClientFromProfile(profile: Profile): ZeroIDClient { + return new ZeroIDClient({ + baseUrl: profile.base_url, + accountId: profile.account_id, + projectId: profile.project_id, + apiKey: profile.api_key, + }); +} + +export function makeClient(profileName?: string): ZeroIDClient { + return makeClientFromProfile(requireProfile(profileName)); +} diff --git a/cli/src/lib/config.ts b/cli/src/lib/config.ts new file mode 100644 index 0000000..756f47f --- /dev/null +++ b/cli/src/lib/config.ts @@ -0,0 +1,106 @@ +/** + * Config management — reads/writes ~/.config/zid/config.json. + * Supports named profiles; "default" is used when no --profile is given. + */ + +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { homedir } from "node:os"; +import { join } from "node:path"; + +export interface Profile { + base_url: string; + /** Required for tenant-scoped operations (agents, creds, signals). */ + account_id?: string; + /** Required for tenant-scoped operations (agents, creds, signals). */ + project_id?: string; + api_key: string; +} + +interface ConfigFile { + active_profile: string; + profiles: Record; +} + +const CONFIG_DIR = join(homedir(), ".config", "zid"); +const CONFIG_PATH = join(CONFIG_DIR, "config.json"); + +function _read(): ConfigFile { + if (!existsSync(CONFIG_PATH)) { + return { active_profile: "", profiles: {} }; + } + try { + return JSON.parse(readFileSync(CONFIG_PATH, "utf8")) as ConfigFile; + } catch { + throw new Error( + `Config file is corrupted (${CONFIG_PATH}). Delete it and run "zid init" to start fresh.`, + ); + } +} + +function _write(cfg: ConfigFile): void { + mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 }); + writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + "\n", { encoding: "utf8", mode: 0o600 }); +} + +export function getProfile(name?: string): Profile | undefined { + const cfg = _read(); + const key = name ?? cfg.active_profile; + return cfg.profiles[key]; +} + +export function setProfile(name: string, profile: Profile): void { + const cfg = _read(); + cfg.profiles[name] = profile; + // Auto-activate the first profile ever saved, or when the active profile has been deleted. + if (!cfg.active_profile || !cfg.profiles[cfg.active_profile]) { + cfg.active_profile = name; + } + _write(cfg); +} + +export function useProfile(name: string): void { + const cfg = _read(); + if (!cfg.profiles[name]) { + throw new Error(`Profile "${name}" not found`); + } + cfg.active_profile = name; + _write(cfg); +} + +export function listProfiles(): { name: string; active: boolean }[] { + const cfg = _read(); + return Object.keys(cfg.profiles).map((name) => ({ + name, + active: name === cfg.active_profile, + })); +} + +export function requireProfile(name?: string): Profile { + // Env vars take precedence over config file — useful in CI. + const fromEnv = profileFromEnv(); + if (fromEnv) return fromEnv; + + const profile = getProfile(name); + if (!profile) { + throw new Error( + 'No profile configured. Run "zid init" or set the ZID_API_KEY env var.', + ); + } + return profile; +} + +function profileFromEnv(): Profile | undefined { + const api_key = process.env["ZID_API_KEY"]; + if (!api_key) return undefined; + return { + api_key, + account_id: process.env["ZID_ACCOUNT_ID"], + project_id: process.env["ZID_PROJECT_ID"], + base_url: process.env["ZID_BASE_URL"] ?? "https://api.zeroid.io", + }; +} + +/** Write api_key to .env.zeroid in the current working directory. */ +export function writeEnvFile(apiKey: string): void { + writeFileSync(join(process.cwd(), ".env.zeroid"), `ZID_API_KEY=${apiKey}\n`, "utf8"); +} diff --git a/cli/src/lib/output.ts b/cli/src/lib/output.ts new file mode 100644 index 0000000..8b27439 --- /dev/null +++ b/cli/src/lib/output.ts @@ -0,0 +1,58 @@ +/** + * Output helpers — table and JSON rendering, consistent error formatting. + */ + +import chalk from "chalk"; +import Table from "cli-table3"; + +export function printTable(headers: string[], rows: string[][]): void { + const table = new Table({ + head: headers.map((h) => chalk.bold(h)), + style: { head: [], border: [] }, + }); + for (const row of rows) { + table.push(row); + } + console.log(table.toString()); +} + +export function printJSON(data: unknown): void { + console.log(JSON.stringify(data, null, 2)); +} + +export function printSuccess(message: string): void { + console.log(chalk.green("✓") + " " + message); +} + +export function printWarning(message: string): void { + console.warn(chalk.yellow("⚠") + " " + message); +} + +export function printError(message: string): void { + console.error(chalk.red("✗") + " " + message); +} + +/** + * Format an ISO timestamp as a short relative string for table display. + * Handles both past ("2h ago") and future ("in 10m") timestamps. + */ +export function relativeTime(iso: string): string { + const delta = Date.now() - new Date(iso).getTime(); + const abs = Math.abs(delta); + const future = delta < 0; + + let rel: string; + if (abs < 60_000) rel = `${Math.floor(abs / 1000)}s`; + else if (abs < 3_600_000) rel = `${Math.floor(abs / 60_000)}m`; + else if (abs < 86_400_000) rel = `${Math.floor(abs / 3_600_000)}h`; + else rel = `${Math.floor(abs / 86_400_000)}d`; + + return future ? `in ${rel}` : `${rel} ago`; +} + +/** Handle a CLI command error — print and exit 1. */ +export function handleError(err: unknown): never { + const msg = err instanceof Error ? err.message : String(err); + printError(msg); + process.exit(1); +} diff --git a/cli/tests/commands/agents.test.ts b/cli/tests/commands/agents.test.ts new file mode 100644 index 0000000..b1aea80 --- /dev/null +++ b/cli/tests/commands/agents.test.ts @@ -0,0 +1,269 @@ +/** + * Tests for `zid agents` subcommands: list, get, rotate-key, deactivate, activate. + */ + +import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import { runCLI, BASE_URL } from "../helpers.js"; +import type { AgentResponse, AgentRegistered, AgentListResponse } from "@highflame/sdk"; + +const server = setupServer(); +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +// --------------------------------------------------------------------------- +// Fixtures +// --------------------------------------------------------------------------- + +const AGENT: AgentResponse = { + id: "agt_abc123", + account_id: "acct_test", + project_id: "proj_test", + name: "github-mcp-server", + external_id: "github-mcp-server", + wimse_uri: "wimse:agent:acct_test/proj_test/github-mcp-server", + api_key_prefix: "zid_sk_abc", + identity_type: "mcp_server", + sub_type: "tool_agent", + trust_level: "first_party", + status: "active", + framework: "mcp", + version: "", + publisher: "", + description: "GitHub MCP server", + capabilities: null, + labels: null, + metadata: null, + created_at: new Date(Date.now() - 2 * 3600 * 1000).toISOString(), + created_by: "user_xyz", + updated_at: new Date().toISOString(), +}; + +const AGENT_LIST: AgentListResponse = { + agents: [AGENT], + total: 1, + limit: 50, + offset: 0, +}; + +const ROTATED: AgentRegistered = { + identity: { ...AGENT, updated_at: new Date().toISOString() }, + api_key: "zid_sk_newkey123", +}; + +// --------------------------------------------------------------------------- +// agents list +// --------------------------------------------------------------------------- + +describe("zid agents list", () => { + it("GET /api/v1/agents/registry and renders a table", async () => { + server.use(http.get(`${BASE_URL}/api/v1/agents/registry`, () => HttpResponse.json(AGENT_LIST))); + const { stdout, exitCode } = await runCLI(["agents", "list"]); + expect(exitCode).toBeUndefined(); + const out = stdout.join("\n"); + expect(out).toContain("github-mcp-server"); + expect(out).toContain("mcp_server"); + expect(out).toContain("first_party"); + expect(out).toContain("active"); + }); + + it("forwards --type as query param", async () => { + let qs = ""; + server.use( + http.get(`${BASE_URL}/api/v1/agents/registry`, ({ request }) => { + qs = new URL(request.url).search; + return HttpResponse.json(AGENT_LIST); + }), + ); + await runCLI(["agents", "list", "--type", "mcp_server"]); + expect(qs).toContain("identity_type=mcp_server"); + }); + + it("outputs a JSON array with --json", async () => { + server.use(http.get(`${BASE_URL}/api/v1/agents/registry`, () => HttpResponse.json(AGENT_LIST))); + const { stdout } = await runCLI(["agents", "list", "--json"]); + const parsed = JSON.parse(stdout.join("")) as AgentResponse[]; + expect(Array.isArray(parsed)).toBe(true); + expect(parsed[0]?.id).toBe("agt_abc123"); + }); + + it("prints 'No agents found' when list is empty", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/agents/registry`, () => + HttpResponse.json({ agents: [], total: 0, limit: 50, offset: 0 }), + ), + ); + const { stdout } = await runCLI(["agents", "list"]); + expect(stdout.join("")).toMatch(/no agents/i); + }); + + it("exits 1 on API error", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/agents/registry`, () => + HttpResponse.json({ title: "Unauthorized" }, { status: 401 }), + ), + ); + const { exitCode } = await runCLI(["agents", "list"]); + expect(exitCode).toBe(1); + }); +}); + +// --------------------------------------------------------------------------- +// agents get +// --------------------------------------------------------------------------- + +describe("zid agents get", () => { + it("GET /api/v1/agents/registry/:id and prints agent details", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/agents/registry/agt_abc123`, () => HttpResponse.json(AGENT)), + ); + const { stdout, exitCode } = await runCLI(["agents", "get", "agt_abc123"]); + expect(exitCode).toBeUndefined(); + const out = stdout.join("\n"); + expect(out).toContain("github-mcp-server"); + expect(out).toContain("wimse:agent:acct_test/proj_test/github-mcp-server"); + expect(out).toContain("mcp_server"); + }); + + it("outputs raw JSON with --json", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/agents/registry/agt_abc123`, () => HttpResponse.json(AGENT)), + ); + const { stdout } = await runCLI(["agents", "get", "--json", "agt_abc123"]); + const parsed = JSON.parse(stdout.join("")) as AgentResponse; + expect(parsed.id).toBe("agt_abc123"); + expect(parsed.wimse_uri).toBe("wimse:agent:acct_test/proj_test/github-mcp-server"); + }); + + it("exits 1 when agent not found", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/agents/registry/ghost`, () => + HttpResponse.json({ title: "Not Found", detail: "identity not found" }, { status: 404 }), + ), + ); + const { exitCode } = await runCLI(["agents", "get", "ghost"]); + expect(exitCode).toBe(1); + }); +}); + +// --------------------------------------------------------------------------- +// agents rotate-key +// --------------------------------------------------------------------------- + +describe("zid agents rotate-key", () => { + it("POST /api/v1/agents/registry/:id/rotate-key and prints new key", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/agt_abc123/rotate-key`, () => + HttpResponse.json(ROTATED), + ), + ); + const { stdout, stderr, exitCode } = await runCLI(["agents", "rotate-key", "agt_abc123"]); + expect(exitCode).toBeUndefined(); + expect(stdout.join("\n")).toContain("zid_sk_newkey123"); + // Should warn about storing the key + expect(stderr.join("")).toMatch(/securely/i); + }); + + it("outputs raw JSON with --json", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/agt_abc123/rotate-key`, () => + HttpResponse.json(ROTATED), + ), + ); + const { stdout } = await runCLI(["agents", "rotate-key", "--json", "agt_abc123"]); + const parsed = JSON.parse(stdout.join("")) as AgentRegistered; + // SDK type check: AgentRegistered uses .identity not .agent + expect(parsed.identity.id).toBe("agt_abc123"); + expect(parsed.api_key).toBe("zid_sk_newkey123"); + }); + + it("exits 1 when agent not found", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/ghost/rotate-key`, () => + HttpResponse.json({ title: "Not Found", detail: "identity not found" }, { status: 404 }), + ), + ); + const { exitCode } = await runCLI(["agents", "rotate-key", "ghost"]); + expect(exitCode).toBe(1); + }); +}); + +// --------------------------------------------------------------------------- +// agents deactivate / activate +// --------------------------------------------------------------------------- + +describe("zid agents deactivate", () => { + it("POST /api/v1/agents/registry/:id/deactivate", async () => { + const deactivated = { ...AGENT, status: "deactivated" as const }; + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/agt_abc123/deactivate`, () => + HttpResponse.json(deactivated), + ), + ); + const { stdout, exitCode } = await runCLI(["agents", "deactivate", "agt_abc123"]); + expect(exitCode).toBeUndefined(); + expect(stdout.join("")).toMatch(/deactivated/i); + }); + + it("outputs raw JSON with --json", async () => { + const deactivated = { ...AGENT, status: "deactivated" as const }; + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/agt_abc123/deactivate`, () => + HttpResponse.json(deactivated), + ), + ); + const { stdout, exitCode } = await runCLI(["agents", "deactivate", "--json", "agt_abc123"]); + expect(exitCode).toBeUndefined(); + const parsed = JSON.parse(stdout.join("")) as AgentResponse; + expect(parsed.id).toBe("agt_abc123"); + expect(parsed.status).toBe("deactivated"); + }); + + it("exits 1 on API error", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/ghost/deactivate`, () => + HttpResponse.json({ title: "Not Found" }, { status: 404 }), + ), + ); + const { exitCode } = await runCLI(["agents", "deactivate", "ghost"]); + expect(exitCode).toBe(1); + }); +}); + +describe("zid agents activate", () => { + it("POST /api/v1/agents/registry/:id/activate", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/agt_abc123/activate`, () => + HttpResponse.json(AGENT), + ), + ); + const { stdout, exitCode } = await runCLI(["agents", "activate", "agt_abc123"]); + expect(exitCode).toBeUndefined(); + expect(stdout.join("")).toMatch(/activated/i); + }); + + it("outputs raw JSON with --json", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/agt_abc123/activate`, () => + HttpResponse.json(AGENT), + ), + ); + const { stdout, exitCode } = await runCLI(["agents", "activate", "--json", "agt_abc123"]); + expect(exitCode).toBeUndefined(); + const parsed = JSON.parse(stdout.join("")) as AgentResponse; + expect(parsed.id).toBe("agt_abc123"); + expect(parsed.status).toBe("active"); + }); + + it("exits 1 on API error", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/registry/ghost/activate`, () => + HttpResponse.json({ title: "Not Found" }, { status: 404 }), + ), + ); + const { exitCode } = await runCLI(["agents", "activate", "ghost"]); + expect(exitCode).toBe(1); + }); +}); diff --git a/cli/tests/commands/config.test.ts b/cli/tests/commands/config.test.ts new file mode 100644 index 0000000..6ee6c69 --- /dev/null +++ b/cli/tests/commands/config.test.ts @@ -0,0 +1,72 @@ +/** + * Tests for `zid config use-profile` and `zid config list-profiles`. + * + * Uses a temp HOME dir so reads/writes don't touch the real config file. + */ + +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { mkdirSync, rmSync } from "node:fs"; +import { join } from "node:path"; +import { runCLI } from "../helpers.js"; + +const TEST_HOME = vi.hoisted( + () => `${process.env.TMPDIR ?? process.env.TEMP ?? "/tmp"}zid-config-cmd-test-${process.pid}`, +); +vi.mock("node:os", async (importOriginal) => { + const actual = await importOriginal(); + return { ...actual, homedir: () => TEST_HOME }; +}); + +// Import config functions after mock is in place so they use TEST_HOME. +const { setProfile } = await import("../../src/lib/config.js"); + +beforeEach(() => { + mkdirSync(join(TEST_HOME, ".config", "zid"), { recursive: true }); +}); + +afterEach(() => { + rmSync(TEST_HOME, { recursive: true, force: true }); +}); + +describe("zid config list-profiles", () => { + it("prints a message when no profiles are configured", async () => { + const { stdout } = await runCLI(["config", "list-profiles"], { + // Clear auth env so it doesn't mask the empty config + ZID_API_KEY: "", + }); + expect(stdout.join("")).toMatch(/no profiles/i); + }); + + it("lists configured profiles and marks the active one", async () => { + setProfile("dev", { base_url: "http://dev", account_id: "a", project_id: "p", api_key: "k1" }); + setProfile("prod", { base_url: "http://prod", account_id: "a", project_id: "p", api_key: "k2" }); + + const { stdout } = await runCLI(["config", "list-profiles"]); + const out = stdout.join("\n"); + expect(out).toContain("dev"); + expect(out).toContain("prod"); + // First profile saved is auto-activated + expect(out).toContain("* dev"); + }); +}); + +describe("zid config use-profile", () => { + it("switches the active profile and prints confirmation", async () => { + setProfile("dev", { base_url: "http://dev", account_id: "a", project_id: "p", api_key: "k1" }); + setProfile("prod", { base_url: "http://prod", account_id: "a", project_id: "p", api_key: "k2" }); + + const { stdout, exitCode } = await runCLI(["config", "use-profile", "prod"]); + expect(exitCode).toBeUndefined(); + expect(stdout.join("")).toContain("prod"); + + // Verify it's now active by listing + const { stdout: list } = await runCLI(["config", "list-profiles"]); + expect(list.join("\n")).toContain("* prod"); + }); + + it("exits 1 when the profile does not exist", async () => { + const { exitCode, stderr } = await runCLI(["config", "use-profile", "ghost"]); + expect(exitCode).toBe(1); + expect(stderr.join("")).toMatch(/not found/i); + }); +}); diff --git a/cli/tests/commands/creds.test.ts b/cli/tests/commands/creds.test.ts new file mode 100644 index 0000000..3849f77 --- /dev/null +++ b/cli/tests/commands/creds.test.ts @@ -0,0 +1,130 @@ +/** + * Tests for `zid creds list`. + */ + +import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import { runCLI, BASE_URL } from "../helpers.js"; +import type { CredentialListResponse, IssuedCredential } from "@highflame/sdk"; + +const server = setupServer(); +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +// --------------------------------------------------------------------------- +// Fixtures +// --------------------------------------------------------------------------- + +const now = new Date().toISOString(); +const past = new Date(Date.now() - 30 * 60 * 1000).toISOString(); +const future = new Date(Date.now() + 10 * 60 * 1000).toISOString(); + +const ACTIVE_CRED: IssuedCredential = { + id: "cred_abc", + identity_id: "agt_abc123", + account_id: "acct_test", + project_id: "proj_test", + jti: "jti_abc", + subject: "wimse:agent:acct_test/proj_test/test", + issued_at: past, + expires_at: future, + ttl_seconds: 900, + scopes: ["repo:read", "pr:write"], + is_revoked: false, + grant_type: "api_key", + delegation_depth: 0, +}; + +const REVOKED_CRED: IssuedCredential = { + ...ACTIVE_CRED, + id: "cred_xyz", + is_revoked: true, + revoked_at: now, + revoke_reason: "manual", +}; + +const LIST_RESPONSE: CredentialListResponse = { + credentials: [ACTIVE_CRED, REVOKED_CRED], + total: 2, +}; + +describe("zid creds list", () => { + it("GET /api/v1/credentials?identity_id= and renders table", async () => { + let capturedUrl = ""; + server.use( + http.get(`${BASE_URL}/api/v1/credentials`, ({ request }) => { + capturedUrl = request.url; + return HttpResponse.json(LIST_RESPONSE); + }), + ); + + const { stdout, exitCode } = await runCLI(["creds", "list", "--agent", "agt_abc123"]); + + expect(exitCode).toBeUndefined(); + expect(capturedUrl).toContain("identity_id=agt_abc123"); + const out = stdout.join("\n"); + expect(out).toContain("cred_abc"); + expect(out).toContain("active"); + expect(out).toContain("cred_xyz"); + expect(out).toContain("revoked"); + }); + + it("--active filters out revoked credentials", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/credentials`, () => HttpResponse.json(LIST_RESPONSE)), + ); + + const { stdout } = await runCLI(["creds", "list", "--agent", "agt_abc123", "--active"]); + const out = stdout.join("\n"); + expect(out).toContain("cred_abc"); + expect(out).not.toContain("cred_xyz"); + expect(out).toContain("1 credential(s)"); + }); + + it("shows scopes in the table", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/credentials`, () => HttpResponse.json(LIST_RESPONSE)), + ); + const { stdout } = await runCLI(["creds", "list", "--agent", "agt_abc123"]); + expect(stdout.join("\n")).toContain("repo:read pr:write"); + }); + + it("outputs raw JSON with --json", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/credentials`, () => HttpResponse.json(LIST_RESPONSE)), + ); + const { stdout } = await runCLI(["creds", "list", "--agent", "agt_abc123", "--json"]); + const parsed = JSON.parse(stdout.join("")) as IssuedCredential[]; + expect(Array.isArray(parsed)).toBe(true); + expect(parsed).toHaveLength(2); + // SDK type contract: is_revoked field must exist + expect(typeof parsed[0]?.is_revoked).toBe("boolean"); + }); + + it("prints 'No credentials found' when list is empty", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/credentials`, () => + HttpResponse.json({ credentials: [], total: 0 }), + ), + ); + const { stdout } = await runCLI(["creds", "list", "--agent", "agt_abc123"]); + expect(stdout.join("")).toMatch(/no credentials/i); + }); + + it("exits 1 when --agent is missing", async () => { + const { exitCode } = await runCLI(["creds", "list"]); + expect(exitCode).toBe(1); + }); + + it("exits 1 on API error", async () => { + server.use( + http.get(`${BASE_URL}/api/v1/credentials`, () => + HttpResponse.json({ title: "Not Found" }, { status: 404 }), + ), + ); + const { exitCode } = await runCLI(["creds", "list", "--agent", "ghost"]); + expect(exitCode).toBe(1); + }); +}); diff --git a/cli/tests/commands/init.test.ts b/cli/tests/commands/init.test.ts new file mode 100644 index 0000000..ef8a4dc --- /dev/null +++ b/cli/tests/commands/init.test.ts @@ -0,0 +1,165 @@ +/** + * Tests for `zid init`. + */ + +import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import { rmSync } from "node:fs"; +import { join } from "node:path"; +import { runCLI, BASE_URL } from "../helpers.js"; +import type { AgentRegistered, AgentResponse } from "@highflame/sdk"; + +const server = setupServer(); +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +const AGENT: AgentResponse = { + id: "agt_new123", + account_id: "acct_test", + project_id: "proj_test", + name: "github-mcp-server", + external_id: "github-mcp-server", + wimse_uri: "wimse:agent:acct_test/proj_test/github-mcp-server", + api_key_prefix: "zid_sk_abc", + identity_type: "mcp_server", + sub_type: "tool_agent", + trust_level: "first_party", + status: "active", + framework: "mcp", + version: "", + publisher: "", + description: "", + capabilities: null, + labels: null, + metadata: null, + created_at: new Date().toISOString(), + created_by: "user_xyz", + updated_at: new Date().toISOString(), +}; + +const REGISTER_RESPONSE: AgentRegistered = { + identity: AGENT, + api_key: "zid_sk_brand_new", +}; + +// Cleanup .env.zeroid written by init +afterEach(() => rmSync(join(process.cwd(), ".env.zeroid"), { force: true })); + +describe("zid init", () => { + it("POST /api/v1/agents/register with correct fields", async () => { + let captured: Record = {}; + server.use( + http.post(`${BASE_URL}/api/v1/agents/register`, async ({ request }) => { + captured = (await request.json()) as Record; + return HttpResponse.json(REGISTER_RESPONSE); + }), + ); + + await runCLI(["init", "--name", "github-mcp-server", "--type", "mcp_server"]); + + expect(captured["name"]).toBe("github-mcp-server"); + expect(captured["external_id"]).toBe("github-mcp-server"); + expect(captured["identity_type"]).toBe("mcp_server"); + }); + + it("uses --id as external_id when provided", async () => { + let captured: Record = {}; + server.use( + http.post(`${BASE_URL}/api/v1/agents/register`, async ({ request }) => { + captured = (await request.json()) as Record; + return HttpResponse.json(REGISTER_RESPONSE); + }), + ); + + await runCLI(["init", "--name", "My Agent", "--id", "my-agent-001"]); + + expect(captured["external_id"]).toBe("my-agent-001"); + expect(captured["name"]).toBe("My Agent"); + }); + + it("includes framework and description when provided", async () => { + let captured: Record = {}; + server.use( + http.post(`${BASE_URL}/api/v1/agents/register`, async ({ request }) => { + captured = (await request.json()) as Record; + return HttpResponse.json(REGISTER_RESPONSE); + }), + ); + + await runCLI([ + "init", "--name", "agent", + "--framework", "langchain", + "--description", "A code reviewer", + ]); + + expect(captured["framework"]).toBe("langchain"); + expect(captured["description"]).toBe("A code reviewer"); + }); + + it("prints WIMSE URI and API key on success", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/register`, () => HttpResponse.json(REGISTER_RESPONSE)), + ); + + const { stdout, stderr } = await runCLI(["init", "--name", "github-mcp-server"]); + const out = stdout.join("\n"); + expect(out).toContain("wimse:agent:acct_test/proj_test/github-mcp-server"); + expect(out).toContain("zid_sk_brand_new"); + // Should warn about storing the key + expect(stderr.join("")).toMatch(/securely/i); + }); + + it("writes .env.zeroid with the api_key", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/register`, () => HttpResponse.json(REGISTER_RESPONSE)), + ); + + await runCLI(["init", "--name", "github-mcp-server"]); + + const { readFileSync } = await import("node:fs"); + const content = readFileSync(join(process.cwd(), ".env.zeroid"), "utf8"); + expect(content).toBe("ZID_API_KEY=zid_sk_brand_new\n"); + }); + + it("outputs raw JSON with --json", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/register`, () => HttpResponse.json(REGISTER_RESPONSE)), + ); + + const { stdout } = await runCLI(["init", "--name", "agent", "--json"]); + const parsed = JSON.parse(stdout.join("")) as AgentRegistered; + // SDK type contract: must be .identity not .agent + expect(parsed.identity.id).toBe("agt_new123"); + expect(parsed.api_key).toBe("zid_sk_brand_new"); + }); + + it("still writes .env.zeroid when --json is given", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/register`, () => HttpResponse.json(REGISTER_RESPONSE)), + ); + + await runCLI(["init", "--name", "agent", "--json"]); + + const { readFileSync } = await import("node:fs"); + const content = readFileSync(join(process.cwd(), ".env.zeroid"), "utf8"); + expect(content).toBe("ZID_API_KEY=zid_sk_brand_new\n"); + }); + + it("exits 1 on conflict (agent already exists)", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/agents/register`, () => + HttpResponse.json({ title: "Conflict", detail: "agent already exists" }, { status: 409 }), + ), + ); + const { exitCode, stderr } = await runCLI(["init", "--name", "duplicate"]); + expect(exitCode).toBe(1); + expect(stderr.join("")).toMatch(/already exists/i); + }); + + it("exits 1 when --name is missing", async () => { + const { exitCode } = await runCLI(["init"]); + expect(exitCode).toBe(1); + }); +}); diff --git a/cli/tests/commands/signal.test.ts b/cli/tests/commands/signal.test.ts new file mode 100644 index 0000000..5c7e2eb --- /dev/null +++ b/cli/tests/commands/signal.test.ts @@ -0,0 +1,159 @@ +/** + * Tests for `zid signal`. + */ + +import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import { runCLI, BASE_URL } from "../helpers.js"; +import type { CAESignal, CreateSignalRequest } from "@highflame/sdk"; + +const server = setupServer(); +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +const SIGNAL_RESPONSE: CAESignal = { + id: "sig_abc123", + account_id: "acct_test", + project_id: "proj_test", + identity_id: "agt_abc123", + signal_type: "anomalous_behavior", + severity: "high", + source: "security-monitor", + created_at: new Date().toISOString(), +}; + +describe("zid signal", () => { + it("POST /api/v1/signals/ingest with all required fields", async () => { + let captured: CreateSignalRequest = {} as CreateSignalRequest; + server.use( + http.post(`${BASE_URL}/api/v1/signals/ingest`, async ({ request }) => { + captured = (await request.json()) as CreateSignalRequest; + return HttpResponse.json(SIGNAL_RESPONSE); + }), + ); + + await runCLI([ + "signal", + "--agent", "agt_abc123", + "--type", "anomalous_behavior", + "--severity", "high", + "--source", "security-monitor", + ]); + + expect(captured.identity_id).toBe("agt_abc123"); + expect(captured.signal_type).toBe("anomalous_behavior"); + expect(captured.severity).toBe("high"); + expect(captured.source).toBe("security-monitor"); + }); + + it("stores --reason in payload.reason", async () => { + let captured: CreateSignalRequest = {} as CreateSignalRequest; + server.use( + http.post(`${BASE_URL}/api/v1/signals/ingest`, async ({ request }) => { + captured = (await request.json()) as CreateSignalRequest; + return HttpResponse.json(SIGNAL_RESPONSE); + }), + ); + + await runCLI([ + "signal", + "--agent", "agt_abc123", + "--type", "policy_violation", + "--severity", "critical", + "--source", "siem", + "--reason", "unexpected outbound call", + ]); + + expect(captured.payload).toEqual({ reason: "unexpected outbound call" }); + }); + + it("omits payload when --reason is not given", async () => { + let captured: CreateSignalRequest = {} as CreateSignalRequest; + server.use( + http.post(`${BASE_URL}/api/v1/signals/ingest`, async ({ request }) => { + captured = (await request.json()) as CreateSignalRequest; + return HttpResponse.json(SIGNAL_RESPONSE); + }), + ); + + await runCLI([ + "signal", + "--agent", "agt_abc123", + "--type", "ip_change", + "--severity", "low", + "--source", "monitor", + ]); + + expect(captured.payload).toBeUndefined(); + }); + + it("prints signal ID on success", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/signals/ingest`, () => HttpResponse.json(SIGNAL_RESPONSE)), + ); + const { stdout, exitCode } = await runCLI([ + "signal", + "--agent", "agt_abc123", + "--type", "anomalous_behavior", + "--severity", "high", + "--source", "monitor", + ]); + expect(exitCode).toBeUndefined(); + expect(stdout.join("\n")).toContain("sig_abc123"); + }); + + it("outputs raw JSON with --json", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/signals/ingest`, () => HttpResponse.json(SIGNAL_RESPONSE)), + ); + const { stdout } = await runCLI([ + "signal", + "--agent", "agt_abc123", + "--type", "anomalous_behavior", + "--severity", "high", + "--source", "monitor", + "--json", + ]); + const parsed = JSON.parse(stdout.join("")) as CAESignal; + expect(parsed.id).toBe("sig_abc123"); + expect(parsed.signal_type).toBe("anomalous_behavior"); + }); + + it("exits 1 when --agent is missing", async () => { + const { exitCode } = await runCLI([ + "signal", + "--type", "anomalous_behavior", + "--severity", "high", + "--source", "monitor", + ]); + expect(exitCode).toBe(1); + }); + + it("exits 1 when --source is missing", async () => { + const { exitCode } = await runCLI([ + "signal", + "--agent", "agt_abc123", + "--type", "anomalous_behavior", + "--severity", "high", + ]); + expect(exitCode).toBe(1); + }); + + it("exits 1 on API error", async () => { + server.use( + http.post(`${BASE_URL}/api/v1/signals/ingest`, () => + HttpResponse.json({ title: "Bad Request" }, { status: 400 }), + ), + ); + const { exitCode } = await runCLI([ + "signal", + "--agent", "agt_abc123", + "--type", "anomalous_behavior", + "--severity", "high", + "--source", "monitor", + ]); + expect(exitCode).toBe(1); + }); +}); diff --git a/cli/tests/commands/token/decode.test.ts b/cli/tests/commands/token/decode.test.ts new file mode 100644 index 0000000..fa05f91 --- /dev/null +++ b/cli/tests/commands/token/decode.test.ts @@ -0,0 +1,87 @@ +/** + * Tests for `zid token decode`. + * + * Pure local operation — no network calls, no msw needed. + */ + +import { describe, expect, it } from "vitest"; +import { runCLI, makeJWT } from "../../helpers.js"; + +describe("zid token decode", () => { + it("decodes a valid JWT and prints key claims", async () => { + const now = Math.floor(Date.now() / 1000); + const jwt = makeJWT({ + sub: "wimse:agent:acct_test/proj_test/my-agent", + identity_type: "agent", + trust_level: "first_party", + grant_type: "api_key", + scopes: ["repo:read", "pr:write"], + exp: now + 600, + }); + + const { stdout, exitCode } = await runCLI(["token", "decode", jwt]); + + expect(exitCode).toBeUndefined(); + expect(stdout.join("\n")).toContain("wimse:agent:acct_test/proj_test/my-agent"); + expect(stdout.join("\n")).toContain("agent"); + expect(stdout.join("\n")).toContain("first_party"); + expect(stdout.join("\n")).toContain("repo:read pr:write"); + }); + + it("outputs raw JSON with --json flag", async () => { + const jwt = makeJWT({ sub: "wimse:agent:acct/proj/x", scopes: ["read"] }); + const { stdout, exitCode } = await runCLI(["token", "decode", "--json", jwt]); + + expect(exitCode).toBeUndefined(); + const parsed = JSON.parse(stdout.join("")) as { header: unknown; payload: unknown }; + expect(parsed).toHaveProperty("header"); + expect(parsed).toHaveProperty("payload"); + expect((parsed.payload as Record)["sub"]).toBe("wimse:agent:acct/proj/x"); + }); + + it("shows alg and kid from header", async () => { + const jwt = makeJWT({ alg: "RS256", kid: "rsa-key-2025" }); + const { stdout } = await runCLI(["token", "decode", jwt]); + const out = stdout.join("\n"); + expect(out).toContain("RS256"); + expect(out).toContain("rsa-key-2025"); + }); + + it("shows delegation info when act claim present", async () => { + const jwt = makeJWT({ + delegation_depth: 1, + act: { sub: "wimse:agent:acct_test/proj_test/orchestrator" }, + }); + const { stdout } = await runCLI(["token", "decode", jwt]); + expect(stdout.join("\n")).toContain("orchestrator"); + expect(stdout.join("\n")).toContain("delegation_depth"); + }); + + it("shows custom claims not in the known set", async () => { + const jwt = makeJWT({ extra: { my_custom_claim: "hello" } }); + const { stdout } = await runCLI(["token", "decode", jwt]); + expect(stdout.join("\n")).toContain("my_custom_claim"); + expect(stdout.join("\n")).toContain("hello"); + }); + + it("marks expired tokens in the exp line", async () => { + const past = Math.floor(Date.now() / 1000) - 300; + const jwt = makeJWT({ exp: past }); + const { stdout, exitCode } = await runCLI(["token", "decode", jwt]); + // decode does not exit 1 on expired — it just shows the claims + expect(exitCode).toBeUndefined(); + expect(stdout.join("\n")).toContain("ago"); + }); + + it("exits 1 on a token with wrong number of parts", async () => { + const { exitCode, stderr } = await runCLI(["token", "decode", "only.two"]); + expect(exitCode).toBe(1); + expect(stderr.join("")).toMatch(/malformed/i); + }); + + it("exits 1 when payload is not valid base64url JSON", async () => { + const { exitCode, stderr } = await runCLI(["token", "decode", "aGVhZA.!!!.c2ln"]); + expect(exitCode).toBe(1); + expect(stderr.join("")).toMatch(/SyntaxError|invalid|unexpected/i); + }); +}); diff --git a/cli/tests/commands/token/issue.test.ts b/cli/tests/commands/token/issue.test.ts new file mode 100644 index 0000000..7b4a80d --- /dev/null +++ b/cli/tests/commands/token/issue.test.ts @@ -0,0 +1,96 @@ +/** + * Tests for `zid token issue`. + */ + +import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import { runCLI, BASE_URL } from "../../helpers.js"; +import type { AccessToken } from "@highflame/sdk"; + +const server = setupServer(); +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +const TOKEN_RESPONSE: AccessToken = { + access_token: "eyJhbGciOiJFUzI1NiJ9.test.sig", + token_type: "Bearer", + expires_in: 900, + jti: "jti_abc", + iat: Math.floor(Date.now() / 1000), + account_id: "acct_test", + project_id: "proj_test", +}; + +describe("zid token issue", () => { + it("posts to /oauth2/token with api_key grant", async () => { + let captured: Record = {}; + server.use( + http.post(`${BASE_URL}/oauth2/token`, async ({ request }) => { + captured = (await request.json()) as Record; + return HttpResponse.json(TOKEN_RESPONSE); + }), + ); + + await runCLI(["token", "issue"]); + + expect(captured["grant_type"]).toBe("api_key"); + expect(captured["api_key"]).toBe("zid_sk_test"); + }); + + it("includes scope when --scope is given", async () => { + let captured: Record = {}; + server.use( + http.post(`${BASE_URL}/oauth2/token`, async ({ request }) => { + captured = (await request.json()) as Record; + return HttpResponse.json(TOKEN_RESPONSE); + }), + ); + + await runCLI(["token", "issue", "--scope", "repo:read pr:write"]); + + expect(captured["scope"]).toBe("repo:read pr:write"); + }); + + it("omits scope field when --scope is empty", async () => { + let captured: Record = {}; + server.use( + http.post(`${BASE_URL}/oauth2/token`, async ({ request }) => { + captured = (await request.json()) as Record; + return HttpResponse.json(TOKEN_RESPONSE); + }), + ); + + await runCLI(["token", "issue"]); + + expect(captured["scope"]).toBeUndefined(); + }); + + it("prints access_token and expires_in", async () => { + server.use(http.post(`${BASE_URL}/oauth2/token`, () => HttpResponse.json(TOKEN_RESPONSE))); + const { stdout } = await runCLI(["token", "issue"]); + const out = stdout.join("\n"); + expect(out).toContain(TOKEN_RESPONSE.access_token); + expect(out).toContain("900s"); + }); + + it("outputs raw JSON with --json", async () => { + server.use(http.post(`${BASE_URL}/oauth2/token`, () => HttpResponse.json(TOKEN_RESPONSE))); + const { stdout } = await runCLI(["token", "issue", "--json"]); + const parsed = JSON.parse(stdout.join("")) as AccessToken; + expect(parsed.access_token).toBe(TOKEN_RESPONSE.access_token); + expect(parsed.expires_in).toBe(900); + }); + + it("exits 1 on API error", async () => { + server.use( + http.post(`${BASE_URL}/oauth2/token`, () => + HttpResponse.json({ error: "invalid_grant", error_description: "API key invalid" }, { status: 401 }), + ), + ); + const { exitCode, stderr } = await runCLI(["token", "issue"]); + expect(exitCode).toBe(1); + expect(stderr.join("")).toMatch(/invalid/i); + }); +}); diff --git a/cli/tests/commands/token/revoke.test.ts b/cli/tests/commands/token/revoke.test.ts new file mode 100644 index 0000000..e05b7e6 --- /dev/null +++ b/cli/tests/commands/token/revoke.test.ts @@ -0,0 +1,70 @@ +/** + * Tests for `zid token revoke`. + */ + +import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import { runCLI, BASE_URL } from "../../helpers.js"; + +const server = setupServer(); +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +describe("zid token revoke", () => { + it("posts token to /oauth2/token/revoke", async () => { + let captured: Record = {}; + server.use( + http.post(`${BASE_URL}/oauth2/token/revoke`, async ({ request }) => { + captured = (await request.json()) as Record; + return HttpResponse.json({ revoked: true }); + }), + ); + + await runCLI(["token", "revoke", "eyJhbGc.test.sig"]); + + expect(captured["token"]).toBe("eyJhbGc.test.sig"); + }); + + it("prints success message", async () => { + server.use( + http.post(`${BASE_URL}/oauth2/token/revoke`, () => HttpResponse.json({ revoked: true })), + ); + const { stdout } = await runCLI(["token", "revoke", "eyJhbGc.test.sig"]); + expect(stdout.join("")).toMatch(/revoked/i); + }); + + it("does not send tenant headers", async () => { + let capturedHeaders = new Headers(); + server.use( + http.post(`${BASE_URL}/oauth2/token/revoke`, ({ request }) => { + capturedHeaders = request.headers; + return HttpResponse.json({ revoked: true }); + }), + ); + await runCLI(["token", "revoke", "tok"]); + expect(capturedHeaders.get("x-account-id")).toBeNull(); + expect(capturedHeaders.get("x-project-id")).toBeNull(); + }); + + it("outputs raw JSON with --json", async () => { + server.use( + http.post(`${BASE_URL}/oauth2/token/revoke`, () => HttpResponse.json({ revoked: true })), + ); + const { stdout, exitCode } = await runCLI(["token", "revoke", "--json", "eyJhbGc.test.sig"]); + expect(exitCode).toBeUndefined(); + const parsed = JSON.parse(stdout.join("")) as { revoked: boolean }; + expect(parsed.revoked).toBe(true); + }); + + it("exits 1 on API error", async () => { + server.use( + http.post(`${BASE_URL}/oauth2/token/revoke`, () => + HttpResponse.json({ title: "Not Found" }, { status: 404 }), + ), + ); + const { exitCode } = await runCLI(["token", "revoke", "bad.token"]); + expect(exitCode).toBe(1); + }); +}); diff --git a/cli/tests/commands/token/verify.test.ts b/cli/tests/commands/token/verify.test.ts new file mode 100644 index 0000000..18b1251 --- /dev/null +++ b/cli/tests/commands/token/verify.test.ts @@ -0,0 +1,98 @@ +/** + * Tests for `zid token verify`. + * + * Mocks `makeClient` directly so we control what `tokens.verify()` returns + * without having to spread SDK internals or deal with real JWKS crypto. + */ + +import { describe, expect, it, vi } from "vitest"; +import { ZeroIDError } from "@highflame/sdk"; +import { runCLI, makeJWT } from "../../helpers.js"; + +const VALID_IDENTITY = { + sub: "wimse:agent:acct_test/proj_test/test-agent", + iss: "http://zeroid.test", + aud: ["http://zeroid.test"], + iat: Math.floor(Date.now() / 1000) - 60, + exp: Math.floor(Date.now() / 1000) + 840, + jti: "jti_test", + account_id: "acct_test", + project_id: "proj_test", + identity_type: "agent", + trust_level: "first_party", + scopes: ["repo:read"], + hasScope: (s: string) => ["repo:read"].includes(s), + hasTool: () => false, + isDelegated: () => false, + delegatedBy: () => undefined, +}; + +const DELEGATED_IDENTITY = { + ...VALID_IDENTITY, + isDelegated: () => true, + delegatedBy: () => "wimse:agent:acct_test/proj_test/orchestrator", +}; + +// Mock makeClient so tests never touch the network or JWKS crypto. +vi.mock("../../../src/lib/client.js", () => ({ + makeClient: vi.fn(() => ({ + baseUrl: "http://zeroid.test", + tokens: { + verify: vi.fn(async (token: string) => { + if (token === "expired.jwt.token") { + throw new ZeroIDError("Token has expired", "token_expired"); + } + if (token === "invalid.jwt.token") { + throw new ZeroIDError("Invalid signature", "invalid_signature"); + } + if (token === "delegated.jwt.token") { + return DELEGATED_IDENTITY; + } + return VALID_IDENTITY; + }), + }, + })), +})); + +describe("zid token verify — valid token", () => { + it("exits 0 and prints identity details", async () => { + const jwt = makeJWT(); + const { stdout, exitCode } = await runCLI(["token", "verify", jwt]); + expect(exitCode).toBe(0); + expect(stdout.join("\n")).toContain("valid"); + expect(stdout.join("\n")).toContain("acct_test"); + expect(stdout.join("\n")).toContain("first_party"); + expect(stdout.join("\n")).toContain("repo:read"); + }); + + it("outputs raw JSON with --json", async () => { + const jwt = makeJWT(); + const { stdout, exitCode } = await runCLI(["token", "verify", "--json", jwt]); + expect(exitCode).toBe(0); + const parsed = JSON.parse(stdout.join("")) as Record; + expect(parsed["sub"]).toBe("wimse:agent:acct_test/proj_test/test-agent"); + expect(parsed["account_id"]).toBe("acct_test"); + }); +}); + +describe("zid token verify — delegated token", () => { + it("prints delegated_by when token carries act claim", async () => { + const { stdout, exitCode } = await runCLI(["token", "verify", "delegated.jwt.token"]); + expect(exitCode).toBe(0); + expect(stdout.join("\n")).toContain("wimse:agent:acct_test/proj_test/orchestrator"); + }); +}); + +describe("zid token verify — error paths", () => { + it("exits 2 for an expired token", async () => { + const { exitCode, stderr } = await runCLI(["token", "verify", "expired.jwt.token"]); + expect(exitCode).toBe(2); + expect(stderr.join("")).toMatch(/expired/i); + }); + + it("exits 1 for an invalid signature", async () => { + const { exitCode, stderr } = await runCLI(["token", "verify", "invalid.jwt.token"]); + expect(exitCode).toBe(1); + expect(stderr.join("")).toMatch(/invalid/i); + }); +}); diff --git a/cli/tests/helpers.ts b/cli/tests/helpers.ts new file mode 100644 index 0000000..da03bc2 --- /dev/null +++ b/cli/tests/helpers.ts @@ -0,0 +1,190 @@ +/** + * Shared test helpers. + * + * - `makeProgram()` — fresh Commander program with all commands registered + * - `runCLI()` — run a command and capture stdout/stderr without process.exit + * - `AUTH_ENV` — env vars that satisfy requireProfile() in tests + */ + +import { vi } from "vitest"; +import { Command } from "commander"; + +import { registerInit } from "../src/commands/init.js"; +import { registerDecode } from "../src/commands/token/decode.js"; +import { registerVerify } from "../src/commands/token/verify.js"; +import { registerIssue } from "../src/commands/token/issue.js"; +import { registerTokenRevoke } from "../src/commands/token/revoke.js"; +import { registerList } from "../src/commands/agents/list.js"; +import { registerGet } from "../src/commands/agents/get.js"; +import { registerRotateKey } from "../src/commands/agents/rotate-key.js"; +import { registerDeactivate } from "../src/commands/agents/deactivate.js"; +import { registerCredsList } from "../src/commands/creds/list.js"; +import { registerSignal } from "../src/commands/signal.js"; +import { registerConfig } from "../src/commands/config.js"; + +export const BASE_URL = "http://zeroid.test"; + +/** Env vars that satisfy requireProfile() without a config file. */ +export const AUTH_ENV = { + ZID_API_KEY: "zid_sk_test", + ZID_ACCOUNT_ID: "acct_test", + ZID_PROJECT_ID: "proj_test", + ZID_BASE_URL: BASE_URL, +}; + +/** Result of running a CLI command in test. */ +export interface RunResult { + stdout: string[]; + stderr: string[]; + exitCode: number | undefined; +} + +/** Build a fresh Commander program with every command registered. */ +export function makeProgram(): Command { + const program = new Command(); + program.name("zid").exitOverride(); // throws instead of process.exit on parse errors + + registerInit(program); + registerSignal(program); + registerConfig(program); + + const tokenCmd = program.command("token"); + registerIssue(tokenCmd); + registerDecode(tokenCmd); + registerVerify(tokenCmd); + registerTokenRevoke(tokenCmd); + + const agentsCmd = program.command("agents"); + registerList(agentsCmd); + registerGet(agentsCmd); + registerRotateKey(agentsCmd); + registerDeactivate(agentsCmd); + + const credsCmd = program.command("creds"); + registerCredsList(credsCmd); + + return program; +} + +/** + * Run a CLI command, capturing all output and the exit code. + * + * Sets AUTH_ENV automatically so requireProfile() is always satisfied. + * Does NOT call process.exit — instead records the exit code. + */ +export async function runCLI( + args: string[], + extraEnv: Record = {}, +): Promise { + const stdout: string[] = []; + const stderr: string[] = []; + let exitCode: number | undefined; + + const logSpy = vi.spyOn(console, "log").mockImplementation((...a) => { + stdout.push(a.map(String).join(" ")); + }); + const warnSpy = vi.spyOn(console, "warn").mockImplementation((...a) => { + stderr.push(a.map(String).join(" ")); + }); + const errorSpy = vi.spyOn(console, "error").mockImplementation((...a) => { + stderr.push(a.map(String).join(" ")); + }); + const exitSpy = vi.spyOn(process, "exit").mockImplementation((code?: number | string | null) => { + exitCode = typeof code === "number" ? code : 0; + throw new ExitError(exitCode); + }); + + // Save only the keys we are about to modify, then restore them individually + // to avoid replacing the process.env object reference (which Node does not support). + const keysToModify = Object.keys({ ...AUTH_ENV, ...extraEnv }); + const savedValues: Record = {}; + for (const key of keysToModify) { + savedValues[key] = process.env[key]; + } + Object.assign(process.env, AUTH_ENV, extraEnv); + + const program = makeProgram(); + try { + await program.parseAsync(["node", "zid", ...args]); + } catch (err) { + if (!(err instanceof ExitError)) { + // Commander parse errors (missing required args etc.) throw CommanderError. + stderr.push(err instanceof Error ? err.message : String(err)); + exitCode = 1; + } + } finally { + for (const [key, value] of Object.entries(savedValues)) { + if (value === undefined) { + delete process.env[key]; + } else { + process.env[key] = value; + } + } + logSpy.mockRestore(); + warnSpy.mockRestore(); + errorSpy.mockRestore(); + exitSpy.mockRestore(); + } + + return { stdout, stderr, exitCode }; +} + +/** Sentinel thrown by the mocked process.exit to stop execution. */ +class ExitError extends Error { + constructor(public readonly code: number) { + super(`process.exit(${code})`); + } +} + +// --------------------------------------------------------------------------- +// JWT helpers — craft minimal unsigned JWTs for decode tests +// --------------------------------------------------------------------------- + +function b64url(obj: unknown): string { + return Buffer.from(JSON.stringify(obj)) + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/, ""); +} + +export interface FakeJWTOptions { + sub?: string; + iss?: string; + exp?: number; // unix seconds + iat?: number; + account_id?: string; + project_id?: string; + identity_type?: string; + trust_level?: string; + grant_type?: string; + scopes?: string[]; + delegation_depth?: number; + act?: { sub: string; iss?: string }; + extra?: Record; + alg?: string; + kid?: string; +} + +/** Build a base64url-encoded JWT with a fake signature (for decode tests only). */ +export function makeJWT(opts: FakeJWTOptions = {}): string { + const now = Math.floor(Date.now() / 1000); + const header = { alg: opts.alg ?? "ES256", kid: opts.kid ?? "test-key-1" }; + const payload = { + sub: opts.sub ?? "wimse:agent:acct_test/proj_test/test-agent", + iss: opts.iss ?? BASE_URL, + iat: opts.iat ?? now - 60, + exp: opts.exp ?? now + 840, + jti: "jti_test_abc", + account_id: opts.account_id ?? "acct_test", + project_id: opts.project_id ?? "proj_test", + identity_type: opts.identity_type ?? "agent", + trust_level: opts.trust_level ?? "first_party", + grant_type: opts.grant_type ?? "api_key", + ...(opts.scopes ? { scopes: opts.scopes } : {}), + ...(opts.delegation_depth !== undefined ? { delegation_depth: opts.delegation_depth } : {}), + ...(opts.act ? { act: opts.act } : {}), + ...opts.extra, + }; + return `${b64url(header)}.${b64url(payload)}.fakesig`; +} diff --git a/cli/tests/lib/config.test.ts b/cli/tests/lib/config.test.ts new file mode 100644 index 0000000..8757407 --- /dev/null +++ b/cli/tests/lib/config.test.ts @@ -0,0 +1,152 @@ +/** + * Unit tests for lib/config.ts + */ + +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { mkdirSync, rmSync } from "node:fs"; +import { join } from "node:path"; + +// vi.hoisted ensures TEST_HOME is defined before vi.mock factories run. +const TEST_HOME = vi.hoisted( + () => `${process.env.TMPDIR ?? process.env.TEMP ?? "/tmp"}zid-config-test-${process.pid}`, +); + +// Must mock before importing config (it resolves HOME at module load time). +// Use importOriginal to keep tmpdir and other exports from node:os intact. +vi.mock("node:os", async (importOriginal) => { + const actual = await importOriginal(); + return { ...actual, homedir: () => TEST_HOME }; +}); + +const { getProfile, setProfile, useProfile, listProfiles, requireProfile, writeEnvFile } = + await import("../../src/lib/config.js"); + +beforeEach(() => { + mkdirSync(join(TEST_HOME, ".config", "zid"), { recursive: true }); + delete process.env["ZID_API_KEY"]; + delete process.env["ZID_ACCOUNT_ID"]; + delete process.env["ZID_PROJECT_ID"]; + delete process.env["ZID_BASE_URL"]; +}); + +afterEach(() => { + rmSync(TEST_HOME, { recursive: true, force: true }); +}); + +describe("getProfile / setProfile", () => { + it("returns undefined when no profile exists", () => { + expect(getProfile("default")).toBeUndefined(); + }); + + it("persists and retrieves a profile", () => { + const p = { base_url: "http://localhost", account_id: "a", project_id: "p", api_key: "k" }; + setProfile("default", p); + expect(getProfile("default")).toEqual(p); + }); + + it("auto-activates the first profile saved", () => { + setProfile("staging", { base_url: "http://staging", account_id: "a", project_id: "p", api_key: "k" }); + // requireProfile() with no args should resolve to the auto-activated profile + const active = requireProfile(); + expect(active.base_url).toBe("http://staging"); + }); + + it("does not change the active profile when a second profile is added", () => { + setProfile("dev", { base_url: "http://dev", account_id: "a", project_id: "p", api_key: "k1" }); + setProfile("prod", { base_url: "http://prod", account_id: "a", project_id: "p", api_key: "k2" }); + // dev was first, so it stays active + expect(requireProfile().base_url).toBe("http://dev"); + }); + + it("can store multiple named profiles", () => { + setProfile("dev", { base_url: "http://dev", account_id: "a", project_id: "p", api_key: "k1" }); + setProfile("prod", { base_url: "http://prod", account_id: "a", project_id: "p", api_key: "k2" }); + expect(getProfile("dev")?.api_key).toBe("k1"); + expect(getProfile("prod")?.api_key).toBe("k2"); + }); + + it("overwrites an existing profile", () => { + setProfile("default", { base_url: "http://old", account_id: "a", project_id: "p", api_key: "old" }); + setProfile("default", { base_url: "http://new", account_id: "a", project_id: "p", api_key: "new" }); + expect(getProfile("default")?.api_key).toBe("new"); + }); +}); + +describe("useProfile", () => { + it("switches the active profile", () => { + setProfile("dev", { base_url: "http://dev", account_id: "a", project_id: "p", api_key: "k" }); + setProfile("prod", { base_url: "http://prod", account_id: "a", project_id: "p", api_key: "k" }); + useProfile("prod"); + expect(requireProfile().base_url).toBe("http://prod"); + }); + + it("throws if profile does not exist", () => { + expect(() => useProfile("ghost")).toThrow(/not found/i); + }); +}); + +describe("listProfiles", () => { + it("returns empty when no profiles configured", () => { + expect(listProfiles()).toEqual([]); + }); + + it("marks the active profile", () => { + setProfile("dev", { base_url: "http://dev", account_id: "a", project_id: "p", api_key: "k" }); + setProfile("prod", { base_url: "http://prod", account_id: "a", project_id: "p", api_key: "k" }); + useProfile("prod"); + const list = listProfiles(); + expect(list.find((p) => p.name === "prod")?.active).toBe(true); + expect(list.find((p) => p.name === "dev")?.active).toBe(false); + }); +}); + +describe("requireProfile", () => { + it("reads from env vars when set", () => { + process.env["ZID_API_KEY"] = "env_key"; + process.env["ZID_ACCOUNT_ID"] = "env_acct"; + process.env["ZID_PROJECT_ID"] = "env_proj"; + const p = requireProfile(); + expect(p.api_key).toBe("env_key"); + expect(p.account_id).toBe("env_acct"); + expect(p.project_id).toBe("env_proj"); + expect(p.base_url).toBe("https://api.zeroid.io"); + }); + + it("works with only ZID_API_KEY — account/project are optional", () => { + process.env["ZID_API_KEY"] = "env_key"; + const p = requireProfile(); + expect(p.api_key).toBe("env_key"); + expect(p.account_id).toBeUndefined(); + expect(p.project_id).toBeUndefined(); + }); + + it("uses ZID_BASE_URL env var when set", () => { + process.env["ZID_API_KEY"] = "k"; + process.env["ZID_BASE_URL"] = "http://custom"; + expect(requireProfile().base_url).toBe("http://custom"); + }); + + it("env vars take precedence over config file", () => { + setProfile("default", { base_url: "http://file", account_id: "a", project_id: "p", api_key: "file_key" }); + process.env["ZID_API_KEY"] = "env_key"; + expect(requireProfile().api_key).toBe("env_key"); + }); + + it("throws a helpful error when nothing is configured", () => { + expect(() => requireProfile()).toThrow(/zid init/i); + }); +}); + +describe("writeEnvFile", () => { + it("writes ZID_API_KEY to .env.zeroid in cwd", async () => { + const { readFileSync } = await import("node:fs"); + const envPath = join(process.cwd(), ".env.zeroid"); + try { + writeEnvFile("zid_sk_test123"); + const contents = readFileSync(envPath, "utf8"); + expect(contents).toBe("ZID_API_KEY=zid_sk_test123\n"); + } finally { + rmSync(envPath, { force: true }); + } + }); +}); diff --git a/cli/tsconfig.json b/cli/tsconfig.json new file mode 100644 index 0000000..67c854d --- /dev/null +++ b/cli/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/cli/vitest.config.ts b/cli/vitest.config.ts new file mode 100644 index 0000000..0180954 --- /dev/null +++ b/cli/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + globals: false, + restoreMocks: true, + }, +}); diff --git a/docs/vs_entra_agentidentity.md b/docs/vs_entra_agentidentity.md new file mode 100644 index 0000000..e4886e6 --- /dev/null +++ b/docs/vs_entra_agentidentity.md @@ -0,0 +1,78 @@ + What They're Solving + + Entra Agent Identity: Gives AI agents a first-class identity within the Microsoft ecosystem — distinct from + service principals and user accounts. Primarily solves "what is this agent?" inside Azure/M365. + + ZeroID: Solves "who authorized this agent, with what scope, through what chain of agents?" — across any + ecosystem, any platform. + + They're complementary but aimed at different problems. + + --- + Feature-by-Feature + + ┌────────────────────────────────┬────────────────────────────────┬─────────────────────────────────┐ + │ │ Entra Agent Identity │ ZeroID │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ First-class agent identity │ ✅ │ ✅ │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Agent registry / inventory │ ✅ (Agent Registry) │ ✅ │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Owner/sponsor accountability │ ✅ (Owner + Sponsor roles) │ ✅ (created_by / owner_user_id) │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Agent-to-agent delegation │ ❌ │ ✅ RFC 8693 + act claim │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Scope attenuation per hop │ ❌ │ ✅ enforced at each exchange │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Delegation depth enforcement │ ❌ │ ✅ max_delegation_depth │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Cascade revocation down chains │ ❌ │ ✅ CAE + SSF signals │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Globally unique identity URI │ ✅ (tenant-scoped Object ID) │ ✅ WIMSE/SPIFFE (spiffe://...) │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Open standards │ ❌ (proprietary Graph API) │ ✅ OAuth 2.1, RFC 8693, WIMSE │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Cross-platform / any agent │ ❌ (Microsoft ecosystem only) │ ✅ │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Self-hostable / open source │ ❌ │ ✅ Apache 2.0 │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ SDK │ PowerShell/Graph API only │ Python, TypeScript, Rust │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ M365 / Azure resource access │ ✅ (licenses, groups, mailbox) │ ❌ (not in scope) │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Conditional Access / Defender │ ✅ deep integration │ ❌ │ + ├────────────────────────────────┼────────────────────────────────┼─────────────────────────────────┤ + │ Real-time token verification │ ✅ via Entra security products │ ✅ JWKS + introspection │ + └────────────────────────────────┴────────────────────────────────┴─────────────────────────────────┘ + + --- + Key Architectural Differences + + Entra's Blueprint model: Credentials live on the Blueprint (a template), not the Agent Identity itself. + Creating an agent involves a multi-stage token exchange: Blueprint authenticates → gets T1 → T1 impersonates + the Agent Identity. This is clever for centralized credential management but is opaque and proprietary. + + ZeroID's delegation model: Each agent has its own API key and identity. When it delegates, it uses RFC 8693 + token exchange — the resulting token carries the act chain explicitly and verifiably at every hop. Any + downstream system can read the full delegation chain from the token without calling back to a central + authority. + + --- + The Critical Gap in Entra + + Entra Agent Identity doesn't address multi-agent delegation at all. If a Copilot Studio orchestrator spawns a + sub-agent, there's no standard mechanism for the sub-agent's token to carry "I was authorized by this + orchestrator, which was authorized by this user, with this scope." That's exactly what ZeroID's RFC 8693 + implementation does. + + Entra solves identity registration and SSO within Microsoft. ZeroID solves identity provenance and delegation + across autonomous agent chains — which is the harder problem the OpenID Foundation whitepaper you cite actually + identifies as the unsolved one. + + --- + Practical Takeaway + + If you're building entirely within Azure/M365, Entra Agent Identity gives you Defender, Purview, Conditional + Access, and M365 app access out of the box — things ZeroID doesn't touch. If you're building multi-agent + systems across any stack (LangGraph, CrewAI, custom), need verifiable delegation chains, or want open standards + and portability, ZeroID covers ground Entra fundamentally doesn't.