From db7fccbaed6bf450964a9dabf8c4bcbd5fd5c0a0 Mon Sep 17 00:00:00 2001 From: Tasnim Kabir Sadik Date: Sat, 9 May 2026 14:51:48 +0600 Subject: [PATCH] feat: initial project structure and generate API types Initialized `package.json`, add tsup build config (CJS/ESM/DTS), wire up full Makefile targets (lint/test/check/build), add missing BadRequest response component to OpenAPI spec, generate TypeScript types from spec, configure biome to exclude generated files, and add `@types/ws` --- .gitignore | 3 + AGENTS.md | 67 + Makefile | 29 + README.md | 1 - api/openapi.yaml | 3174 +++++++++++++++++++++++++++++ biome.json | 35 + package.json | 44 + pnpm-lock.yaml | 2321 +++++++++++++++++++++ src/models/generated.ts | 4252 +++++++++++++++++++++++++++++++++++++++ tsconfig.json | 45 + tsup.config.ts | 11 + 11 files changed, 9981 insertions(+), 1 deletion(-) create mode 100644 AGENTS.md create mode 100644 Makefile create mode 100644 api/openapi.yaml create mode 100644 biome.json create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/models/generated.ts create mode 100644 tsconfig.json create mode 100644 tsup.config.ts diff --git a/.gitignore b/.gitignore index 2309cc8..7540ffd 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,6 @@ dist .yarn/install-state.gz .pnp.* +# AI agents +.opencode +PLAN.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8d29c26 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,67 @@ +# AGENTS.md + +## Project + +Wrenn JavaScript SDK — a client library for the Wrenn microVM platform. e2b drop-in replacement. +Package name: `@wrenn/sdk`. Node.js 18+, TypeScript 5.5+, managed with [pnpm](https://pnpm.io/). + +## Commands + +```bash +pnpm install # install deps +make lint # biome check + format check (no auto-fix) +make test # unit tests only (vitest) +make test-integration # all tests including integration (needs live server) +make generate # regenerate types from OpenAPI spec (openapi-typescript) +make check # lint + unit test +make build # tsup build (CJS + ESM + DTS) +``` + +- `make test` runs all non-integration tests. To run a specific test file: `pnpm vitest run tests/commands.test.ts` +- No separate typecheck step — `vitest` and `tsup` handle type checking during test/build. `tsc --noEmit` is available but not wired up in CI. + +## Architecture + +- `src/` — the library package + - `capsule.ts` — high-level `Capsule` class (main user-facing class) + - `client.ts` — low-level `WrennClient` with `CapsulesResource` and `SnapshotsResource` + - `commands.ts` — command execution, streaming, and background process management + - `files.ts` — filesystem operations + - `pty.ts` — interactive terminal (PTY) over WebSocket + - `exceptions.ts` — typed error hierarchy (`WrennError` base) + - `models/generated.ts` — **auto-generated** from OpenAPI spec via `openapi-typescript` (never edit directly; run `make generate`) + - `git/` — git operations inside capsules (clone, push, pull, status, branches, etc.) + - `code-interpreter/` — specialized capsule for stateful Jupyter kernel execution + - `_shared/http.ts` — thin `fetch` wrapper with auth headers, base URL, and error mapping + - `_shared/websocket.ts` — WebSocket helper wrapping `ws` + - `config.ts` — constants (`DEFAULT_BASE_URL`, env var names) +- `tests/` — unit tests use `msw` to mock HTTP; integration tests are in `tests/integration/` +- `api/openapi.yaml` — OpenAPI spec used for type generation + +## Key Conventions + +- Generated types live in `src/models/generated.ts`. Never edit them. Run `make generate` to update. +- No sync/async split — JS is naturally async. One `Capsule` class, all methods return `Promise`. +- `Sandbox` is a deprecated alias for `Capsule`. New code should use `Capsule`. +- Uses native `fetch` for HTTP (Node 18+), `ws` for WebSockets, `zod` for runtime validation. +- Resource disposal via `Symbol.asyncDispose` (`await using`). Also supports manual `.close()`. +- Streaming methods return `AsyncGenerator` (e.g. `commands.stream()`, `files.downloadStream()`). +- Static + instance method pattern: `capsule.destroy()` (instance) and `Capsule.destroy(id, opts)` (static). + +## Testing + +- Unit tests mock HTTP via `msw` (Mock Service Worker for Node). +- Integration tests require env vars: `WRENN_API_KEY` (or `WRENN_TOKEN`), optionally `WRENN_BASE_URL`. +- Integration test fixtures in `tests/integration/setup.ts` create real capsules and clean them up. +- Tests use `vitest` — no `@jest` globals. Use `import { describe, it, expect } from 'vitest'`. + +## CI + +Woodpecker CI (`.woodpecker/check.yml`) runs on push to `main` and `dev`: +1. `make lint` +2. `make test` +3. `make test-integration` + +## Dependencies + +Runtime: `ws` (WebSocket), `zod` (validation). Everything else is dev-only. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5202d47 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +# Makefile +.PHONY: generate lint test test-integration check build + +SPEC_URL = "https://raw.githubusercontent.com/wrennhq/wrenn/refs/heads/main/internal/api/openapi.yaml" +SPEC_PATH = "api/openapi.yaml" + +generate: + @echo "Fetching latest OpenAPI spec from Git repo..." + mkdir -p api + curl -fsSL $(SPEC_URL) -o $(SPEC_PATH) + @echo "Generating TypeScript types..." + mkdir -p src/models + pnpm generate + +lint: + pnpm exec biome check . + +test: + pnpm vitest run --exclude tests/integration + +test-integration: + pnpm test:integration + +check: + $(MAKE) lint + $(MAKE) test + +build: + pnpm build diff --git a/README.md b/README.md index f80e5b9..3370a5e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ # js-sdk - diff --git a/api/openapi.yaml b/api/openapi.yaml new file mode 100644 index 0000000..2f341ee --- /dev/null +++ b/api/openapi.yaml @@ -0,0 +1,3174 @@ +openapi: "3.1.0" +info: + title: Wrenn API + description: MicroVM-based code execution platform API. + version: "0.1.4" + +servers: + - url: http://localhost:8080 + description: Local development + +security: [] + +paths: + /v1/auth/signup: + post: + summary: Create a new account + operationId: signup + tags: [auth] + description: | + Creates an inactive user account and sends an activation email. + The user must activate their account within 30 minutes. + Does not return a JWT — the user must activate first, then sign in. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SignupRequest" + responses: + "201": + description: Account created, activation email sent + content: + application/json: + schema: + $ref: "#/components/schemas/SignupResponse" + "400": + description: Invalid request (bad email, short password) + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Email already registered or signup cooldown active + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/auth/activate: + post: + summary: Activate account via email token + operationId: activate + tags: [auth] + description: | + Consumes the activation token sent via email and activates the user account. + Creates a default team and returns a JWT to log the user in. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [token] + properties: + token: + type: string + responses: + "200": + description: Account activated, JWT issued + content: + application/json: + schema: + $ref: "#/components/schemas/AuthResponse" + "400": + description: Invalid or expired token + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/auth/switch-team: + post: + summary: Switch active team + operationId: switchTeam + tags: [auth] + security: + - bearerAuth: [] + description: | + Re-issues a JWT scoped to a different team. The user must be a member of + the target team (verified from DB). Use the returned token for subsequent + requests to that team's resources. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [team_id] + properties: + team_id: + type: string + responses: + "200": + description: New JWT issued for the target team + content: + application/json: + schema: + $ref: "#/components/schemas/AuthResponse" + "403": + description: Not a member of this team + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Team not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/auth/login: + post: + summary: Log in with email and password + operationId: login + tags: [auth] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/LoginRequest" + responses: + "200": + description: Login successful + content: + application/json: + schema: + $ref: "#/components/schemas/AuthResponse" + "401": + description: Invalid credentials + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/auth/oauth/{provider}: + parameters: + - name: provider + in: path + required: true + schema: + type: string + enum: [github] + description: OAuth provider name + + get: + summary: Start OAuth login flow + operationId: oauthRedirect + tags: [auth] + description: | + Redirects the user to the OAuth provider's authorization page. + Sets a short-lived CSRF state cookie for validation on callback. + responses: + "302": + description: Redirect to provider authorization URL + "404": + description: Provider not found or not configured + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/auth/oauth/{provider}/callback: + parameters: + - name: provider + in: path + required: true + schema: + type: string + enum: [github] + description: OAuth provider name + + get: + summary: OAuth callback + operationId: oauthCallback + tags: [auth] + description: | + Handles the OAuth provider's callback after user authorization. + Exchanges the authorization code for a user profile, creates or + logs in the user, and redirects to the frontend with a JWT token. + + **On success:** redirects to `{OAUTH_REDIRECT_URL}/auth/{provider}/callback?token=...&user_id=...&team_id=...&email=...` + + **On error:** redirects to `{OAUTH_REDIRECT_URL}/auth/{provider}/callback?error=...` + + Possible error codes: `access_denied`, `invalid_state`, `missing_code`, + `exchange_failed`, `email_taken`, `internal_error`. + parameters: + - name: code + in: query + schema: + type: string + description: Authorization code from the OAuth provider + - name: state + in: query + schema: + type: string + description: CSRF state parameter (must match the cookie) + responses: + "302": + description: Redirect to frontend with token or error + + /v1/me: + get: + summary: Get current user profile + operationId: getMe + tags: [account] + security: + - bearerAuth: [] + responses: + "200": + description: User profile + content: + application/json: + schema: + $ref: "#/components/schemas/MeResponse" + + patch: + summary: Update display name + operationId: updateName + tags: [account] + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [name] + properties: + name: + type: string + minLength: 1 + maxLength: 100 + responses: + "200": + description: Name updated, new JWT issued + content: + application/json: + schema: + $ref: "#/components/schemas/AuthResponse" + "400": + description: Invalid name + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + delete: + summary: Delete current account + operationId: deleteAccount + tags: [account] + security: + - bearerAuth: [] + description: | + Soft-deletes the account (sets status=deleted, deleted_at=now). + The account is permanently removed after 15 days. Blocked if the user + owns any team that has other members. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [confirmation] + properties: + confirmation: + type: string + description: Must match the user's email address (case-insensitive) + responses: + "204": + description: Account scheduled for deletion + "400": + description: Confirmation does not match email + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: User owns teams with other members + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/me/password: + post: + summary: Change or add password + operationId: changePassword + tags: [account] + security: + - bearerAuth: [] + description: | + For users with an existing password: requires `current_password` and `new_password`. + For OAuth-only users adding a password: requires `new_password` and `confirm_password`. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ChangePasswordRequest" + responses: + "204": + description: Password updated + "400": + description: Invalid request (short password, mismatch, etc.) + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Current password is incorrect + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/me/password/reset: + post: + summary: Request a password reset email + operationId: requestPasswordReset + tags: [account] + description: | + Sends a password reset link to the given email. Always returns 200 + regardless of whether the email exists, to prevent account enumeration. + The reset token expires in 15 minutes. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [email] + properties: + email: + type: string + format: email + responses: + "204": + description: Request accepted (email sent if account exists) + + /v1/me/password/reset/confirm: + post: + summary: Confirm password reset + operationId: confirmPasswordReset + tags: [account] + description: | + Consumes a password reset token and sets a new password. The token is + single-use and expires after 15 minutes. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [token, new_password] + properties: + token: + type: string + description: Raw reset token from the email link + new_password: + type: string + minLength: 8 + responses: + "204": + description: Password reset successful + "400": + description: Invalid or expired token, or password too short + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/me/providers/{provider}/connect: + parameters: + - name: provider + in: path + required: true + schema: + type: string + enum: [github] + description: OAuth provider name + + get: + summary: Initiate OAuth provider link + operationId: connectProvider + tags: [account] + security: + - bearerAuth: [] + description: | + Sets OAuth state and link cookies, then returns the provider's + authorization URL. The frontend navigates to this URL to start the + OAuth flow. On callback, the provider is linked to the current account + (not a new registration). + responses: + "200": + description: Authorization URL + content: + application/json: + schema: + type: object + properties: + auth_url: + type: string + format: uri + "404": + description: Provider not found or not configured + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/me/providers/{provider}: + parameters: + - name: provider + in: path + required: true + schema: + type: string + enum: [github] + description: OAuth provider name + + delete: + summary: Disconnect an OAuth provider + operationId: disconnectProvider + tags: [account] + security: + - bearerAuth: [] + description: | + Unlinks the OAuth provider from the current account. Blocked if this + is the user's only login method (no password and no other providers). + responses: + "204": + description: Provider disconnected + "400": + description: Cannot disconnect last login method + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Provider not connected + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/api-keys: + post: + summary: Create an API key + operationId: createAPIKey + tags: [api-keys] + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateAPIKeyRequest" + responses: + "201": + description: API key created (plaintext key only shown once) + content: + application/json: + schema: + $ref: "#/components/schemas/APIKeyResponse" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + get: + summary: List API keys for your team + operationId: listAPIKeys + tags: [api-keys] + security: + - bearerAuth: [] + responses: + "200": + description: List of API keys (plaintext keys are never returned) + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/APIKeyResponse" + + /v1/api-keys/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: string + + delete: + summary: Delete an API key + operationId: deleteAPIKey + tags: [api-keys] + security: + - bearerAuth: [] + responses: + "204": + description: API key deleted + + /v1/users/search: + get: + summary: Search users by email prefix + operationId: searchUsers + tags: [users] + security: + - bearerAuth: [] + description: | + Returns up to 10 users whose email starts with the given prefix. + The prefix must contain "@". Intended for the add-member UI autocomplete. + parameters: + - name: email + in: query + required: true + schema: + type: string + description: Email prefix (must contain "@", e.g. "alice@") + responses: + "200": + description: Matching users + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/UserSearchResult" + "400": + description: Prefix does not contain "@" + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/teams: + get: + summary: List teams for the authenticated user + operationId: listTeams + tags: [teams] + security: + - bearerAuth: [] + responses: + "200": + description: Teams the user belongs to, each with their role + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TeamWithRole" + + post: + summary: Create a new team + operationId: createTeam + tags: [teams] + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [name] + properties: + name: + type: string + description: 1-128 chars; A-Z a-z 0-9 space _ + responses: + "201": + description: Team created (caller is owner) + content: + application/json: + schema: + $ref: "#/components/schemas/TeamWithRole" + "400": + description: Invalid team name + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/teams/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: string + description: Team ID (must match the JWT's team_id) + + get: + summary: Get team info and member list + operationId: getTeam + tags: [teams] + security: + - bearerAuth: [] + responses: + "200": + description: Team details with members + content: + application/json: + schema: + $ref: "#/components/schemas/TeamDetail" + "403": + description: JWT team does not match requested team + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Team not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + patch: + summary: Rename the team + operationId: renameTeam + tags: [teams] + security: + - bearerAuth: [] + description: Admin or owner role required (verified from DB). + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [name] + properties: + name: + type: string + responses: + "204": + description: Renamed + "400": + description: Invalid team name + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Insufficient role + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + delete: + summary: Delete the team + operationId: deleteTeam + tags: [teams] + security: + - bearerAuth: [] + description: | + Owner only. Soft-deletes the team and destroys all running/paused/starting + capsulees. All DB records are preserved. The team slug is permanently reserved. + responses: + "204": + description: Team deleted + "403": + description: Caller is not the owner + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/teams/{id}/members: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: List team members + operationId: listTeamMembers + tags: [teams] + security: + - bearerAuth: [] + responses: + "200": + description: Members with roles + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TeamMember" + + post: + summary: Add a member by email + operationId: addTeamMember + tags: [teams] + security: + - bearerAuth: [] + description: Admin or owner role required. User is added instantly as a member. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [email] + properties: + email: + type: string + format: email + responses: + "201": + description: Member added + content: + application/json: + schema: + $ref: "#/components/schemas/TeamMember" + "403": + description: Insufficient role + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: No account with that email + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "400": + description: User is already a member + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/teams/{id}/members/{uid}: + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: uid + in: path + required: true + schema: + type: string + description: Target user ID + + patch: + summary: Update member role + operationId: updateMemberRole + tags: [teams] + security: + - bearerAuth: [] + description: | + Admin or owner required. Valid target roles: admin, member. + The owner's role cannot be changed. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [role] + properties: + role: + type: string + enum: [admin, member] + responses: + "204": + description: Role updated + "403": + description: Insufficient role or attempt to modify owner + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: User is not a member + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + delete: + summary: Remove a member + operationId: removeTeamMember + tags: [teams] + security: + - bearerAuth: [] + description: Admin or owner required. Owner cannot be removed. + responses: + "204": + description: Member removed + "403": + description: Insufficient role or attempt to remove owner + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: User is not a member + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/teams/{id}/leave: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Leave the team + operationId: leaveTeam + tags: [teams] + security: + - bearerAuth: [] + description: The owner cannot leave; they must delete the team instead. + responses: + "204": + description: Left the team + "403": + description: Owner cannot leave + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules: + post: + summary: Create a capsule + operationId: createCapsule + tags: [capsules] + security: + - apiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateCapsuleRequest" + responses: + "201": + description: Capsule created + content: + application/json: + schema: + $ref: "#/components/schemas/Capsule" + "502": + description: Host agent error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + get: + summary: List capsulees for your team + operationId: listCapsules + tags: [capsules] + security: + - apiKeyAuth: [] + responses: + "200": + description: List of capsulees + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Capsule" + + /v1/capsules/stats: + get: + summary: Get capsule usage stats for your team + operationId: getCapsuleStats + tags: [capsules] + security: + - apiKeyAuth: [] + parameters: + - name: range + in: query + required: false + schema: + type: string + enum: [5m, 1h, 6h, 24h, 30d] + default: 1h + description: Time window for the time-series data. + responses: + "200": + description: Capsule stats for the team + content: + application/json: + schema: + $ref: "#/components/schemas/CapsuleStats" + "400": + $ref: "#/components/responses/BadRequest" + + /v1/capsules/usage: + get: + summary: Get daily CPU and RAM usage for your team + operationId: getCapsuleUsage + tags: [capsules] + security: + - apiKeyAuth: [] + parameters: + - name: from + in: query + required: false + schema: + type: string + format: date + description: Start date (YYYY-MM-DD). Defaults to 30 days ago. + - name: to + in: query + required: false + schema: + type: string + format: date + description: End date (YYYY-MM-DD). Defaults to today. + responses: + "200": + description: Daily usage data for the team + content: + application/json: + schema: + $ref: "#/components/schemas/UsageResponse" + "400": + $ref: "#/components/responses/BadRequest" + + /v1/capsules/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: Get capsule details + operationId: getCapsule + tags: [capsules] + security: + - apiKeyAuth: [] + responses: + "200": + description: Capsule details + content: + application/json: + schema: + $ref: "#/components/schemas/Capsule" + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + delete: + summary: Destroy a capsule + operationId: destroyCapsule + tags: [capsules] + security: + - apiKeyAuth: [] + responses: + "204": + description: Capsule destroyed + + /v1/capsules/{id}/exec: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Execute a command + operationId: execCommand + tags: [capsules] + security: + - apiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ExecRequest" + responses: + "200": + description: Command output (foreground exec) + content: + application/json: + schema: + $ref: "#/components/schemas/ExecResponse" + "202": + description: Background process started + content: + application/json: + schema: + $ref: "#/components/schemas/BackgroundExecResponse" + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/processes: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: List running processes + operationId: listProcesses + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Returns all running processes inside the capsule, including background + processes and any processes started by templates or init scripts. + responses: + "200": + description: Process list + content: + application/json: + schema: + $ref: "#/components/schemas/ProcessListResponse" + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/processes/{selector}: + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: selector + in: path + required: true + description: Process PID (numeric) or tag (string) + schema: + type: string + + delete: + summary: Kill a process + operationId: killProcess + tags: [capsules] + security: + - apiKeyAuth: [] + parameters: + - name: signal + in: query + required: false + description: Signal to send (SIGKILL or SIGTERM, default SIGKILL) + schema: + type: string + enum: [SIGKILL, SIGTERM] + default: SIGKILL + responses: + "204": + description: Process killed + "404": + description: Capsule or process not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/processes/{selector}/stream: + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: selector + in: path + required: true + description: Process PID (numeric) or tag (string) + schema: + type: string + + get: + summary: Stream process output via WebSocket + operationId: connectProcess + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Opens a WebSocket connection to stream stdout/stderr from a running + background process. The selector can be a numeric PID or a string tag. + + Server sends JSON messages: + - `{"type": "start", "pid": 42}` — connected to process + - `{"type": "stdout", "data": "..."}` — stdout output + - `{"type": "stderr", "data": "..."}` — stderr output + - `{"type": "exit", "exit_code": 0}` — process exited + - `{"type": "error", "data": "..."}` — error message + responses: + "101": + description: WebSocket upgrade + + /v1/capsules/{id}/ping: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Reset capsule inactivity timer + operationId: pingCapsule + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Resets the last_active_at timestamp for a running capsule, preventing + the auto-pause TTL from expiring. Use this as a keepalive for capsulees + that are idle but should remain running. + responses: + "204": + description: Ping acknowledged, inactivity timer reset + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/metrics: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: Get per-capsule resource metrics + operationId: getCapsuleMetrics + tags: [capsules] + security: + - apiKeyAuth: [] + - bearerAuth: [] + description: | + Returns time-series CPU, memory, and disk metrics for a capsule. + Three tiers are available with different granularity and retention: + - `10m`: 500ms samples, last 10 minutes + - `2h`: 30-second averages, last 2 hours + - `24h`: 5-minute averages, last 24 hours + + For running capsulees, data comes from the host agent's in-memory + ring buffer. For paused capsulees, data is read from persisted + snapshots in the database. Stopped/destroyed capsulees return 404. + parameters: + - name: range + in: query + required: false + schema: + type: string + enum: ["5m", "10m", "1h", "2h", "6h", "12h", "24h"] + default: "10m" + description: Time range filter to query + responses: + "200": + description: Metrics retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/CapsuleMetrics" + "400": + description: Invalid range parameter + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Capsule not found or metrics not available + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/pause: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Pause a running capsule + operationId: pauseCapsule + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Takes a snapshot of the capsule (VM state + memory + rootfs), then + destroys all running resources. The capsule exists only as files on + disk and can be resumed later. + responses: + "200": + description: Capsule paused (snapshot taken, resources released) + content: + application/json: + schema: + $ref: "#/components/schemas/Capsule" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/resume: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Resume a paused capsule + operationId: resumeCapsule + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Restores a paused capsule from its snapshot using UFFD for lazy + memory loading. Boots a fresh Firecracker process, sets up a new + network slot, and waits for envd to become ready. + responses: + "200": + description: Capsule resumed (new VM booted from snapshot) + content: + application/json: + schema: + $ref: "#/components/schemas/Capsule" + "409": + description: Capsule not paused + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/snapshots: + post: + summary: Create a snapshot template + operationId: createSnapshot + tags: [snapshots] + security: + - apiKeyAuth: [] + description: | + Pauses a running capsule, takes a full snapshot, copies the snapshot + files to the images directory as a reusable template, then destroys + the capsule. The template can be used to create new capsulees. + parameters: + - name: overwrite + in: query + required: false + schema: + type: string + enum: ["true"] + description: Set to "true" to overwrite an existing snapshot with the same name. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateSnapshotRequest" + responses: + "201": + description: Snapshot created + content: + application/json: + schema: + $ref: "#/components/schemas/Template" + "409": + description: Name already exists or capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + get: + summary: List templates for your team + operationId: listSnapshots + tags: [snapshots] + security: + - apiKeyAuth: [] + parameters: + - name: type + in: query + required: false + schema: + type: string + enum: [base, snapshot] + description: Filter by template type. + responses: + "200": + description: List of templates + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Template" + + /v1/snapshots/{name}: + parameters: + - name: name + in: path + required: true + schema: + type: string + + delete: + summary: Delete a snapshot template + operationId: deleteSnapshot + tags: [snapshots] + security: + - apiKeyAuth: [] + description: Removes the snapshot files from disk and deletes the database record. + responses: + "204": + description: Snapshot deleted + "404": + description: Template not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/files/write: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Upload a file + operationId: uploadFile + tags: [capsules] + security: + - apiKeyAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [path, file] + properties: + path: + type: string + description: Absolute destination path inside the capsule + file: + type: string + format: binary + description: File content + responses: + "204": + description: File uploaded + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "413": + description: File too large + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/files/read: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Download a file + operationId: downloadFile + tags: [capsules] + security: + - apiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ReadFileRequest" + responses: + "200": + description: File content + content: + application/octet-stream: + schema: + type: string + format: binary + "404": + description: Capsule or file not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/files/list: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: List directory contents + operationId: listDir + tags: [capsules] + security: + - apiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ListDirRequest" + responses: + "200": + description: Directory listing + content: + application/json: + schema: + $ref: "#/components/schemas/ListDirResponse" + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/files/mkdir: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Create a directory + operationId: makeDir + tags: [capsules] + security: + - apiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MakeDirRequest" + responses: + "200": + description: Directory created + content: + application/json: + schema: + $ref: "#/components/schemas/MakeDirResponse" + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/files/remove: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Remove a file or directory + operationId: removePath + tags: [capsules] + security: + - apiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RemoveRequest" + responses: + "204": + description: File or directory removed + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/exec/stream: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: Stream command execution via WebSocket + operationId: execStream + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Opens a WebSocket connection for streaming command execution. + + **Client sends** (first message to start the process): + ```json + {"type": "start", "cmd": "tail", "args": ["-f", "/var/log/syslog"]} + ``` + + **Client sends** (to stop the process): + ```json + {"type": "stop"} + ``` + + **Server sends** (process events as they arrive): + ```json + {"type": "start", "pid": 1234} + {"type": "stdout", "data": "line of output\n"} + {"type": "stderr", "data": "warning message\n"} + {"type": "exit", "exit_code": 0} + {"type": "error", "data": "description of error"} + ``` + + The connection closes automatically after the process exits. + responses: + "101": + description: WebSocket upgrade + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/pty: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: Interactive PTY session via WebSocket + operationId: ptySession + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Opens a WebSocket connection for an interactive PTY (terminal) session. + Supports creating new sessions, sending input, resizing, killing, and + reconnecting to existing sessions. + + **Client sends** (first message — start a new PTY): + ```json + { + "type": "start", + "cmd": "/bin/bash", + "args": [], + "cols": 80, + "rows": 24, + "envs": {"TERM": "xterm-256color"}, + "cwd": "/home/user", + "user": "user" + } + ``` + All fields except `type` are optional. Defaults: cmd="/bin/bash", cols=80, rows=24. + + **Client sends** (first message — reconnect to existing PTY): + ```json + {"type": "connect", "tag": "pty-abc123de"} + ``` + + **Client sends** (after session is established): + ```json + {"type": "input", "data": ""} + {"type": "resize", "cols": 120, "rows": 40} + {"type": "kill"} + ``` + + **Server sends**: + ```json + {"type": "started", "tag": "pty-abc123de", "pid": 42} + {"type": "output", "data": ""} + {"type": "exit", "exit_code": 0} + {"type": "error", "data": "description", "fatal": true} + {"type": "ping"} + ``` + + PTY data (input and output) is base64-encoded because it contains raw + terminal bytes (escape sequences, control codes) that are not valid UTF-8. + + Sessions persist across WebSocket disconnections — the process keeps + running in the capsule. Use the `tag` from the "started" response to + reconnect later. + responses: + "101": + description: WebSocket upgrade + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/files/stream/write: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Upload a file (streaming) + operationId: streamUploadFile + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Streams file content to the capsule without buffering in memory. + Suitable for large files. Uses the same multipart/form-data format + as the non-streaming upload endpoint. + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [path, file] + properties: + path: + type: string + description: Absolute destination path inside the capsule + file: + type: string + format: binary + description: File content + responses: + "204": + description: File uploaded + "404": + description: Capsule not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/capsules/{id}/files/stream/read: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Download a file (streaming) + operationId: streamDownloadFile + tags: [capsules] + security: + - apiKeyAuth: [] + description: | + Streams file content from the capsule without buffering in memory. + Suitable for large files. Returns raw bytes with chunked transfer encoding. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ReadFileRequest" + responses: + "200": + description: File content streamed in chunks + content: + application/octet-stream: + schema: + type: string + format: binary + "404": + description: Capsule or file not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Capsule not running + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/hosts: + post: + summary: Create a host + operationId: createHost + tags: [hosts] + security: + - bearerAuth: [] + description: | + Creates a new host record and returns a one-time registration token. + Regular hosts can only be created by admins. BYOC hosts can be created + by admins or team owners. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateHostRequest" + responses: + "201": + description: Host created with registration token + content: + application/json: + schema: + $ref: "#/components/schemas/CreateHostResponse" + "400": + description: Invalid request + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Insufficient permissions + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + get: + summary: List hosts + operationId: listHosts + tags: [hosts] + security: + - bearerAuth: [] + description: | + Admins see all hosts. Non-admins see only BYOC hosts belonging to their team. + responses: + "200": + description: List of hosts + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Host" + + /v1/hosts/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: Get host details + operationId: getHost + tags: [hosts] + security: + - bearerAuth: [] + responses: + "200": + description: Host details + content: + application/json: + schema: + $ref: "#/components/schemas/Host" + "404": + description: Host not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + delete: + summary: Delete a host + operationId: deleteHost + tags: [hosts] + security: + - bearerAuth: [] + description: | + Admins can delete any host. Team owners and admins can delete BYOC hosts + belonging to their team. Without `?force=true`, returns 409 if the host + has active capsulees. With `?force=true`, destroys all capsulees first. + parameters: + - name: force + in: query + required: false + schema: + type: boolean + description: If true, destroy all capsulees on the host before deleting. + responses: + "204": + description: Host deleted + "403": + description: Insufficient permissions + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Host has active capsulees (only when force is not set) + content: + application/json: + schema: + $ref: "#/components/schemas/HostHasCapsulesError" + + /v1/hosts/{id}/token: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Regenerate registration token + operationId: regenerateHostToken + tags: [hosts] + security: + - bearerAuth: [] + description: | + Issues a new registration token for a host still in "pending" status. + Use this when a previous registration attempt failed after consuming + the original token. Same permission model as host creation. + responses: + "201": + description: New registration token issued + content: + application/json: + schema: + $ref: "#/components/schemas/CreateHostResponse" + "403": + description: Insufficient permissions + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Host is not in pending status + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/hosts/register: + post: + summary: Register a host agent + operationId: registerHost + tags: [hosts] + description: | + Called by the host agent on first startup. Validates the one-time + registration token, records machine specs, sets the host status to + "online", and returns a long-lived JWT for subsequent API calls + (heartbeats). + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RegisterHostRequest" + responses: + "201": + description: Host registered, JWT returned + content: + application/json: + schema: + $ref: "#/components/schemas/RegisterHostResponse" + "400": + description: Invalid request + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Invalid or expired registration token + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/hosts/{id}/heartbeat: + parameters: + - name: id + in: path + required: true + schema: + type: string + + post: + summary: Host agent heartbeat + operationId: hostHeartbeat + tags: [hosts] + security: + - hostTokenAuth: [] + description: | + Updates the host's last_heartbeat_at timestamp. The host ID in the URL + must match the host ID in the JWT. + responses: + "204": + description: Heartbeat recorded + "401": + description: Invalid or missing host token + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Host ID mismatch + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/hosts/auth/refresh: + post: + summary: Refresh host JWT + operationId: refreshHostToken + tags: [hosts] + description: | + Exchanges a refresh token for a new JWT and rotated refresh token. + The old refresh token is immediately revoked. No authentication required — + the refresh token itself is the credential. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RefreshHostTokenRequest" + responses: + "200": + description: New JWT and rotated refresh token + content: + application/json: + schema: + $ref: "#/components/schemas/RefreshHostTokenResponse" + "401": + description: Invalid, expired, or revoked refresh token + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/hosts/{id}/delete-preview: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: Preview host deletion + operationId: getHostDeletePreview + tags: [hosts] + security: + - bearerAuth: [] + description: | + Returns the list of capsule IDs that would be destroyed if the host + were deleted with `?force=true`. No state is modified. + responses: + "200": + description: Deletion preview + content: + application/json: + schema: + $ref: "#/components/schemas/HostDeletePreview" + "403": + description: Insufficient permissions + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Host not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/hosts/{id}/tags: + parameters: + - name: id + in: path + required: true + schema: + type: string + + get: + summary: List host tags + operationId: listHostTags + tags: [hosts] + security: + - bearerAuth: [] + responses: + "200": + description: List of tags + content: + application/json: + schema: + type: array + items: + type: string + + post: + summary: Add a tag to a host + operationId: addHostTag + tags: [hosts] + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AddTagRequest" + responses: + "204": + description: Tag added + "404": + description: Host not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/hosts/{id}/tags/{tag}: + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: tag + in: path + required: true + schema: + type: string + + delete: + summary: Remove a tag from a host + operationId: removeHostTag + tags: [hosts] + security: + - bearerAuth: [] + responses: + "204": + description: Tag removed + "404": + description: Host not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/channels: + post: + summary: Create a notification channel + operationId: createChannel + tags: [channels] + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateChannelRequest" + responses: + "201": + description: Channel created + content: + application/json: + schema: + $ref: "#/components/schemas/ChannelResponse" + "400": + $ref: "#/components/responses/BadRequest" + get: + summary: List notification channels + operationId: listChannels + tags: [channels] + security: + - bearerAuth: [] + responses: + "200": + description: Channels list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ChannelResponse" + + /v1/channels/test: + post: + summary: Test a channel configuration + description: > + Sends a test notification using the provided provider and config without + saving anything. Use this to verify credentials before creating a channel. + operationId: testChannel + tags: [channels] + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TestChannelRequest" + responses: + "200": + description: Test notification sent successfully + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: ok + "400": + $ref: "#/components/responses/BadRequest" + + /v1/channels/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: string + get: + summary: Get a notification channel + operationId: getChannel + tags: [channels] + security: + - bearerAuth: [] + responses: + "200": + description: Channel details + content: + application/json: + schema: + $ref: "#/components/schemas/ChannelResponse" + "404": + description: Channel not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + patch: + summary: Update a notification channel + operationId: updateChannel + tags: [channels] + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateChannelRequest" + responses: + "200": + description: Channel updated + content: + application/json: + schema: + $ref: "#/components/schemas/ChannelResponse" + "400": + $ref: "#/components/responses/BadRequest" + "404": + description: Channel not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + delete: + summary: Delete a notification channel + operationId: deleteChannel + tags: [channels] + security: + - bearerAuth: [] + responses: + "204": + description: Channel deleted + + /v1/channels/{id}/config: + parameters: + - name: id + in: path + required: true + schema: + type: string + put: + summary: Rotate channel secrets + description: > + Replaces the channel's provider configuration entirely with new secrets. + The previous config is discarded. Config fields must match the provider's + required fields. + operationId: rotateChannelConfig + tags: [channels] + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RotateConfigRequest" + responses: + "200": + description: Config rotated + content: + application/json: + schema: + $ref: "#/components/schemas/ChannelResponse" + "400": + $ref: "#/components/responses/BadRequest" + "404": + description: Channel not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + +components: + securitySchemes: + apiKeyAuth: + type: apiKey + in: header + name: X-API-Key + description: API key for capsule lifecycle operations. Create via POST /v1/api-keys. + + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token from /v1/auth/login or /v1/auth/signup. Valid for 6 hours. + + hostTokenAuth: + type: apiKey + in: header + name: X-Host-Token + description: Host JWT returned from POST /v1/hosts/register or POST /v1/hosts/auth/refresh. Valid for 7 days. + + responses: + BadRequest: + description: Invalid request + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + schemas: + SignupRequest: + type: object + required: [email, password, name] + properties: + email: + type: string + format: email + password: + type: string + minLength: 8 + name: + type: string + maxLength: 100 + + LoginRequest: + type: object + required: [email, password] + properties: + email: + type: string + format: email + password: + type: string + + SignupResponse: + type: object + properties: + message: + type: string + description: Confirmation message instructing user to check email + + AuthResponse: + type: object + properties: + token: + type: string + description: JWT token (valid for 6 hours) + user_id: + type: string + team_id: + type: string + email: + type: string + name: + type: string + + CreateAPIKeyRequest: + type: object + properties: + name: + type: string + default: Unnamed API Key + + APIKeyResponse: + type: object + properties: + id: + type: string + team_id: + type: string + name: + type: string + key_prefix: + type: string + description: Display prefix (e.g. "wrn_ab12cd34...") + created_at: + type: string + format: date-time + last_used: + type: string + format: date-time + nullable: true + key: + type: string + description: Full plaintext key. Only returned on creation, never again. + nullable: true + + CreateCapsuleRequest: + type: object + properties: + template: + type: string + default: minimal + vcpus: + type: integer + default: 1 + memory_mb: + type: integer + default: 512 + timeout_sec: + type: integer + default: 0 + description: > + Auto-pause TTL in seconds. The capsule is automatically paused + after this duration of inactivity (no exec or ping). 0 means + no auto-pause. + + UsageResponse: + type: object + properties: + from: + type: string + format: date + to: + type: string + format: date + points: + type: array + items: + type: object + properties: + date: + type: string + format: date + cpu_minutes: + type: number + ram_mb_minutes: + type: number + + CapsuleStats: + type: object + properties: + range: + type: string + enum: [5m, 1h, 6h, 24h, 30d] + current: + type: object + properties: + running_count: + type: integer + vcpus_reserved: + type: integer + memory_mb_reserved: + type: integer + sampled_at: + type: string + format: date-time + nullable: true + peaks: + type: object + description: Maximum values over the last 30 days. + properties: + running_count: + type: integer + vcpus: + type: integer + memory_mb: + type: integer + series: + type: object + description: Parallel arrays for chart rendering. + properties: + labels: + type: array + items: + type: string + format: date-time + running: + type: array + items: + type: integer + vcpus: + type: array + items: + type: integer + memory_mb: + type: array + items: + type: integer + + Capsule: + type: object + properties: + id: + type: string + status: + type: string + enum: [pending, starting, running, paused, hibernated, stopped, missing, error] + template: + type: string + vcpus: + type: integer + memory_mb: + type: integer + timeout_sec: + type: integer + guest_ip: + type: string + host_ip: + type: string + created_at: + type: string + format: date-time + started_at: + type: string + format: date-time + nullable: true + last_active_at: + type: string + format: date-time + nullable: true + last_updated: + type: string + format: date-time + + CreateSnapshotRequest: + type: object + required: [sandbox_id] + properties: + sandbox_id: + type: string + description: ID of the running capsule to snapshot. + name: + type: string + description: Name for the snapshot template. Auto-generated if omitted. + + Template: + type: object + properties: + name: + type: string + type: + type: string + enum: [base, snapshot] + vcpus: + type: integer + nullable: true + memory_mb: + type: integer + nullable: true + size_bytes: + type: integer + format: int64 + created_at: + type: string + format: date-time + + ExecRequest: + type: object + required: [cmd] + properties: + cmd: + type: string + args: + type: array + items: + type: string + timeout_sec: + type: integer + default: 30 + description: Timeout in seconds (foreground exec only, default 30) + background: + type: boolean + default: false + description: If true, starts the process in the background and returns immediately with a PID and tag (HTTP 202) + tag: + type: string + description: Optional user-chosen tag for the background process. Auto-generated if omitted. Only used when background is true. + envs: + type: object + additionalProperties: + type: string + description: Environment variables for the process (background exec only) + cwd: + type: string + description: Working directory for the process (background exec only) + + BackgroundExecResponse: + type: object + properties: + sandbox_id: + type: string + cmd: + type: string + pid: + type: integer + tag: + type: string + + ProcessEntry: + type: object + properties: + pid: + type: integer + tag: + type: string + cmd: + type: string + args: + type: array + items: + type: string + + ProcessListResponse: + type: object + properties: + processes: + type: array + items: + $ref: "#/components/schemas/ProcessEntry" + + ExecResponse: + type: object + properties: + sandbox_id: + type: string + cmd: + type: string + stdout: + type: string + stderr: + type: string + exit_code: + type: integer + duration_ms: + type: integer + encoding: + type: string + enum: [utf-8, base64] + description: Output encoding. "base64" when stdout/stderr contain binary data. + + ReadFileRequest: + type: object + required: [path] + properties: + path: + type: string + description: Absolute file path inside the capsule + + ListDirRequest: + type: object + required: [path] + properties: + path: + type: string + description: Directory path inside the capsule + depth: + type: integer + default: 1 + description: Recursion depth (0 = non-recursive, 1 = immediate children) + + ListDirResponse: + type: object + properties: + entries: + type: array + items: + $ref: "#/components/schemas/FileEntry" + + FileEntry: + type: object + properties: + name: + type: string + path: + type: string + type: + type: string + enum: [file, directory, symlink] + size: + type: integer + format: int64 + mode: + type: integer + permissions: + type: string + description: Human-readable permissions (e.g. "-rwxr-xr-x") + owner: + type: string + group: + type: string + modified_at: + type: integer + format: int64 + description: Unix timestamp (seconds) + symlink_target: + type: string + nullable: true + + MakeDirRequest: + type: object + required: [path] + properties: + path: + type: string + description: Directory path to create inside the capsule + + MakeDirResponse: + type: object + properties: + entry: + $ref: "#/components/schemas/FileEntry" + + RemoveRequest: + type: object + required: [path] + properties: + path: + type: string + description: Path to remove inside the capsule + + CreateHostRequest: + type: object + required: [type] + properties: + type: + type: string + enum: [regular, byoc] + description: Host type. Regular hosts are shared; BYOC hosts belong to a team. + team_id: + type: string + description: Required for BYOC hosts. + provider: + type: string + description: Cloud provider (e.g. aws, gcp, hetzner, bare-metal). + availability_zone: + type: string + description: Availability zone (e.g. us-east, eu-west). + + CreateHostResponse: + type: object + properties: + host: + $ref: "#/components/schemas/Host" + registration_token: + type: string + description: One-time registration token for the host agent. Expires in 1 hour. + + RegisterHostRequest: + type: object + required: [token, address] + properties: + token: + type: string + description: One-time registration token from POST /v1/hosts. + arch: + type: string + description: CPU architecture (e.g. x86_64, aarch64). + cpu_cores: + type: integer + memory_mb: + type: integer + disk_gb: + type: integer + address: + type: string + description: Host agent address (ip:port). + + RegisterHostResponse: + type: object + properties: + host: + $ref: "#/components/schemas/Host" + token: + type: string + description: Host JWT for X-Host-Token header. Valid for 7 days. + refresh_token: + type: string + description: Refresh token for obtaining new JWTs. Valid for 60 days; rotated on each use. + + Host: + type: object + properties: + id: + type: string + type: + type: string + enum: [regular, byoc] + team_id: + type: string + nullable: true + provider: + type: string + nullable: true + availability_zone: + type: string + nullable: true + arch: + type: string + nullable: true + cpu_cores: + type: integer + nullable: true + memory_mb: + type: integer + nullable: true + disk_gb: + type: integer + nullable: true + address: + type: string + nullable: true + status: + type: string + enum: [pending, online, offline, draining, unreachable] + last_heartbeat_at: + type: string + format: date-time + nullable: true + created_by: + type: string + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + + RefreshHostTokenRequest: + type: object + required: [refresh_token] + properties: + refresh_token: + type: string + description: Refresh token obtained from registration or a previous refresh. + + RefreshHostTokenResponse: + type: object + properties: + host: + $ref: "#/components/schemas/Host" + token: + type: string + description: New host JWT. Valid for 7 days. + refresh_token: + type: string + description: New refresh token. Valid for 60 days; old token is revoked. + + HostDeletePreview: + type: object + properties: + host: + $ref: "#/components/schemas/Host" + sandbox_ids: + type: array + items: + type: string + description: IDs of capsulees that would be destroyed on force-delete. + + HostHasCapsulesError: + type: object + properties: + error: + type: object + properties: + code: + type: string + example: host_has_sandboxes + message: + type: string + sandbox_ids: + type: array + items: + type: string + description: IDs of active capsulees blocking deletion. + + AddTagRequest: + type: object + required: [tag] + properties: + tag: + type: string + + UserSearchResult: + type: object + properties: + user_id: + type: string + email: + type: string + + Team: + type: object + properties: + id: + type: string + name: + type: string + slug: + type: string + description: Immutable 12-char hex slug (e.g. a1b2c3-d1e2f3) + created_at: + type: string + format: date-time + + TeamWithRole: + allOf: + - $ref: "#/components/schemas/Team" + - type: object + properties: + role: + type: string + enum: [owner, admin, member] + + TeamMember: + type: object + properties: + user_id: + type: string + email: + type: string + role: + type: string + enum: [owner, admin, member] + joined_at: + type: string + format: date-time + + TeamDetail: + type: object + properties: + team: + $ref: "#/components/schemas/Team" + members: + type: array + items: + $ref: "#/components/schemas/TeamMember" + + CapsuleMetrics: + type: object + properties: + sandbox_id: + type: string + range: + type: string + enum: ["5m", "10m", "1h", "2h", "6h", "12h", "24h"] + points: + type: array + items: + $ref: "#/components/schemas/MetricPoint" + + MetricPoint: + type: object + properties: + timestamp_unix: + type: integer + format: int64 + cpu_pct: + type: number + format: double + description: "CPU utilization percentage (0-100), normalized to vCPU count" + mem_bytes: + type: integer + format: int64 + description: "Resident memory in bytes (VmRSS of Firecracker process)" + disk_bytes: + type: integer + format: int64 + description: "Allocated disk bytes for the CoW sparse file" + + CreateChannelRequest: + type: object + required: [name, provider, config, events] + properties: + name: + type: string + description: Unique channel name within the team. + provider: + type: string + enum: [discord, slack, teams, googlechat, telegram, matrix, webhook] + config: + type: object + additionalProperties: + type: string + description: > + Provider-specific configuration fields. + Discord/Slack/Teams/Google Chat: {"webhook_url": "..."}. + Telegram: {"bot_token": "...", "chat_id": "..."}. + Matrix: {"homeserver_url": "...", "access_token": "...", "room_id": "..."}. + Webhook: {"url": "...", "secret": "..."} (secret is auto-generated if omitted). + events: + type: array + items: + type: string + enum: + - capsule.created + - capsule.running + - capsule.paused + - capsule.destroyed + - template.snapshot.created + - template.snapshot.deleted + - host.up + - host.down + + TestChannelRequest: + type: object + required: [provider, config] + properties: + provider: + type: string + enum: [discord, slack, teams, googlechat, telegram, matrix, webhook] + config: + type: object + additionalProperties: + type: string + description: Provider-specific configuration fields (same as CreateChannelRequest.config). + + RotateConfigRequest: + type: object + required: [config] + properties: + config: + type: object + additionalProperties: + type: string + description: > + New provider configuration fields. Must include all required fields + for the channel's provider. Replaces the existing config entirely. + + UpdateChannelRequest: + type: object + required: [name, events] + properties: + name: + type: string + events: + type: array + items: + type: string + enum: + - capsule.created + - capsule.running + - capsule.paused + - capsule.destroyed + - template.snapshot.created + - template.snapshot.deleted + - host.up + - host.down + + ChannelResponse: + type: object + properties: + id: + type: string + team_id: + type: string + name: + type: string + provider: + type: string + enum: [discord, slack, teams, googlechat, telegram, matrix, webhook] + events: + type: array + items: + type: string + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + secret: + type: string + nullable: true + description: Webhook secret. Only returned on creation, never again. + + MeResponse: + type: object + properties: + name: + type: string + email: + type: string + format: email + has_password: + type: boolean + description: Whether the user has a password set (false for OAuth-only accounts) + providers: + type: array + items: + type: string + description: List of linked OAuth provider names (e.g. ["github"]) + + ChangePasswordRequest: + type: object + required: [new_password] + properties: + current_password: + type: string + description: Required when changing an existing password + new_password: + type: string + minLength: 8 + confirm_password: + type: string + description: Required when adding a password to an OAuth-only account (must match new_password) + + Error: + type: object + properties: + error: + type: object + properties: + code: + type: string + message: + type: string diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..ee7a309 --- /dev/null +++ b/biome.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.14/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": ["**", "!src/models/generated.ts", "!dist"] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5032bb5 --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "js-sdk", + "version": "0.1.0", + "description": "Wrenn JavaScript SDK — a client library for the Wrenn microVM platform.", + "type": "module", + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/dts/index.d.ts", + "exports": { + ".": { + "types": "./dist/dts/index.d.ts", + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, + "scripts": { + "build": "tsup", + "check": "make lint && make test", + "test": "vitest run", + "lint": "make lint", + "test:watch": "vitest", + "test:integration": "vitest run --config vitest.integration.config.ts", + "generate": "openapi-typescript api/openapi.yaml --output src/models/generated.ts", + "format": "biome format --write ." + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@10.26.1", + "devDependencies": { + "@biomejs/biome": "^2.4.14", + "@types/node": "^25.6.0", + "@types/ws": "^8.18.1", + "msw": "^2.14.3", + "openapi-typescript": "^7.13.0", + "tsup": "^8.5.1", + "typescript": "^6.0.3", + "vitest": "^4.1.5" + }, + "dependencies": { + "ws": "^8.20.0", + "zod": "^4.4.3" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..f9d43f0 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2321 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + ws: + specifier: ^8.20.0 + version: 8.20.0 + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@biomejs/biome': + specifier: ^2.4.14 + version: 2.4.14 + '@types/node': + specifier: ^25.6.0 + version: 25.6.0 + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 + msw: + specifier: ^2.14.3 + version: 2.14.3(@types/node@25.6.0)(typescript@6.0.3) + openapi-typescript: + specifier: ^7.13.0 + version: 7.13.0(typescript@6.0.3) + tsup: + specifier: ^8.5.1 + version: 8.5.1(postcss@8.5.14)(typescript@6.0.3) + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vitest: + specifier: ^4.1.5 + version: 4.1.5(@types/node@25.6.0)(msw@2.14.3(@types/node@25.6.0)(typescript@6.0.3))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)) + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.4.14': + resolution: {integrity: sha512-TmAvxOEgrpLypzVGJ8FulIZnlyA9TxrO1hyqYrCz9r+bwma9xXxuLA5IuYnj55XQneFx460KjRbx6SWGLkg3bQ==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.4.14': + resolution: {integrity: sha512-XvgoE9XOawUOQPdmvs4J7wPhi/DLwSCGks3AlPJDmh34O0awRTqCED1HRcRDdpf1Zrp4us4MGOOdIxNpbqNF5Q==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.4.14': + resolution: {integrity: sha512-jE7hKBCFhOx3uUh+ZkWBfOHxAcILPfhFplNkuID/eZeSTLHzfZzoZxW8fbqY9xXRnPi7jGNAf1iPVR+0yWsM/Q==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.4.14': + resolution: {integrity: sha512-/z+6gqAqqUQTHazwStxSXKHg9b8UvqBmDFRp+c4wYbq2KXhELQDon9EoC9RpmQ8JWkqQx/lIUy/cs+MhzDZp6A==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.4.14': + resolution: {integrity: sha512-2TELhZnW5RSLL063l9rc5xLpA0ZIw0Ccwy/0q384rvNAgFw3yI76bd59547yxowdQr5MNPET/xDLrLuvgSeeWQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.4.14': + resolution: {integrity: sha512-R6BWgJdQOwW9ulJatuTVrQkjnODjqHZkKNOqb1sz++3Noe5LYd0i3PchnOBUCYAPHoPWHhjJqbdZlHEu0hpjdA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.4.14': + resolution: {integrity: sha512-zHrlQZDBDUz4OLAraYpWKcnLS6HOewBFWYOzY91d1ZjdqZwibOyb6BEu6WuWLugyo0P3riCmsbV9UqV1cSXwQg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.4.14': + resolution: {integrity: sha512-M3EH5hqOI/F/FUA2u4xcLoUgmxd218mvuj/6JL7Hv2toQvr2/AdOvKSpGkoRuWFCtQPVa+ZqkEV3Q5xBA9+XSA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.4.14': + resolution: {integrity: sha512-WL0EG5qE+EAKomGXbf2g6VnSKJhTL3tXC0QRzWRwA5VpjxNYa6H4P7ZWfymbGE4IhZZQi1KXQ2R0YjwInmz2fA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@inquirer/ansi@2.0.5': + resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/confirm@6.0.12': + resolution: {integrity: sha512-h9FgGun3QwVYNj5TWIZZ+slii73bMoBFjPfVIGtnFuL4t8gBiNDV9PcSfIzkuxvgquJKt9nr1QzszpBzTbH8Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.1.9': + resolution: {integrity: sha512-BDE4fG22uYh1bGSifcj7JSx119TVYNViMhMu85usp4Fswrzh6M0DV3yld64jA98uOAa2GSQ4Bg4bZRm2d2cwSg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.5': + resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/type@4.0.5': + resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mswjs/interceptors@0.41.8': + resolution: {integrity: sha512-pRLMNKTSGRoLq+KnEB/7OY5vijw1XmcheAAOiv6pj7W1FG32kAGqj1C/RK/cqxRGr1Fh+zBi8sDur8kj3EQv6A==} + engines: {node: '>=18'} + + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/deferred-promise@3.0.0': + resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + + '@oxc-project/types@0.127.0': + resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + + '@redocly/ajv@8.11.2': + resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} + + '@redocly/config@0.22.0': + resolution: {integrity: sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==} + + '@redocly/openapi-core@1.34.14': + resolution: {integrity: sha512-y+xFx+Zz54Xhr8jUdnLENYnt7Y7GEDL6Q03ga7rTtX8DVwefX9H+hQEPgJp1nda7vdH+wJ9/HBVvyfBuW9x6rA==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + + '@rolldown/binding-android-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.17': + resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-rc.17': + resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} + + '@rollup/rollup-android-arm-eabi@4.60.3': + resolution: {integrity: sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.3': + resolution: {integrity: sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.3': + resolution: {integrity: sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.3': + resolution: {integrity: sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.3': + resolution: {integrity: sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.3': + resolution: {integrity: sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.60.3': + resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.60.3': + resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.60.3': + resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.3': + resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.3': + resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.60.3': + resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.60.3': + resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.60.3': + resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.60.3': + resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.60.3': + resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.3': + resolution: {integrity: sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.3': + resolution: {integrity: sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.3': + resolution: {integrity: sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.3': + resolution: {integrity: sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.3': + resolution: {integrity: sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + + '@types/set-cookie-parser@2.4.10': + resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@vitest/expect@4.1.5': + resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} + + '@vitest/mocker@4.1.5': + resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.5': + resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} + + '@vitest/runner@4.1.5': + resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==} + + '@vitest/snapshot@4.1.5': + resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==} + + '@vitest/spy@4.1.5': + resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} + + '@vitest/utils@4.1.5': + resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.1.0: + resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@1.4.0: + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + graphql@16.13.2: + resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + headers-polyfill@5.0.1: + resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msw@2.14.3: + resolution: {integrity: sha512-kk8G5cocVlJ4wsKMGZegn2H6XLOEKjbA+nSJE2354e/SRp4mDicCHUYnMXpymzVcVDCs+GUAsmNqSn+yHv4T2A==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + openapi-typescript@7.13.0: + resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==} + hasBin: true + peerDependencies: + typescript: ^5.x + + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + 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 + + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + engines: {node: ^10 || ^12 || >=14} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + rettime@0.11.11: + resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==} + + rolldown@1.0.0-rc.17: + resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup@4.60.3: + resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + set-cookie-parser@3.1.0: + resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + engines: {node: '>=18'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + tldts-core@7.0.30: + resolution: {integrity: sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==} + + tldts@7.0.30: + resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==} + hasBin: true + + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} + engines: {node: '>=18'} + hasBin: true + 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 + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + + uri-js-replace@1.0.1: + resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==} + + vite@8.0.10: + resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.5: + resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.5 + '@vitest/browser-preview': 4.1.5 + '@vitest/browser-webdriverio': 4.1.5 + '@vitest/coverage-istanbul': 4.1.5 + '@vitest/coverage-v8': 4.1.5 + '@vitest/ui': 4.1.5 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.28.5': {} + + '@biomejs/biome@2.4.14': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.4.14 + '@biomejs/cli-darwin-x64': 2.4.14 + '@biomejs/cli-linux-arm64': 2.4.14 + '@biomejs/cli-linux-arm64-musl': 2.4.14 + '@biomejs/cli-linux-x64': 2.4.14 + '@biomejs/cli-linux-x64-musl': 2.4.14 + '@biomejs/cli-win32-arm64': 2.4.14 + '@biomejs/cli-win32-x64': 2.4.14 + + '@biomejs/cli-darwin-arm64@2.4.14': + optional: true + + '@biomejs/cli-darwin-x64@2.4.14': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.4.14': + optional: true + + '@biomejs/cli-linux-arm64@2.4.14': + optional: true + + '@biomejs/cli-linux-x64-musl@2.4.14': + optional: true + + '@biomejs/cli-linux-x64@2.4.14': + optional: true + + '@biomejs/cli-win32-arm64@2.4.14': + optional: true + + '@biomejs/cli-win32-x64@2.4.14': + optional: true + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@inquirer/ansi@2.0.5': {} + + '@inquirer/confirm@6.0.12(@types/node@25.6.0)': + dependencies: + '@inquirer/core': 11.1.9(@types/node@25.6.0) + '@inquirer/type': 4.0.5(@types/node@25.6.0) + optionalDependencies: + '@types/node': 25.6.0 + + '@inquirer/core@11.1.9(@types/node@25.6.0)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@25.6.0) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 25.6.0 + + '@inquirer/figures@2.0.5': {} + + '@inquirer/type@4.0.5(@types/node@25.6.0)': + optionalDependencies: + '@types/node': 25.6.0 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mswjs/interceptors@0.41.8': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/deferred-promise@3.0.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + + '@oxc-project/types@0.127.0': {} + + '@redocly/ajv@8.11.2': + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js-replace: 1.0.1 + + '@redocly/config@0.22.0': {} + + '@redocly/openapi-core@1.34.14(supports-color@10.2.2)': + dependencies: + '@redocly/ajv': 8.11.2 + '@redocly/config': 0.22.0 + colorette: 1.4.0 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + js-levenshtein: 1.1.6 + js-yaml: 4.1.1 + minimatch: 5.1.9 + pluralize: 8.0.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - supports-color + + '@rolldown/binding-android-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + optional: true + + '@rolldown/pluginutils@1.0.0-rc.17': {} + + '@rollup/rollup-android-arm-eabi@4.60.3': + optional: true + + '@rollup/rollup-android-arm64@4.60.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.3': + optional: true + + '@rollup/rollup-darwin-x64@4.60.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.3': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.3': + optional: true + + '@standard-schema/spec@1.1.0': {} + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/node@25.6.0': + dependencies: + undici-types: 7.19.2 + + '@types/set-cookie-parser@2.4.10': + dependencies: + '@types/node': 25.6.0 + + '@types/statuses@2.0.6': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.6.0 + + '@vitest/expect@4.1.5': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.5(msw@2.14.3(@types/node@25.6.0)(typescript@6.0.3))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7))': + dependencies: + '@vitest/spy': 4.1.5 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.14.3(@types/node@25.6.0)(typescript@6.0.3) + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7) + + '@vitest/pretty-format@4.1.5': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.5': + dependencies: + '@vitest/utils': 4.1.5 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + '@vitest/utils': 4.1.5 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.5': {} + + '@vitest/utils@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + acorn@8.16.0: {} + + agent-base@7.1.4: {} + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + any-promise@1.3.0: {} + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.1.0: + dependencies: + balanced-match: 1.0.2 + + bundle-require@5.1.0(esbuild@0.27.7): + dependencies: + esbuild: 0.27.7 + load-tsconfig: 0.2.5 + + cac@6.7.14: {} + + chai@6.2.2: {} + + change-case@5.4.4: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + cli-width@4.1.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@1.4.0: {} + + commander@4.1.1: {} + + confbox@0.1.8: {} + + consola@3.4.2: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + debug@4.4.3(supports-color@10.2.2): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 + + detect-libc@2.1.2: {} + + emoji-regex@8.0.0: {} + + es-module-lexer@2.1.0: {} + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + escalade@3.2.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + expect-type@1.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.60.3 + + fsevents@2.3.3: + optional: true + + get-caller-file@2.0.5: {} + + graphql@16.13.2: {} + + headers-polyfill@5.0.1: + dependencies: + '@types/set-cookie-parser': 2.4.10 + set-cookie-parser: 3.1.0 + + https-proxy-agent@7.0.6(supports-color@10.2.2): + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + index-to-position@1.2.0: {} + + is-fullwidth-code-point@3.0.0: {} + + is-node-process@1.2.0: {} + + joycon@3.1.1: {} + + js-levenshtein@1.1.6: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-schema-traverse@1.0.0: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.0 + + mlly@1.8.2: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + + ms@2.1.3: {} + + msw@2.14.3(@types/node@25.6.0)(typescript@6.0.3): + dependencies: + '@inquirer/confirm': 6.0.12(@types/node@25.6.0) + '@mswjs/interceptors': 0.41.8 + '@open-draft/deferred-promise': 3.0.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.13.2 + headers-polyfill: 5.0.1 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.11.11 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.1 + type-fest: 5.6.0 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@types/node' + + mute-stream@3.0.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.12: {} + + object-assign@4.1.1: {} + + obug@2.1.1: {} + + openapi-typescript@7.13.0(typescript@6.0.3): + dependencies: + '@redocly/openapi-core': 1.34.14(supports-color@10.2.2) + ansi-colors: 4.1.3 + change-case: 5.4.4 + parse-json: 8.3.0 + supports-color: 10.2.2 + typescript: 6.0.3 + yargs-parser: 21.1.1 + + outvariant@1.4.3: {} + + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.29.0 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + path-to-regexp@6.3.0: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + pirates@4.0.7: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + pluralize@8.0.0: {} + + postcss-load-config@6.0.1(postcss@8.5.14): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.5.14 + + postcss@8.5.14: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + readdirp@4.1.2: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@5.0.0: {} + + rettime@0.11.11: {} + + rolldown@1.0.0-rc.17: + dependencies: + '@oxc-project/types': 0.127.0 + '@rolldown/pluginutils': 1.0.0-rc.17 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.17 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.17 + '@rolldown/binding-darwin-x64': 1.0.0-rc.17 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.17 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 + + rollup@4.60.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.3 + '@rollup/rollup-android-arm64': 4.60.3 + '@rollup/rollup-darwin-arm64': 4.60.3 + '@rollup/rollup-darwin-x64': 4.60.3 + '@rollup/rollup-freebsd-arm64': 4.60.3 + '@rollup/rollup-freebsd-x64': 4.60.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.3 + '@rollup/rollup-linux-arm-musleabihf': 4.60.3 + '@rollup/rollup-linux-arm64-gnu': 4.60.3 + '@rollup/rollup-linux-arm64-musl': 4.60.3 + '@rollup/rollup-linux-loong64-gnu': 4.60.3 + '@rollup/rollup-linux-loong64-musl': 4.60.3 + '@rollup/rollup-linux-ppc64-gnu': 4.60.3 + '@rollup/rollup-linux-ppc64-musl': 4.60.3 + '@rollup/rollup-linux-riscv64-gnu': 4.60.3 + '@rollup/rollup-linux-riscv64-musl': 4.60.3 + '@rollup/rollup-linux-s390x-gnu': 4.60.3 + '@rollup/rollup-linux-x64-gnu': 4.60.3 + '@rollup/rollup-linux-x64-musl': 4.60.3 + '@rollup/rollup-openbsd-x64': 4.60.3 + '@rollup/rollup-openharmony-arm64': 4.60.3 + '@rollup/rollup-win32-arm64-msvc': 4.60.3 + '@rollup/rollup-win32-ia32-msvc': 4.60.3 + '@rollup/rollup-win32-x64-gnu': 4.60.3 + '@rollup/rollup-win32-x64-msvc': 4.60.3 + fsevents: 2.3.3 + + set-cookie-parser@3.1.0: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@4.1.0: {} + + strict-event-emitter@0.5.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.16 + ts-interface-checker: 0.1.13 + + supports-color@10.2.2: {} + + tagged-tag@1.0.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyexec@1.1.2: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyrainbow@3.1.0: {} + + tldts-core@7.0.30: {} + + tldts@7.0.30: + dependencies: + tldts-core: 7.0.30 + + tough-cookie@6.0.1: + dependencies: + tldts: 7.0.30 + + tree-kill@1.2.2: {} + + ts-interface-checker@0.1.13: {} + + tslib@2.8.1: + optional: true + + tsup@8.5.1(postcss@8.5.14)(typescript@6.0.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.27.7) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3(supports-color@10.2.2) + esbuild: 0.27.7 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.14) + resolve-from: 5.0.0 + rollup: 4.60.3 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.16 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.14 + typescript: 6.0.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + + type-fest@4.41.0: {} + + type-fest@5.6.0: + dependencies: + tagged-tag: 1.0.0 + + typescript@6.0.3: {} + + ufo@1.6.4: {} + + undici-types@7.19.2: {} + + until-async@3.0.2: {} + + uri-js-replace@1.0.1: {} + + vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.14 + rolldown: 1.0.0-rc.17 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 25.6.0 + esbuild: 0.27.7 + fsevents: 2.3.3 + + vitest@4.1.5(@types/node@25.6.0)(msw@2.14.3(@types/node@25.6.0)(typescript@6.0.3))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)): + dependencies: + '@vitest/expect': 4.1.5 + '@vitest/mocker': 4.1.5(msw@2.14.3(@types/node@25.6.0)(typescript@6.0.3))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.7)) + '@vitest/pretty-format': 4.1.5 + '@vitest/runner': 4.1.5 + '@vitest/snapshot': 4.1.5 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.1.2 + tinyglobby: 0.2.16 + tinyrainbow: 3.1.0 + vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.7) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.6.0 + transitivePeerDependencies: + - msw + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + ws@8.20.0: {} + + y18n@5.0.8: {} + + yaml-ast-parser@0.0.43: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + zod@4.4.3: {} diff --git a/src/models/generated.ts b/src/models/generated.ts new file mode 100644 index 0000000..7fff265 --- /dev/null +++ b/src/models/generated.ts @@ -0,0 +1,4252 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/v1/auth/signup": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create a new account + * @description Creates an inactive user account and sends an activation email. + * The user must activate their account within 30 minutes. + * Does not return a JWT — the user must activate first, then sign in. + */ + post: operations["signup"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/auth/activate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Activate account via email token + * @description Consumes the activation token sent via email and activates the user account. + * Creates a default team and returns a JWT to log the user in. + */ + post: operations["activate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/auth/switch-team": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Switch active team + * @description Re-issues a JWT scoped to a different team. The user must be a member of + * the target team (verified from DB). Use the returned token for subsequent + * requests to that team's resources. + */ + post: operations["switchTeam"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/auth/login": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Log in with email and password */ + post: operations["login"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/auth/oauth/{provider}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description OAuth provider name */ + provider: "github"; + }; + cookie?: never; + }; + /** + * Start OAuth login flow + * @description Redirects the user to the OAuth provider's authorization page. + * Sets a short-lived CSRF state cookie for validation on callback. + */ + get: operations["oauthRedirect"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/auth/oauth/{provider}/callback": { + parameters: { + query?: never; + header?: never; + path: { + /** @description OAuth provider name */ + provider: "github"; + }; + cookie?: never; + }; + /** + * OAuth callback + * @description Handles the OAuth provider's callback after user authorization. + * Exchanges the authorization code for a user profile, creates or + * logs in the user, and redirects to the frontend with a JWT token. + * + * **On success:** redirects to `{OAUTH_REDIRECT_URL}/auth/{provider}/callback?token=...&user_id=...&team_id=...&email=...` + * + * **On error:** redirects to `{OAUTH_REDIRECT_URL}/auth/{provider}/callback?error=...` + * + * Possible error codes: `access_denied`, `invalid_state`, `missing_code`, + * `exchange_failed`, `email_taken`, `internal_error`. + */ + get: operations["oauthCallback"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/me": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get current user profile */ + get: operations["getMe"]; + put?: never; + post?: never; + /** + * Delete current account + * @description Soft-deletes the account (sets status=deleted, deleted_at=now). + * The account is permanently removed after 15 days. Blocked if the user + * owns any team that has other members. + */ + delete: operations["deleteAccount"]; + options?: never; + head?: never; + /** Update display name */ + patch: operations["updateName"]; + trace?: never; + }; + "/v1/me/password": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Change or add password + * @description For users with an existing password: requires `current_password` and `new_password`. + * For OAuth-only users adding a password: requires `new_password` and `confirm_password`. + */ + post: operations["changePassword"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/me/password/reset": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Request a password reset email + * @description Sends a password reset link to the given email. Always returns 200 + * regardless of whether the email exists, to prevent account enumeration. + * The reset token expires in 15 minutes. + */ + post: operations["requestPasswordReset"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/me/password/reset/confirm": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Confirm password reset + * @description Consumes a password reset token and sets a new password. The token is + * single-use and expires after 15 minutes. + */ + post: operations["confirmPasswordReset"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/me/providers/{provider}/connect": { + parameters: { + query?: never; + header?: never; + path: { + /** @description OAuth provider name */ + provider: "github"; + }; + cookie?: never; + }; + /** + * Initiate OAuth provider link + * @description Sets OAuth state and link cookies, then returns the provider's + * authorization URL. The frontend navigates to this URL to start the + * OAuth flow. On callback, the provider is linked to the current account + * (not a new registration). + */ + get: operations["connectProvider"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/me/providers/{provider}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description OAuth provider name */ + provider: "github"; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * Disconnect an OAuth provider + * @description Unlinks the OAuth provider from the current account. Blocked if this + * is the user's only login method (no password and no other providers). + */ + delete: operations["disconnectProvider"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/api-keys": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List API keys for your team */ + get: operations["listAPIKeys"]; + put?: never; + /** Create an API key */ + post: operations["createAPIKey"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/api-keys/{id}": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete an API key */ + delete: operations["deleteAPIKey"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/users/search": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Search users by email prefix + * @description Returns up to 10 users whose email starts with the given prefix. + * The prefix must contain "@". Intended for the add-member UI autocomplete. + */ + get: operations["searchUsers"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/teams": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List teams for the authenticated user */ + get: operations["listTeams"]; + put?: never; + /** Create a new team */ + post: operations["createTeam"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/teams/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description Team ID (must match the JWT's team_id) */ + id: string; + }; + cookie?: never; + }; + /** Get team info and member list */ + get: operations["getTeam"]; + put?: never; + post?: never; + /** + * Delete the team + * @description Owner only. Soft-deletes the team and destroys all running/paused/starting + * capsulees. All DB records are preserved. The team slug is permanently reserved. + */ + delete: operations["deleteTeam"]; + options?: never; + head?: never; + /** + * Rename the team + * @description Admin or owner role required (verified from DB). + */ + patch: operations["renameTeam"]; + trace?: never; + }; + "/v1/teams/{id}/members": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** List team members */ + get: operations["listTeamMembers"]; + put?: never; + /** + * Add a member by email + * @description Admin or owner role required. User is added instantly as a member. + */ + post: operations["addTeamMember"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/teams/{id}/members/{uid}": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + /** @description Target user ID */ + uid: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * Remove a member + * @description Admin or owner required. Owner cannot be removed. + */ + delete: operations["removeTeamMember"]; + options?: never; + head?: never; + /** + * Update member role + * @description Admin or owner required. Valid target roles: admin, member. + * The owner's role cannot be changed. + */ + patch: operations["updateMemberRole"]; + trace?: never; + }; + "/v1/teams/{id}/leave": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Leave the team + * @description The owner cannot leave; they must delete the team instead. + */ + post: operations["leaveTeam"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List capsulees for your team */ + get: operations["listCapsules"]; + put?: never; + /** Create a capsule */ + post: operations["createCapsule"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get capsule usage stats for your team */ + get: operations["getCapsuleStats"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/usage": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get daily CPU and RAM usage for your team */ + get: operations["getCapsuleUsage"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** Get capsule details */ + get: operations["getCapsule"]; + put?: never; + post?: never; + /** Destroy a capsule */ + delete: operations["destroyCapsule"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/exec": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** Execute a command */ + post: operations["execCommand"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/processes": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** + * List running processes + * @description Returns all running processes inside the capsule, including background + * processes and any processes started by templates or init scripts. + */ + get: operations["listProcesses"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/processes/{selector}": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + /** @description Process PID (numeric) or tag (string) */ + selector: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Kill a process */ + delete: operations["killProcess"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/processes/{selector}/stream": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + /** @description Process PID (numeric) or tag (string) */ + selector: string; + }; + cookie?: never; + }; + /** + * Stream process output via WebSocket + * @description Opens a WebSocket connection to stream stdout/stderr from a running + * background process. The selector can be a numeric PID or a string tag. + * + * Server sends JSON messages: + * - `{"type": "start", "pid": 42}` — connected to process + * - `{"type": "stdout", "data": "..."}` — stdout output + * - `{"type": "stderr", "data": "..."}` — stderr output + * - `{"type": "exit", "exit_code": 0}` — process exited + * - `{"type": "error", "data": "..."}` — error message + */ + get: operations["connectProcess"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/ping": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Reset capsule inactivity timer + * @description Resets the last_active_at timestamp for a running capsule, preventing + * the auto-pause TTL from expiring. Use this as a keepalive for capsulees + * that are idle but should remain running. + */ + post: operations["pingCapsule"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/metrics": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** + * Get per-capsule resource metrics + * @description Returns time-series CPU, memory, and disk metrics for a capsule. + * Three tiers are available with different granularity and retention: + * - `10m`: 500ms samples, last 10 minutes + * - `2h`: 30-second averages, last 2 hours + * - `24h`: 5-minute averages, last 24 hours + * + * For running capsulees, data comes from the host agent's in-memory + * ring buffer. For paused capsulees, data is read from persisted + * snapshots in the database. Stopped/destroyed capsulees return 404. + */ + get: operations["getCapsuleMetrics"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/pause": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Pause a running capsule + * @description Takes a snapshot of the capsule (VM state + memory + rootfs), then + * destroys all running resources. The capsule exists only as files on + * disk and can be resumed later. + */ + post: operations["pauseCapsule"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/resume": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Resume a paused capsule + * @description Restores a paused capsule from its snapshot using UFFD for lazy + * memory loading. Boots a fresh Firecracker process, sets up a new + * network slot, and waits for envd to become ready. + */ + post: operations["resumeCapsule"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/snapshots": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List templates for your team */ + get: operations["listSnapshots"]; + put?: never; + /** + * Create a snapshot template + * @description Pauses a running capsule, takes a full snapshot, copies the snapshot + * files to the images directory as a reusable template, then destroys + * the capsule. The template can be used to create new capsulees. + */ + post: operations["createSnapshot"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/snapshots/{name}": { + parameters: { + query?: never; + header?: never; + path: { + name: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * Delete a snapshot template + * @description Removes the snapshot files from disk and deletes the database record. + */ + delete: operations["deleteSnapshot"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/files/write": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** Upload a file */ + post: operations["uploadFile"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/files/read": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** Download a file */ + post: operations["downloadFile"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/files/list": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** List directory contents */ + post: operations["listDir"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/files/mkdir": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** Create a directory */ + post: operations["makeDir"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/files/remove": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** Remove a file or directory */ + post: operations["removePath"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/exec/stream": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** + * Stream command execution via WebSocket + * @description Opens a WebSocket connection for streaming command execution. + * + * **Client sends** (first message to start the process): + * ```json + * {"type": "start", "cmd": "tail", "args": ["-f", "/var/log/syslog"]} + * ``` + * + * **Client sends** (to stop the process): + * ```json + * {"type": "stop"} + * ``` + * + * **Server sends** (process events as they arrive): + * ```json + * {"type": "start", "pid": 1234} + * {"type": "stdout", "data": "line of output\n"} + * {"type": "stderr", "data": "warning message\n"} + * {"type": "exit", "exit_code": 0} + * {"type": "error", "data": "description of error"} + * ``` + * + * The connection closes automatically after the process exits. + */ + get: operations["execStream"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/pty": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** + * Interactive PTY session via WebSocket + * @description Opens a WebSocket connection for an interactive PTY (terminal) session. + * Supports creating new sessions, sending input, resizing, killing, and + * reconnecting to existing sessions. + * + * **Client sends** (first message — start a new PTY): + * ```json + * { + * "type": "start", + * "cmd": "/bin/bash", + * "args": [], + * "cols": 80, + * "rows": 24, + * "envs": {"TERM": "xterm-256color"}, + * "cwd": "/home/user", + * "user": "user" + * } + * ``` + * All fields except `type` are optional. Defaults: cmd="/bin/bash", cols=80, rows=24. + * + * **Client sends** (first message — reconnect to existing PTY): + * ```json + * {"type": "connect", "tag": "pty-abc123de"} + * ``` + * + * **Client sends** (after session is established): + * ```json + * {"type": "input", "data": ""} + * {"type": "resize", "cols": 120, "rows": 40} + * {"type": "kill"} + * ``` + * + * **Server sends**: + * ```json + * {"type": "started", "tag": "pty-abc123de", "pid": 42} + * {"type": "output", "data": ""} + * {"type": "exit", "exit_code": 0} + * {"type": "error", "data": "description", "fatal": true} + * {"type": "ping"} + * ``` + * + * PTY data (input and output) is base64-encoded because it contains raw + * terminal bytes (escape sequences, control codes) that are not valid UTF-8. + * + * Sessions persist across WebSocket disconnections — the process keeps + * running in the capsule. Use the `tag` from the "started" response to + * reconnect later. + */ + get: operations["ptySession"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/files/stream/write": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Upload a file (streaming) + * @description Streams file content to the capsule without buffering in memory. + * Suitable for large files. Uses the same multipart/form-data format + * as the non-streaming upload endpoint. + */ + post: operations["streamUploadFile"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/capsules/{id}/files/stream/read": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Download a file (streaming) + * @description Streams file content from the capsule without buffering in memory. + * Suitable for large files. Returns raw bytes with chunked transfer encoding. + */ + post: operations["streamDownloadFile"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List hosts + * @description Admins see all hosts. Non-admins see only BYOC hosts belonging to their team. + */ + get: operations["listHosts"]; + put?: never; + /** + * Create a host + * @description Creates a new host record and returns a one-time registration token. + * Regular hosts can only be created by admins. BYOC hosts can be created + * by admins or team owners. + */ + post: operations["createHost"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts/{id}": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** Get host details */ + get: operations["getHost"]; + put?: never; + post?: never; + /** + * Delete a host + * @description Admins can delete any host. Team owners and admins can delete BYOC hosts + * belonging to their team. Without `?force=true`, returns 409 if the host + * has active capsulees. With `?force=true`, destroys all capsulees first. + */ + delete: operations["deleteHost"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts/{id}/token": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Regenerate registration token + * @description Issues a new registration token for a host still in "pending" status. + * Use this when a previous registration attempt failed after consuming + * the original token. Same permission model as host creation. + */ + post: operations["regenerateHostToken"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts/register": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Register a host agent + * @description Called by the host agent on first startup. Validates the one-time + * registration token, records machine specs, sets the host status to + * "online", and returns a long-lived JWT for subsequent API calls + * (heartbeats). + */ + post: operations["registerHost"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts/{id}/heartbeat": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Host agent heartbeat + * @description Updates the host's last_heartbeat_at timestamp. The host ID in the URL + * must match the host ID in the JWT. + */ + post: operations["hostHeartbeat"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts/auth/refresh": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Refresh host JWT + * @description Exchanges a refresh token for a new JWT and rotated refresh token. + * The old refresh token is immediately revoked. No authentication required — + * the refresh token itself is the credential. + */ + post: operations["refreshHostToken"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts/{id}/delete-preview": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** + * Preview host deletion + * @description Returns the list of capsule IDs that would be destroyed if the host + * were deleted with `?force=true`. No state is modified. + */ + get: operations["getHostDeletePreview"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts/{id}/tags": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** List host tags */ + get: operations["listHostTags"]; + put?: never; + /** Add a tag to a host */ + post: operations["addHostTag"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/hosts/{id}/tags/{tag}": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + tag: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Remove a tag from a host */ + delete: operations["removeHostTag"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/channels": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List notification channels */ + get: operations["listChannels"]; + put?: never; + /** Create a notification channel */ + post: operations["createChannel"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/channels/test": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Test a channel configuration + * @description Sends a test notification using the provided provider and config without saving anything. Use this to verify credentials before creating a channel. + */ + post: operations["testChannel"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/channels/{id}": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + /** Get a notification channel */ + get: operations["getChannel"]; + put?: never; + post?: never; + /** Delete a notification channel */ + delete: operations["deleteChannel"]; + options?: never; + head?: never; + /** Update a notification channel */ + patch: operations["updateChannel"]; + trace?: never; + }; + "/v1/channels/{id}/config": { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + get?: never; + /** + * Rotate channel secrets + * @description Replaces the channel's provider configuration entirely with new secrets. The previous config is discarded. Config fields must match the provider's required fields. + */ + put: operations["rotateChannelConfig"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + SignupRequest: { + /** Format: email */ + email: string; + password: string; + name: string; + }; + LoginRequest: { + /** Format: email */ + email: string; + password: string; + }; + SignupResponse: { + /** @description Confirmation message instructing user to check email */ + message?: string; + }; + AuthResponse: { + /** @description JWT token (valid for 6 hours) */ + token?: string; + user_id?: string; + team_id?: string; + email?: string; + name?: string; + }; + CreateAPIKeyRequest: { + /** @default Unnamed API Key */ + name: string; + }; + APIKeyResponse: { + id?: string; + team_id?: string; + name?: string; + /** @description Display prefix (e.g. "wrn_ab12cd34...") */ + key_prefix?: string; + /** Format: date-time */ + created_at?: string; + /** Format: date-time */ + last_used?: string | null; + /** @description Full plaintext key. Only returned on creation, never again. */ + key?: string | null; + }; + CreateCapsuleRequest: { + /** @default minimal */ + template: string; + /** @default 1 */ + vcpus: number; + /** @default 512 */ + memory_mb: number; + /** + * @description Auto-pause TTL in seconds. The capsule is automatically paused after this duration of inactivity (no exec or ping). 0 means no auto-pause. + * @default 0 + */ + timeout_sec: number; + }; + UsageResponse: { + /** Format: date */ + from?: string; + /** Format: date */ + to?: string; + points?: { + /** Format: date */ + date?: string; + cpu_minutes?: number; + ram_mb_minutes?: number; + }[]; + }; + CapsuleStats: { + /** @enum {string} */ + range?: "5m" | "1h" | "6h" | "24h" | "30d"; + current?: { + running_count?: number; + vcpus_reserved?: number; + memory_mb_reserved?: number; + /** Format: date-time */ + sampled_at?: string | null; + }; + /** @description Maximum values over the last 30 days. */ + peaks?: { + running_count?: number; + vcpus?: number; + memory_mb?: number; + }; + /** @description Parallel arrays for chart rendering. */ + series?: { + labels?: string[]; + running?: number[]; + vcpus?: number[]; + memory_mb?: number[]; + }; + }; + Capsule: { + id?: string; + /** @enum {string} */ + status?: "pending" | "starting" | "running" | "paused" | "hibernated" | "stopped" | "missing" | "error"; + template?: string; + vcpus?: number; + memory_mb?: number; + timeout_sec?: number; + guest_ip?: string; + host_ip?: string; + /** Format: date-time */ + created_at?: string; + /** Format: date-time */ + started_at?: string | null; + /** Format: date-time */ + last_active_at?: string | null; + /** Format: date-time */ + last_updated?: string; + }; + CreateSnapshotRequest: { + /** @description ID of the running capsule to snapshot. */ + sandbox_id: string; + /** @description Name for the snapshot template. Auto-generated if omitted. */ + name?: string; + }; + Template: { + name?: string; + /** @enum {string} */ + type?: "base" | "snapshot"; + vcpus?: number | null; + memory_mb?: number | null; + /** Format: int64 */ + size_bytes?: number; + /** Format: date-time */ + created_at?: string; + }; + ExecRequest: { + cmd: string; + args?: string[]; + /** + * @description Timeout in seconds (foreground exec only, default 30) + * @default 30 + */ + timeout_sec: number; + /** + * @description If true, starts the process in the background and returns immediately with a PID and tag (HTTP 202) + * @default false + */ + background: boolean; + /** @description Optional user-chosen tag for the background process. Auto-generated if omitted. Only used when background is true. */ + tag?: string; + /** @description Environment variables for the process (background exec only) */ + envs?: { + [key: string]: string; + }; + /** @description Working directory for the process (background exec only) */ + cwd?: string; + }; + BackgroundExecResponse: { + sandbox_id?: string; + cmd?: string; + pid?: number; + tag?: string; + }; + ProcessEntry: { + pid?: number; + tag?: string; + cmd?: string; + args?: string[]; + }; + ProcessListResponse: { + processes?: components["schemas"]["ProcessEntry"][]; + }; + ExecResponse: { + sandbox_id?: string; + cmd?: string; + stdout?: string; + stderr?: string; + exit_code?: number; + duration_ms?: number; + /** + * @description Output encoding. "base64" when stdout/stderr contain binary data. + * @enum {string} + */ + encoding?: "utf-8" | "base64"; + }; + ReadFileRequest: { + /** @description Absolute file path inside the capsule */ + path: string; + }; + ListDirRequest: { + /** @description Directory path inside the capsule */ + path: string; + /** + * @description Recursion depth (0 = non-recursive, 1 = immediate children) + * @default 1 + */ + depth: number; + }; + ListDirResponse: { + entries?: components["schemas"]["FileEntry"][]; + }; + FileEntry: { + name?: string; + path?: string; + /** @enum {string} */ + type?: "file" | "directory" | "symlink"; + /** Format: int64 */ + size?: number; + mode?: number; + /** @description Human-readable permissions (e.g. "-rwxr-xr-x") */ + permissions?: string; + owner?: string; + group?: string; + /** + * Format: int64 + * @description Unix timestamp (seconds) + */ + modified_at?: number; + symlink_target?: string | null; + }; + MakeDirRequest: { + /** @description Directory path to create inside the capsule */ + path: string; + }; + MakeDirResponse: { + entry?: components["schemas"]["FileEntry"]; + }; + RemoveRequest: { + /** @description Path to remove inside the capsule */ + path: string; + }; + CreateHostRequest: { + /** + * @description Host type. Regular hosts are shared; BYOC hosts belong to a team. + * @enum {string} + */ + type: "regular" | "byoc"; + /** @description Required for BYOC hosts. */ + team_id?: string; + /** @description Cloud provider (e.g. aws, gcp, hetzner, bare-metal). */ + provider?: string; + /** @description Availability zone (e.g. us-east, eu-west). */ + availability_zone?: string; + }; + CreateHostResponse: { + host?: components["schemas"]["Host"]; + /** @description One-time registration token for the host agent. Expires in 1 hour. */ + registration_token?: string; + }; + RegisterHostRequest: { + /** @description One-time registration token from POST /v1/hosts. */ + token: string; + /** @description CPU architecture (e.g. x86_64, aarch64). */ + arch?: string; + cpu_cores?: number; + memory_mb?: number; + disk_gb?: number; + /** @description Host agent address (ip:port). */ + address: string; + }; + RegisterHostResponse: { + host?: components["schemas"]["Host"]; + /** @description Host JWT for X-Host-Token header. Valid for 7 days. */ + token?: string; + /** @description Refresh token for obtaining new JWTs. Valid for 60 days; rotated on each use. */ + refresh_token?: string; + }; + Host: { + id?: string; + /** @enum {string} */ + type?: "regular" | "byoc"; + team_id?: string | null; + provider?: string | null; + availability_zone?: string | null; + arch?: string | null; + cpu_cores?: number | null; + memory_mb?: number | null; + disk_gb?: number | null; + address?: string | null; + /** @enum {string} */ + status?: "pending" | "online" | "offline" | "draining" | "unreachable"; + /** Format: date-time */ + last_heartbeat_at?: string | null; + created_by?: string; + /** Format: date-time */ + created_at?: string; + /** Format: date-time */ + updated_at?: string; + }; + RefreshHostTokenRequest: { + /** @description Refresh token obtained from registration or a previous refresh. */ + refresh_token: string; + }; + RefreshHostTokenResponse: { + host?: components["schemas"]["Host"]; + /** @description New host JWT. Valid for 7 days. */ + token?: string; + /** @description New refresh token. Valid for 60 days; old token is revoked. */ + refresh_token?: string; + }; + HostDeletePreview: { + host?: components["schemas"]["Host"]; + /** @description IDs of capsulees that would be destroyed on force-delete. */ + sandbox_ids?: string[]; + }; + HostHasCapsulesError: { + error?: { + /** @example host_has_sandboxes */ + code?: string; + message?: string; + /** @description IDs of active capsulees blocking deletion. */ + sandbox_ids?: string[]; + }; + }; + AddTagRequest: { + tag: string; + }; + UserSearchResult: { + user_id?: string; + email?: string; + }; + Team: { + id?: string; + name?: string; + /** @description Immutable 12-char hex slug (e.g. a1b2c3-d1e2f3) */ + slug?: string; + /** Format: date-time */ + created_at?: string; + }; + TeamWithRole: components["schemas"]["Team"] & { + /** @enum {string} */ + role?: "owner" | "admin" | "member"; + }; + TeamMember: { + user_id?: string; + email?: string; + /** @enum {string} */ + role?: "owner" | "admin" | "member"; + /** Format: date-time */ + joined_at?: string; + }; + TeamDetail: { + team?: components["schemas"]["Team"]; + members?: components["schemas"]["TeamMember"][]; + }; + CapsuleMetrics: { + sandbox_id?: string; + /** @enum {string} */ + range?: "5m" | "10m" | "1h" | "2h" | "6h" | "12h" | "24h"; + points?: components["schemas"]["MetricPoint"][]; + }; + MetricPoint: { + /** Format: int64 */ + timestamp_unix?: number; + /** + * Format: double + * @description CPU utilization percentage (0-100), normalized to vCPU count + */ + cpu_pct?: number; + /** + * Format: int64 + * @description Resident memory in bytes (VmRSS of Firecracker process) + */ + mem_bytes?: number; + /** + * Format: int64 + * @description Allocated disk bytes for the CoW sparse file + */ + disk_bytes?: number; + }; + CreateChannelRequest: { + /** @description Unique channel name within the team. */ + name: string; + /** @enum {string} */ + provider: "discord" | "slack" | "teams" | "googlechat" | "telegram" | "matrix" | "webhook"; + /** @description Provider-specific configuration fields. Discord/Slack/Teams/Google Chat: {"webhook_url": "..."}. Telegram: {"bot_token": "...", "chat_id": "..."}. Matrix: {"homeserver_url": "...", "access_token": "...", "room_id": "..."}. Webhook: {"url": "...", "secret": "..."} (secret is auto-generated if omitted). */ + config: { + [key: string]: string; + }; + events: ("capsule.created" | "capsule.running" | "capsule.paused" | "capsule.destroyed" | "template.snapshot.created" | "template.snapshot.deleted" | "host.up" | "host.down")[]; + }; + TestChannelRequest: { + /** @enum {string} */ + provider: "discord" | "slack" | "teams" | "googlechat" | "telegram" | "matrix" | "webhook"; + /** @description Provider-specific configuration fields (same as CreateChannelRequest.config). */ + config: { + [key: string]: string; + }; + }; + RotateConfigRequest: { + /** @description New provider configuration fields. Must include all required fields for the channel's provider. Replaces the existing config entirely. */ + config: { + [key: string]: string; + }; + }; + UpdateChannelRequest: { + name: string; + events: ("capsule.created" | "capsule.running" | "capsule.paused" | "capsule.destroyed" | "template.snapshot.created" | "template.snapshot.deleted" | "host.up" | "host.down")[]; + }; + ChannelResponse: { + id?: string; + team_id?: string; + name?: string; + /** @enum {string} */ + provider?: "discord" | "slack" | "teams" | "googlechat" | "telegram" | "matrix" | "webhook"; + events?: string[]; + /** Format: date-time */ + created_at?: string; + /** Format: date-time */ + updated_at?: string; + /** @description Webhook secret. Only returned on creation, never again. */ + secret?: string | null; + }; + MeResponse: { + name?: string; + /** Format: email */ + email?: string; + /** @description Whether the user has a password set (false for OAuth-only accounts) */ + has_password?: boolean; + /** @description List of linked OAuth provider names (e.g. ["github"]) */ + providers?: string[]; + }; + ChangePasswordRequest: { + /** @description Required when changing an existing password */ + current_password?: string; + new_password: string; + /** @description Required when adding a password to an OAuth-only account (must match new_password) */ + confirm_password?: string; + }; + Error: { + error?: { + code?: string; + message?: string; + }; + }; + }; + responses: { + /** @description Invalid request */ + BadRequest: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + signup: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SignupRequest"]; + }; + }; + responses: { + /** @description Account created, activation email sent */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SignupResponse"]; + }; + }; + /** @description Invalid request (bad email, short password) */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Email already registered or signup cooldown active */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + activate: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + token: string; + }; + }; + }; + responses: { + /** @description Account activated, JWT issued */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AuthResponse"]; + }; + }; + /** @description Invalid or expired token */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + switchTeam: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + team_id: string; + }; + }; + }; + responses: { + /** @description New JWT issued for the target team */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AuthResponse"]; + }; + }; + /** @description Not a member of this team */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Team not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + login: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["LoginRequest"]; + }; + }; + responses: { + /** @description Login successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AuthResponse"]; + }; + }; + /** @description Invalid credentials */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + oauthRedirect: { + parameters: { + query?: never; + header?: never; + path: { + /** @description OAuth provider name */ + provider: "github"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Redirect to provider authorization URL */ + 302: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Provider not found or not configured */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + oauthCallback: { + parameters: { + query?: { + /** @description Authorization code from the OAuth provider */ + code?: string; + /** @description CSRF state parameter (must match the cookie) */ + state?: string; + }; + header?: never; + path: { + /** @description OAuth provider name */ + provider: "github"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Redirect to frontend with token or error */ + 302: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + getMe: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description User profile */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MeResponse"]; + }; + }; + }; + }; + deleteAccount: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description Must match the user's email address (case-insensitive) */ + confirmation: string; + }; + }; + }; + responses: { + /** @description Account scheduled for deletion */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Confirmation does not match email */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description User owns teams with other members */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + updateName: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + }; + }; + }; + responses: { + /** @description Name updated, new JWT issued */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AuthResponse"]; + }; + }; + /** @description Invalid name */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + changePassword: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ChangePasswordRequest"]; + }; + }; + responses: { + /** @description Password updated */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Invalid request (short password, mismatch, etc.) */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Current password is incorrect */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + requestPasswordReset: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** Format: email */ + email: string; + }; + }; + }; + responses: { + /** @description Request accepted (email sent if account exists) */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + confirmPasswordReset: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description Raw reset token from the email link */ + token: string; + new_password: string; + }; + }; + }; + responses: { + /** @description Password reset successful */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Invalid or expired token, or password too short */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + connectProvider: { + parameters: { + query?: never; + header?: never; + path: { + /** @description OAuth provider name */ + provider: "github"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Authorization URL */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** Format: uri */ + auth_url?: string; + }; + }; + }; + /** @description Provider not found or not configured */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + disconnectProvider: { + parameters: { + query?: never; + header?: never; + path: { + /** @description OAuth provider name */ + provider: "github"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Provider disconnected */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Cannot disconnect last login method */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Provider not connected */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listAPIKeys: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of API keys (plaintext keys are never returned) */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["APIKeyResponse"][]; + }; + }; + }; + }; + createAPIKey: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateAPIKeyRequest"]; + }; + }; + responses: { + /** @description API key created (plaintext key only shown once) */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["APIKeyResponse"]; + }; + }; + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + deleteAPIKey: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description API key deleted */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + searchUsers: { + parameters: { + query: { + /** @description Email prefix (must contain "@", e.g. "alice@") */ + email: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Matching users */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UserSearchResult"][]; + }; + }; + /** @description Prefix does not contain "@" */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listTeams: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Teams the user belongs to, each with their role */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TeamWithRole"][]; + }; + }; + }; + }; + createTeam: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description 1-128 chars; A-Z a-z 0-9 space _ */ + name: string; + }; + }; + }; + responses: { + /** @description Team created (caller is owner) */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TeamWithRole"]; + }; + }; + /** @description Invalid team name */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getTeam: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Team ID (must match the JWT's team_id) */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Team details with members */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TeamDetail"]; + }; + }; + /** @description JWT team does not match requested team */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Team not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + deleteTeam: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Team ID (must match the JWT's team_id) */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Team deleted */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Caller is not the owner */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + renameTeam: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Team ID (must match the JWT's team_id) */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name: string; + }; + }; + }; + responses: { + /** @description Renamed */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Invalid team name */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Insufficient role */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listTeamMembers: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Members with roles */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TeamMember"][]; + }; + }; + }; + }; + addTeamMember: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** Format: email */ + email: string; + }; + }; + }; + responses: { + /** @description Member added */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TeamMember"]; + }; + }; + /** @description User is already a member */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Insufficient role */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description No account with that email */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + removeTeamMember: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + /** @description Target user ID */ + uid: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Member removed */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Insufficient role or attempt to remove owner */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description User is not a member */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + updateMemberRole: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + /** @description Target user ID */ + uid: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + role: "admin" | "member"; + }; + }; + }; + responses: { + /** @description Role updated */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Insufficient role or attempt to modify owner */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description User is not a member */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + leaveTeam: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Left the team */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Owner cannot leave */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listCapsules: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of capsulees */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Capsule"][]; + }; + }; + }; + }; + createCapsule: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateCapsuleRequest"]; + }; + }; + responses: { + /** @description Capsule created */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Capsule"]; + }; + }; + /** @description Host agent error */ + 502: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getCapsuleStats: { + parameters: { + query?: { + /** @description Time window for the time-series data. */ + range?: "5m" | "1h" | "6h" | "24h" | "30d"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Capsule stats for the team */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CapsuleStats"]; + }; + }; + 400: components["responses"]["BadRequest"]; + }; + }; + getCapsuleUsage: { + parameters: { + query?: { + /** @description Start date (YYYY-MM-DD). Defaults to 30 days ago. */ + from?: string; + /** @description End date (YYYY-MM-DD). Defaults to today. */ + to?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Daily usage data for the team */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UsageResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + }; + }; + getCapsule: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Capsule details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Capsule"]; + }; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + destroyCapsule: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Capsule destroyed */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + execCommand: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExecRequest"]; + }; + }; + responses: { + /** @description Command output (foreground exec) */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ExecResponse"]; + }; + }; + /** @description Background process started */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BackgroundExecResponse"]; + }; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listProcesses: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Process list */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProcessListResponse"]; + }; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + killProcess: { + parameters: { + query?: { + /** @description Signal to send (SIGKILL or SIGTERM, default SIGKILL) */ + signal?: "SIGKILL" | "SIGTERM"; + }; + header?: never; + path: { + id: string; + /** @description Process PID (numeric) or tag (string) */ + selector: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Process killed */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Capsule or process not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + connectProcess: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + /** @description Process PID (numeric) or tag (string) */ + selector: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description WebSocket upgrade */ + 101: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + pingCapsule: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Ping acknowledged, inactivity timer reset */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getCapsuleMetrics: { + parameters: { + query?: { + /** @description Time range filter to query */ + range?: "5m" | "10m" | "1h" | "2h" | "6h" | "12h" | "24h"; + }; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Metrics retrieved */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CapsuleMetrics"]; + }; + }; + /** @description Invalid range parameter */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not found or metrics not available */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + pauseCapsule: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Capsule paused (snapshot taken, resources released) */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Capsule"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + resumeCapsule: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Capsule resumed (new VM booted from snapshot) */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Capsule"]; + }; + }; + /** @description Capsule not paused */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listSnapshots: { + parameters: { + query?: { + /** @description Filter by template type. */ + type?: "base" | "snapshot"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of templates */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Template"][]; + }; + }; + }; + }; + createSnapshot: { + parameters: { + query?: { + /** @description Set to "true" to overwrite an existing snapshot with the same name. */ + overwrite?: "true"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateSnapshotRequest"]; + }; + }; + responses: { + /** @description Snapshot created */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Template"]; + }; + }; + /** @description Name already exists or capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + deleteSnapshot: { + parameters: { + query?: never; + header?: never; + path: { + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Snapshot deleted */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Template not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + uploadFile: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "multipart/form-data": { + /** @description Absolute destination path inside the capsule */ + path: string; + /** + * Format: binary + * @description File content + */ + file: string; + }; + }; + }; + responses: { + /** @description File uploaded */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description File too large */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + downloadFile: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ReadFileRequest"]; + }; + }; + responses: { + /** @description File content */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/octet-stream": string; + }; + }; + /** @description Capsule or file not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listDir: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ListDirRequest"]; + }; + }; + responses: { + /** @description Directory listing */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListDirResponse"]; + }; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + makeDir: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MakeDirRequest"]; + }; + }; + responses: { + /** @description Directory created */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MakeDirResponse"]; + }; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + removePath: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RemoveRequest"]; + }; + }; + responses: { + /** @description File or directory removed */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + execStream: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description WebSocket upgrade */ + 101: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + ptySession: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description WebSocket upgrade */ + 101: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + streamUploadFile: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "multipart/form-data": { + /** @description Absolute destination path inside the capsule */ + path: string; + /** + * Format: binary + * @description File content + */ + file: string; + }; + }; + }; + responses: { + /** @description File uploaded */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Capsule not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + streamDownloadFile: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ReadFileRequest"]; + }; + }; + responses: { + /** @description File content streamed in chunks */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/octet-stream": string; + }; + }; + /** @description Capsule or file not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Capsule not running */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listHosts: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of hosts */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Host"][]; + }; + }; + }; + }; + createHost: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateHostRequest"]; + }; + }; + responses: { + /** @description Host created with registration token */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CreateHostResponse"]; + }; + }; + /** @description Invalid request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Insufficient permissions */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getHost: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Host details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Host"]; + }; + }; + /** @description Host not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + deleteHost: { + parameters: { + query?: { + /** @description If true, destroy all capsulees on the host before deleting. */ + force?: boolean; + }; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Host deleted */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Insufficient permissions */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Host has active capsulees (only when force is not set) */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HostHasCapsulesError"]; + }; + }; + }; + }; + regenerateHostToken: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description New registration token issued */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CreateHostResponse"]; + }; + }; + /** @description Insufficient permissions */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Host is not in pending status */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + registerHost: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RegisterHostRequest"]; + }; + }; + responses: { + /** @description Host registered, JWT returned */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RegisterHostResponse"]; + }; + }; + /** @description Invalid request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Invalid or expired registration token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + hostHeartbeat: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Heartbeat recorded */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Invalid or missing host token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Host ID mismatch */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + refreshHostToken: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RefreshHostTokenRequest"]; + }; + }; + responses: { + /** @description New JWT and rotated refresh token */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RefreshHostTokenResponse"]; + }; + }; + /** @description Invalid, expired, or revoked refresh token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getHostDeletePreview: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Deletion preview */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HostDeletePreview"]; + }; + }; + /** @description Insufficient permissions */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Host not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listHostTags: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of tags */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": string[]; + }; + }; + }; + }; + addHostTag: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AddTagRequest"]; + }; + }; + responses: { + /** @description Tag added */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Host not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + removeHostTag: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + tag: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Tag removed */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Host not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listChannels: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Channels list */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ChannelResponse"][]; + }; + }; + }; + }; + createChannel: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateChannelRequest"]; + }; + }; + responses: { + /** @description Channel created */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ChannelResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + }; + }; + testChannel: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TestChannelRequest"]; + }; + }; + responses: { + /** @description Test notification sent successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example ok */ + status?: string; + }; + }; + }; + 400: components["responses"]["BadRequest"]; + }; + }; + getChannel: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Channel details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ChannelResponse"]; + }; + }; + /** @description Channel not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + deleteChannel: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Channel deleted */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + updateChannel: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateChannelRequest"]; + }; + }; + responses: { + /** @description Channel updated */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ChannelResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + /** @description Channel not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + rotateChannelConfig: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RotateConfigRequest"]; + }; + }; + responses: { + /** @description Config rotated */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ChannelResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + /** @description Channel not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1576950 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,45 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + // "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + "ignoreDeprecations": "6.0" + } +} diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..3206ba6 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: { resolve: true }, + outDir: "dist", + clean: true, + sourcemap: true, + minify: false, +});