From 3cdc08dabd7e47090add9151607d87340e39fe5c Mon Sep 17 00:00:00 2001 From: Morgan Dean Date: Thu, 12 Jun 2025 19:55:58 -0400 Subject: [PATCH] Initial computer typescript library creation --- libs/computer/{ => python}/README.md | 0 .../{ => python}/computer/__init__.py | 0 .../{ => python}/computer/computer.py | 0 .../{ => python}/computer/diorama_computer.py | 0 .../computer/{ => python}/computer/helpers.py | 0 .../computer/interface/__init__.py | 0 .../{ => python}/computer/interface/base.py | 0 .../computer/interface/factory.py | 0 .../{ => python}/computer/interface/linux.py | 0 .../{ => python}/computer/interface/macos.py | 0 .../{ => python}/computer/interface/models.py | 0 libs/computer/{ => python}/computer/logger.py | 0 libs/computer/{ => python}/computer/models.py | 0 .../computer/providers/__init__.py | 0 .../{ => python}/computer/providers/base.py | 0 .../computer/providers/cloud/__init__.py | 0 .../computer/providers/cloud/provider.py | 0 .../computer/providers/factory.py | 0 .../computer/providers/lume/__init__.py | 0 .../computer/providers/lume/provider.py | 0 .../computer/providers/lume_api.py | 0 .../computer/providers/lumier/__init__.py | 0 .../computer/providers/lumier/provider.py | 0 .../{ => python}/computer/telemetry.py | 0 .../{ => python}/computer/ui/__init__.py | 0 .../computer/ui/gradio/__init__.py | 0 .../{ => python}/computer/ui/gradio/app.py | 0 libs/computer/{ => python}/computer/utils.py | 0 libs/computer/{ => python}/poetry.toml | 0 libs/computer/{ => python}/pyproject.toml | 0 libs/computer/typescript/.editorconfig | 6 + libs/computer/typescript/.gitattributes | 1 + libs/computer/typescript/.github/FUNDING.yml | 1 + .../typescript/.github/renovate.json5 | 4 + .../typescript/.github/workflows/release.yml | 26 + .../.github/workflows/unit-test.yml | 38 + libs/computer/typescript/.gitignore | 6 + libs/computer/typescript/.nvmrc | 1 + libs/computer/typescript/LICENSE | 21 + libs/computer/typescript/README.md | 119 + libs/computer/typescript/biome.json | 86 + libs/computer/typescript/package.json | 54 + libs/computer/typescript/pnpm-lock.yaml | 2000 +++++++++++++++++ libs/computer/typescript/pnpm-workspace.yaml | 3 + .../typescript/src/computer/computer.ts | 644 ++++++ .../computer/typescript/src/computer/index.ts | 2 + libs/computer/typescript/src/helpers.ts | 76 + libs/computer/typescript/src/index.ts | 28 + .../computer/typescript/src/interface/base.ts | 41 + .../typescript/src/interface/factory.ts | 56 + .../typescript/src/interface/index.ts | 5 + .../typescript/src/interface/linux.ts | 47 + .../typescript/src/interface/macos.ts | 47 + .../typescript/src/interface/models.ts | 96 + libs/computer/typescript/src/logger.ts | 50 + libs/computer/typescript/src/models.ts | 21 + .../computer/typescript/src/providers/base.ts | 140 ++ .../typescript/src/providers/cloud/index.ts | 5 + .../src/providers/cloud/provider.ts | 68 + .../typescript/src/providers/factory.ts | 150 ++ .../typescript/src/providers/index.ts | 12 + .../typescript/src/providers/lume/index.ts | 16 + .../typescript/src/providers/lume/provider.ts | 182 ++ .../typescript/src/providers/lume_api.ts | 265 +++ .../typescript/src/providers/lumier/index.ts | 16 + .../src/providers/lumier/provider.ts | 401 ++++ libs/computer/typescript/src/telemetry.ts | 115 + libs/computer/typescript/src/utils.ts | 118 + libs/computer/typescript/tsconfig.json | 22 + libs/computer/typescript/tsdown.config.ts | 10 + libs/computer/typescript/vitest.config.ts | 3 + 71 files changed, 5002 insertions(+) rename libs/computer/{ => python}/README.md (100%) rename libs/computer/{ => python}/computer/__init__.py (100%) rename libs/computer/{ => python}/computer/computer.py (100%) rename libs/computer/{ => python}/computer/diorama_computer.py (100%) rename libs/computer/{ => python}/computer/helpers.py (100%) rename libs/computer/{ => python}/computer/interface/__init__.py (100%) rename libs/computer/{ => python}/computer/interface/base.py (100%) rename libs/computer/{ => python}/computer/interface/factory.py (100%) rename libs/computer/{ => python}/computer/interface/linux.py (100%) rename libs/computer/{ => python}/computer/interface/macos.py (100%) rename libs/computer/{ => python}/computer/interface/models.py (100%) rename libs/computer/{ => python}/computer/logger.py (100%) rename libs/computer/{ => python}/computer/models.py (100%) rename libs/computer/{ => python}/computer/providers/__init__.py (100%) rename libs/computer/{ => python}/computer/providers/base.py (100%) rename libs/computer/{ => python}/computer/providers/cloud/__init__.py (100%) rename libs/computer/{ => python}/computer/providers/cloud/provider.py (100%) rename libs/computer/{ => python}/computer/providers/factory.py (100%) rename libs/computer/{ => python}/computer/providers/lume/__init__.py (100%) rename libs/computer/{ => python}/computer/providers/lume/provider.py (100%) rename libs/computer/{ => python}/computer/providers/lume_api.py (100%) rename libs/computer/{ => python}/computer/providers/lumier/__init__.py (100%) rename libs/computer/{ => python}/computer/providers/lumier/provider.py (100%) rename libs/computer/{ => python}/computer/telemetry.py (100%) rename libs/computer/{ => python}/computer/ui/__init__.py (100%) rename libs/computer/{ => python}/computer/ui/gradio/__init__.py (100%) rename libs/computer/{ => python}/computer/ui/gradio/app.py (100%) rename libs/computer/{ => python}/computer/utils.py (100%) rename libs/computer/{ => python}/poetry.toml (100%) rename libs/computer/{ => python}/pyproject.toml (100%) create mode 100644 libs/computer/typescript/.editorconfig create mode 100644 libs/computer/typescript/.gitattributes create mode 100644 libs/computer/typescript/.github/FUNDING.yml create mode 100644 libs/computer/typescript/.github/renovate.json5 create mode 100644 libs/computer/typescript/.github/workflows/release.yml create mode 100644 libs/computer/typescript/.github/workflows/unit-test.yml create mode 100644 libs/computer/typescript/.gitignore create mode 100644 libs/computer/typescript/.nvmrc create mode 100644 libs/computer/typescript/LICENSE create mode 100644 libs/computer/typescript/README.md create mode 100644 libs/computer/typescript/biome.json create mode 100644 libs/computer/typescript/package.json create mode 100644 libs/computer/typescript/pnpm-lock.yaml create mode 100644 libs/computer/typescript/pnpm-workspace.yaml create mode 100644 libs/computer/typescript/src/computer/computer.ts create mode 100644 libs/computer/typescript/src/computer/index.ts create mode 100644 libs/computer/typescript/src/helpers.ts create mode 100644 libs/computer/typescript/src/index.ts create mode 100644 libs/computer/typescript/src/interface/base.ts create mode 100644 libs/computer/typescript/src/interface/factory.ts create mode 100644 libs/computer/typescript/src/interface/index.ts create mode 100644 libs/computer/typescript/src/interface/linux.ts create mode 100644 libs/computer/typescript/src/interface/macos.ts create mode 100644 libs/computer/typescript/src/interface/models.ts create mode 100644 libs/computer/typescript/src/logger.ts create mode 100644 libs/computer/typescript/src/models.ts create mode 100644 libs/computer/typescript/src/providers/base.ts create mode 100644 libs/computer/typescript/src/providers/cloud/index.ts create mode 100644 libs/computer/typescript/src/providers/cloud/provider.ts create mode 100644 libs/computer/typescript/src/providers/factory.ts create mode 100644 libs/computer/typescript/src/providers/index.ts create mode 100644 libs/computer/typescript/src/providers/lume/index.ts create mode 100644 libs/computer/typescript/src/providers/lume/provider.ts create mode 100644 libs/computer/typescript/src/providers/lume_api.ts create mode 100644 libs/computer/typescript/src/providers/lumier/index.ts create mode 100644 libs/computer/typescript/src/providers/lumier/provider.ts create mode 100644 libs/computer/typescript/src/telemetry.ts create mode 100644 libs/computer/typescript/src/utils.ts create mode 100644 libs/computer/typescript/tsconfig.json create mode 100644 libs/computer/typescript/tsdown.config.ts create mode 100644 libs/computer/typescript/vitest.config.ts diff --git a/libs/computer/README.md b/libs/computer/python/README.md similarity index 100% rename from libs/computer/README.md rename to libs/computer/python/README.md diff --git a/libs/computer/computer/__init__.py b/libs/computer/python/computer/__init__.py similarity index 100% rename from libs/computer/computer/__init__.py rename to libs/computer/python/computer/__init__.py diff --git a/libs/computer/computer/computer.py b/libs/computer/python/computer/computer.py similarity index 100% rename from libs/computer/computer/computer.py rename to libs/computer/python/computer/computer.py diff --git a/libs/computer/computer/diorama_computer.py b/libs/computer/python/computer/diorama_computer.py similarity index 100% rename from libs/computer/computer/diorama_computer.py rename to libs/computer/python/computer/diorama_computer.py diff --git a/libs/computer/computer/helpers.py b/libs/computer/python/computer/helpers.py similarity index 100% rename from libs/computer/computer/helpers.py rename to libs/computer/python/computer/helpers.py diff --git a/libs/computer/computer/interface/__init__.py b/libs/computer/python/computer/interface/__init__.py similarity index 100% rename from libs/computer/computer/interface/__init__.py rename to libs/computer/python/computer/interface/__init__.py diff --git a/libs/computer/computer/interface/base.py b/libs/computer/python/computer/interface/base.py similarity index 100% rename from libs/computer/computer/interface/base.py rename to libs/computer/python/computer/interface/base.py diff --git a/libs/computer/computer/interface/factory.py b/libs/computer/python/computer/interface/factory.py similarity index 100% rename from libs/computer/computer/interface/factory.py rename to libs/computer/python/computer/interface/factory.py diff --git a/libs/computer/computer/interface/linux.py b/libs/computer/python/computer/interface/linux.py similarity index 100% rename from libs/computer/computer/interface/linux.py rename to libs/computer/python/computer/interface/linux.py diff --git a/libs/computer/computer/interface/macos.py b/libs/computer/python/computer/interface/macos.py similarity index 100% rename from libs/computer/computer/interface/macos.py rename to libs/computer/python/computer/interface/macos.py diff --git a/libs/computer/computer/interface/models.py b/libs/computer/python/computer/interface/models.py similarity index 100% rename from libs/computer/computer/interface/models.py rename to libs/computer/python/computer/interface/models.py diff --git a/libs/computer/computer/logger.py b/libs/computer/python/computer/logger.py similarity index 100% rename from libs/computer/computer/logger.py rename to libs/computer/python/computer/logger.py diff --git a/libs/computer/computer/models.py b/libs/computer/python/computer/models.py similarity index 100% rename from libs/computer/computer/models.py rename to libs/computer/python/computer/models.py diff --git a/libs/computer/computer/providers/__init__.py b/libs/computer/python/computer/providers/__init__.py similarity index 100% rename from libs/computer/computer/providers/__init__.py rename to libs/computer/python/computer/providers/__init__.py diff --git a/libs/computer/computer/providers/base.py b/libs/computer/python/computer/providers/base.py similarity index 100% rename from libs/computer/computer/providers/base.py rename to libs/computer/python/computer/providers/base.py diff --git a/libs/computer/computer/providers/cloud/__init__.py b/libs/computer/python/computer/providers/cloud/__init__.py similarity index 100% rename from libs/computer/computer/providers/cloud/__init__.py rename to libs/computer/python/computer/providers/cloud/__init__.py diff --git a/libs/computer/computer/providers/cloud/provider.py b/libs/computer/python/computer/providers/cloud/provider.py similarity index 100% rename from libs/computer/computer/providers/cloud/provider.py rename to libs/computer/python/computer/providers/cloud/provider.py diff --git a/libs/computer/computer/providers/factory.py b/libs/computer/python/computer/providers/factory.py similarity index 100% rename from libs/computer/computer/providers/factory.py rename to libs/computer/python/computer/providers/factory.py diff --git a/libs/computer/computer/providers/lume/__init__.py b/libs/computer/python/computer/providers/lume/__init__.py similarity index 100% rename from libs/computer/computer/providers/lume/__init__.py rename to libs/computer/python/computer/providers/lume/__init__.py diff --git a/libs/computer/computer/providers/lume/provider.py b/libs/computer/python/computer/providers/lume/provider.py similarity index 100% rename from libs/computer/computer/providers/lume/provider.py rename to libs/computer/python/computer/providers/lume/provider.py diff --git a/libs/computer/computer/providers/lume_api.py b/libs/computer/python/computer/providers/lume_api.py similarity index 100% rename from libs/computer/computer/providers/lume_api.py rename to libs/computer/python/computer/providers/lume_api.py diff --git a/libs/computer/computer/providers/lumier/__init__.py b/libs/computer/python/computer/providers/lumier/__init__.py similarity index 100% rename from libs/computer/computer/providers/lumier/__init__.py rename to libs/computer/python/computer/providers/lumier/__init__.py diff --git a/libs/computer/computer/providers/lumier/provider.py b/libs/computer/python/computer/providers/lumier/provider.py similarity index 100% rename from libs/computer/computer/providers/lumier/provider.py rename to libs/computer/python/computer/providers/lumier/provider.py diff --git a/libs/computer/computer/telemetry.py b/libs/computer/python/computer/telemetry.py similarity index 100% rename from libs/computer/computer/telemetry.py rename to libs/computer/python/computer/telemetry.py diff --git a/libs/computer/computer/ui/__init__.py b/libs/computer/python/computer/ui/__init__.py similarity index 100% rename from libs/computer/computer/ui/__init__.py rename to libs/computer/python/computer/ui/__init__.py diff --git a/libs/computer/computer/ui/gradio/__init__.py b/libs/computer/python/computer/ui/gradio/__init__.py similarity index 100% rename from libs/computer/computer/ui/gradio/__init__.py rename to libs/computer/python/computer/ui/gradio/__init__.py diff --git a/libs/computer/computer/ui/gradio/app.py b/libs/computer/python/computer/ui/gradio/app.py similarity index 100% rename from libs/computer/computer/ui/gradio/app.py rename to libs/computer/python/computer/ui/gradio/app.py diff --git a/libs/computer/computer/utils.py b/libs/computer/python/computer/utils.py similarity index 100% rename from libs/computer/computer/utils.py rename to libs/computer/python/computer/utils.py diff --git a/libs/computer/poetry.toml b/libs/computer/python/poetry.toml similarity index 100% rename from libs/computer/poetry.toml rename to libs/computer/python/poetry.toml diff --git a/libs/computer/pyproject.toml b/libs/computer/python/pyproject.toml similarity index 100% rename from libs/computer/pyproject.toml rename to libs/computer/python/pyproject.toml diff --git a/libs/computer/typescript/.editorconfig b/libs/computer/typescript/.editorconfig new file mode 100644 index 00000000..7095e7fb --- /dev/null +++ b/libs/computer/typescript/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +indent_size = 2 +end_of_line = lf +insert_final_newline = true diff --git a/libs/computer/typescript/.gitattributes b/libs/computer/typescript/.gitattributes new file mode 100644 index 00000000..6313b56c --- /dev/null +++ b/libs/computer/typescript/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/libs/computer/typescript/.github/FUNDING.yml b/libs/computer/typescript/.github/FUNDING.yml new file mode 100644 index 00000000..c4630bc2 --- /dev/null +++ b/libs/computer/typescript/.github/FUNDING.yml @@ -0,0 +1 @@ +github: sxzz diff --git a/libs/computer/typescript/.github/renovate.json5 b/libs/computer/typescript/.github/renovate.json5 new file mode 100644 index 00000000..ac1c0dc9 --- /dev/null +++ b/libs/computer/typescript/.github/renovate.json5 @@ -0,0 +1,4 @@ +{ + extends: ['github>sxzz/renovate-config'], + automerge: true, +} diff --git a/libs/computer/typescript/.github/workflows/release.yml b/libs/computer/typescript/.github/workflows/release.yml new file mode 100644 index 00000000..e5b7a4c3 --- /dev/null +++ b/libs/computer/typescript/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set node + uses: actions/setup-node@v4 + with: + node-version: lts/* + + - run: npx changelogithub + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/libs/computer/typescript/.github/workflows/unit-test.yml b/libs/computer/typescript/.github/workflows/unit-test.yml new file mode 100644 index 00000000..dc3418c5 --- /dev/null +++ b/libs/computer/typescript/.github/workflows/unit-test.yml @@ -0,0 +1,38 @@ +name: Unit Test + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4.1.0 + + - name: Set node LTS + uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: pnpm + + - name: Install + run: pnpm install + + - name: Build + run: pnpm run build + + - name: Lint + run: pnpm run lint + + - name: Typecheck + run: pnpm run typecheck + + - name: Test + run: pnpm run test diff --git a/libs/computer/typescript/.gitignore b/libs/computer/typescript/.gitignore new file mode 100644 index 00000000..e79f2036 --- /dev/null +++ b/libs/computer/typescript/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist + +*.log +.DS_Store +.eslintcache diff --git a/libs/computer/typescript/.nvmrc b/libs/computer/typescript/.nvmrc new file mode 100644 index 00000000..dc864a05 --- /dev/null +++ b/libs/computer/typescript/.nvmrc @@ -0,0 +1 @@ +v24.2.0 diff --git a/libs/computer/typescript/LICENSE b/libs/computer/typescript/LICENSE new file mode 100644 index 00000000..7ff04379 --- /dev/null +++ b/libs/computer/typescript/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2025 C/UA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/computer/typescript/README.md b/libs/computer/typescript/README.md new file mode 100644 index 00000000..5ed0f1ac --- /dev/null +++ b/libs/computer/typescript/README.md @@ -0,0 +1,119 @@ +# C/UA Computer TypeScript Library + +The TypeScript library for C/UA Computer - a powerful computer control and automation library. + +## Overview + +This library is a TypeScript port of the Python computer library, providing the same functionality for controlling virtual machines and computer interfaces. It includes: + +- **Computer Class**: Main class for interacting with computers (virtual or host) +- **VM Providers**: Support for different VM providers (Lume, Lumier, Cloud) +- **Computer Interfaces**: OS-specific interfaces for controlling computers (macOS, Linux, Windows) +- **Utilities**: Helper functions for display parsing, memory parsing, logging, and telemetry + +## Installation + +```bash +npm install @cua/computer +# or +pnpm add @cua/computer +``` + +## Usage + +```typescript +import { Computer } from '@cua/computer'; + +// Create a new computer instance +const computer = new Computer({ + display: '1024x768', + memory: '8GB', + cpu: '4', + osType: 'macos', + image: 'macos-sequoia-cua:latest' +}); + +// Start the computer +await computer.run(); + +// Get the computer interface for interaction +const interface = computer.interface; + +// Take a screenshot +const screenshot = await interface.getScreenshot(); + +// Click at coordinates +await interface.click(500, 300); + +// Type text +await interface.typeText('Hello, world!'); + +// Stop the computer +await computer.stop(); +``` + +## Architecture + +The library is organized into several key modules: + +### Core Components +- `Computer`: Main class that manages VM lifecycle and interfaces +- `ComputerOptions`: Configuration options for computer instances + +### Models +- `Display`: Display configuration (width, height) +- `ComputerConfig`: Internal computer configuration + +### Providers +- `BaseVMProvider`: Abstract base class for VM providers +- `VMProviderFactory`: Factory for creating provider instances +- Provider types: `LUME`, `LUMIER`, `CLOUD` + +### Interfaces +- `BaseComputerInterface`: Abstract base class for OS interfaces +- `InterfaceFactory`: Factory for creating OS-specific interfaces +- Interface models: Key types, mouse buttons, accessibility tree + +### Utilities +- `Logger`: Logging with different verbosity levels +- `helpers`: Default computer management and sandboxed execution +- `utils`: Display/memory parsing, timeout utilities +- `telemetry`: Usage tracking and metrics + +## Development + +- Install dependencies: + +```bash +pnpm install +``` + +- Run the unit tests: + +```bash +pnpm test +``` + +- Build the library: + +```bash +pnpm build +``` + +- Type checking: + +```bash +pnpm typecheck +``` + +## External Dependencies + +- `sharp`: For image processing and screenshot manipulation +- Additional provider-specific packages need to be installed separately: + - `@cua/computer-lume`: For Lume provider support + - `@cua/computer-lumier`: For Lumier provider support + - `@cua/computer-cloud`: For Cloud provider support + +## License + +[MIT](./LICENSE) License 2025 [C/UA](https://github.com/trycua) diff --git a/libs/computer/typescript/biome.json b/libs/computer/typescript/biome.json new file mode 100644 index 00000000..221019d2 --- /dev/null +++ b/libs/computer/typescript/biome.json @@ -0,0 +1,86 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": [ + "build", + "node_modules" + ] + }, + "formatter": { + "enabled": true, + "useEditorconfig": true, + "formatWithErrors": false, + "indentStyle": "space", + "indentWidth": 2, + "lineEnding": "lf", + "lineWidth": 80, + "attributePosition": "auto", + "bracketSpacing": true + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "style": { + "useSelfClosingElements": "warn", + "noUnusedTemplateLiteral": "warn", + "noNonNullAssertion": "off" + }, + "a11y": { + "useMediaCaption": "off", + "useKeyWithClickEvents": "warn", + "useKeyWithMouseEvents": "warn", + "noSvgWithoutTitle": "off", + "useButtonType": "warn", + "noAutofocus": "off" + }, + "suspicious": { + "noArrayIndexKey": "off" + }, + "correctness": { + "noUnusedVariables": "warn", + "noUnusedFunctionParameters": "warn", + "noUnusedImports": "warn" + }, + "complexity": { + "useOptionalChain": "info" + }, + "nursery": { + "useSortedClasses": { + "level": "warn", + "fix": "safe", + "options": { + "attributes": [ + "className" + ], + "functions": [ + "cn" + ] + } + } + } + } + }, + "javascript": { + "formatter": { + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "trailingCommas": "es5", + "semicolons": "always", + "arrowParentheses": "always", + "bracketSameLine": false, + "quoteStyle": "single", + "attributePosition": "auto", + "bracketSpacing": true + } + } +} \ No newline at end of file diff --git a/libs/computer/typescript/package.json b/libs/computer/typescript/package.json new file mode 100644 index 00000000..3727210b --- /dev/null +++ b/libs/computer/typescript/package.json @@ -0,0 +1,54 @@ +{ + "name": "@cua/computer", + "version": "0.0.0", + "packageManager": "pnpm@10.11.0", + "description": "", + "type": "module", + "license": "MIT", + "homepage": "", + "bugs": { + "url": "" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/trycua/cua.git" + }, + "author": "", + "funding": "", + "files": [ + "dist" + ], + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": "./dist/index.js", + "./package.json": "./package.json" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "lint": "biome lint --cache .", + "lint:fix": "biome lint --cache --fix .", + "build": "tsdown", + "dev": "tsdown --watch", + "test": "vitest", + "typecheck": "tsc --noEmit", + "release": "bumpp && pnpm publish", + "prepublishOnly": "pnpm run build" + }, + "dependencies": { + "sharp": "^0.33.0" + }, + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@types/node": "^22.15.17", + "bumpp": "^10.1.0", + "happy-dom": "^17.4.7", + "tsdown": "^0.11.9", + "tsx": "^4.19.4", + "typescript": "^5.8.3", + "vitest": "^3.1.3" + } +} diff --git a/libs/computer/typescript/pnpm-lock.yaml b/libs/computer/typescript/pnpm-lock.yaml new file mode 100644 index 00000000..12d841f9 --- /dev/null +++ b/libs/computer/typescript/pnpm-lock.yaml @@ -0,0 +1,2000 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + sharp: + specifier: ^0.33.0 + version: 0.33.5 + devDependencies: + '@biomejs/biome': + specifier: ^1.9.4 + version: 1.9.4 + '@types/node': + specifier: ^22.15.17 + version: 22.15.31 + bumpp: + specifier: ^10.1.0 + version: 10.1.1 + happy-dom: + specifier: ^17.4.7 + version: 17.6.3 + tsdown: + specifier: ^0.11.9 + version: 0.11.13(typescript@5.8.3) + tsx: + specifier: ^4.19.4 + version: 4.20.2 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + vitest: + specifier: ^3.1.3 + version: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0) + +packages: + + '@babel/generator@7.27.5': + resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.27.5': + resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.27.6': + resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@1.9.4': + resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@1.9.4': + resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@1.9.4': + resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@1.9.4': + resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@1.9.4': + resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@1.9.4': + resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@1.9.4': + resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@1.9.4': + resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@1.9.4': + resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@emnapi/core@1.4.3': + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} + + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + + '@emnapi/wasi-threads@1.0.2': + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} + + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@napi-rs/wasm-runtime@0.2.11': + resolution: {integrity: sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==} + + '@oxc-project/types@0.70.0': + resolution: {integrity: sha512-ngyLUpUjO3dpqygSRQDx7nMx8+BmXbWOU4oIwTJFV2MVIDG7knIZwgdwXlQWLg3C3oxg1lS7ppMtPKqKFb7wzw==} + + '@quansync/fs@0.1.3': + resolution: {integrity: sha512-G0OnZbMWEs5LhDyqy2UL17vGhSVHkQIfVojMtEWVenvj0V5S84VBgy86kJIuNsGDp2p7sTKlpSIpBUWdC35OKg==} + engines: {node: '>=20.0.0'} + + '@rolldown/binding-darwin-arm64@1.0.0-beta.9': + resolution: {integrity: sha512-geUG/FUpm+membLC0NQBb39vVyOfguYZ2oyXc7emr6UjH6TeEECT4b0CPZXKFnELareTiU/Jfl70/eEgNxyQeA==} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.9': + resolution: {integrity: sha512-7wPXDwcOtv2I+pWTL2UNpNAxMAGukgBT90Jz4DCfwaYdGvQncF7J0S7IWrRVsRFhBavxM+65RcueE3VXw5UIbg==} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.9': + resolution: {integrity: sha512-agO5mONTNKVrcIt4SRxw5Ni0FOVV3gaH8dIiNp1A4JeU91b9kw7x+JRuNJAQuM2X3pYqVvA6qh13UTNOsaqM/Q==} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.9': + resolution: {integrity: sha512-dDNDV9p/8WYDriS9HCcbH6y6+JP38o3enj/pMkdkmkxEnZ0ZoHIfQ9RGYWeRYU56NKBCrya4qZBJx49Jk9LRug==} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.9': + resolution: {integrity: sha512-kZKegmHG1ZvfsFIwYU6DeFSxSIcIliXzeznsJHUo9D9/dlVSDi/PUvsRKcuJkQjZoejM6pk8MHN/UfgGdIhPHw==} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.9': + resolution: {integrity: sha512-f+VL8mO31pyMJiJPr2aA1ryYONkP2UqgbwK7fKtKHZIeDd/AoUGn3+ujPqDhuy2NxgcJ5H8NaSvDpG1tJMHh+g==} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.9': + resolution: {integrity: sha512-GiUEZ0WPjX5LouDoC3O8aJa4h6BLCpIvaAboNw5JoRour/3dC6rbtZZ/B5FC3/ySsN3/dFOhAH97ylQxoZJi7A==} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.9': + resolution: {integrity: sha512-AMb0dicw+QHh6RxvWo4BRcuTMgS0cwUejJRMpSyIcHYnKTbj6nUW4HbWNQuDfZiF27l6F5gEwBS+YLUdVzL9vg==} + cpu: [x64] + os: [linux] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.9': + resolution: {integrity: sha512-+pdaiTx7L8bWKvsAuCE0HAxP1ze1WOLoWGCawcrZbMSY10dMh2i82lJiH6tXGXbfYYwsNWhWE2NyG4peFZvRfQ==} + engines: {node: '>=14.21.3'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.9': + resolution: {integrity: sha512-A7kN248viWvb8eZMzQu024TBKGoyoVYBsDG2DtoP8u2pzwoh5yDqUL291u01o4f8uzpUHq8mfwQJmcGChFu8KQ==} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.9': + resolution: {integrity: sha512-DzKN7iEYjAP8AK8F2G2aCej3fk43Y/EQrVrR3gF0XREes56chjQ7bXIhw819jv74BbxGdnpPcslhet/cgt7WRA==} + cpu: [ia32] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.9': + resolution: {integrity: sha512-GMWgTvvbZ8TfBsAiJpoz4SRq3IN3aUMn0rYm8q4I8dcEk4J1uISyfb6ZMzvqW+cvScTWVKWZNqnrmYOKLLUt4w==} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.9': + resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==} + + '@rollup/rollup-android-arm-eabi@4.43.0': + resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.43.0': + resolution: {integrity: sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.43.0': + resolution: {integrity: sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.43.0': + resolution: {integrity: sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.43.0': + resolution: {integrity: sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.43.0': + resolution: {integrity: sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.43.0': + resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.43.0': + resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.43.0': + resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.43.0': + resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.43.0': + resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.43.0': + resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.43.0': + resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.43.0': + resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.43.0': + resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.43.0': + resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.43.0': + resolution: {integrity: sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.43.0': + resolution: {integrity: sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.43.0': + resolution: {integrity: sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.43.0': + resolution: {integrity: sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==} + cpu: [x64] + os: [win32] + + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.15.31': + resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==} + + '@vitest/expect@3.2.3': + resolution: {integrity: sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==} + + '@vitest/mocker@3.2.3': + resolution: {integrity: sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.3': + resolution: {integrity: sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==} + + '@vitest/runner@3.2.3': + resolution: {integrity: sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==} + + '@vitest/snapshot@3.2.3': + resolution: {integrity: sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==} + + '@vitest/spy@3.2.3': + resolution: {integrity: sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==} + + '@vitest/utils@3.2.3': + resolution: {integrity: sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==} + + ansis@4.1.0: + resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==} + engines: {node: '>=14'} + + args-tokenizer@0.3.0: + resolution: {integrity: sha512-xXAd7G2Mll5W8uo37GETpQ2VrE84M181Z7ugHFGQnJZ50M2mbOv0osSZ9VsSgPfJQ+LVG0prSi0th+ELMsno7Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-kit@2.1.0: + resolution: {integrity: sha512-ROM2LlXbZBZVk97crfw8PGDOBzzsJvN2uJCmwswvPUNyfH14eg90mSN3xNqsri1JS1G9cz0VzeDUhxJkTrr4Ew==} + engines: {node: '>=20.18.0'} + + birpc@2.3.0: + resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==} + + bumpp@10.1.1: + resolution: {integrity: sha512-69ejE1J5O5qDN3oRu2jRas1nQmi5zEYepjzbYPpi1znuDnp+zZ9Yezsf/nYauWeoMNALQ5toniNGET05Txj2cQ==} + engines: {node: '>=18'} + hasBin: true + + c12@3.0.4: + resolution: {integrity: sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==} + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + 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==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} + + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} + engines: {node: '>=12'} + + dts-resolver@2.1.1: + resolution: {integrity: sha512-3BiGFhB6mj5Kv+W2vdJseQUYW+SKVzAFJL6YNP6ursbrwy1fXHRotfHi3xLNxe4wZl/K8qbAFeCDjZLjzqxxRw==} + engines: {node: '>=20.18.0'} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + + empathic@1.1.0: + resolution: {integrity: sha512-rsPft6CK3eHtrlp9Y5ALBb+hfK+DWnA4WFebbazxjWyx8vSm3rZeoM3z9irsjcqO3PYRzlfv27XIB4tz2DV7RA==} + engines: {node: '>=14'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + 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.2.1: + resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} + engines: {node: '>=12.0.0'} + + exsolve@1.0.5: + resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + 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-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + giget@2.0.0: + resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} + hasBin: true + + happy-dom@17.6.3: + resolution: {integrity: sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug==} + engines: {node: '>=20.0.0'} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch-native@1.6.6: + resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} + + nypm@0.6.0: + resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pkg-types@2.1.0: + resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==} + + postcss@8.5.5: + resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==} + engines: {node: ^10 || ^12 || >=14} + + quansync@0.2.10: + resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + rolldown-plugin-dts@0.13.11: + resolution: {integrity: sha512-1TScN31JImk8xcq9kdm52z2W8/QX3zeDpEjFkyZmK+GcD0u8QqSWWARBsCEdfS99NyI6D9NIbUpsABXlcpZhig==} + engines: {node: '>=20.18.0'} + peerDependencies: + '@typescript/native-preview': '>=7.0.0-dev.20250601.1' + rolldown: ^1.0.0-beta.9 + typescript: ^5.0.0 + vue-tsc: ~2.2.0 + peerDependenciesMeta: + '@typescript/native-preview': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.0.0-beta.9: + resolution: {integrity: sha512-ZgZky52n6iF0UainGKjptKGrOG4Con2S5sdc4C4y2Oj25D5PHAY8Y8E5f3M2TSd/zlhQs574JlMeTe3vREczSg==} + hasBin: true + peerDependencies: + '@oxc-project/runtime': 0.70.0 + peerDependenciesMeta: + '@oxc-project/runtime': + optional: true + + rollup@4.43.0: + resolution: {integrity: sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + + 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.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.0: + resolution: {integrity: sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + engines: {node: '>=14.0.0'} + + tsdown@0.11.13: + resolution: {integrity: sha512-VSfoNm8MJXFdg7PJ4p2javgjMRiQQHpkP9N3iBBTrmCixcT6YZ9ZtqYMW3NDHczqR0C0Qnur1HMQr1ZfZcmrng==} + engines: {node: '>=18.0.0'} + hasBin: true + peerDependencies: + publint: ^0.3.0 + typescript: ^5.0.0 + unplugin-lightningcss: ^0.4.0 + unplugin-unused: ^0.5.0 + peerDependenciesMeta: + publint: + optional: true + typescript: + optional: true + unplugin-lightningcss: + optional: true + unplugin-unused: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.20.2: + resolution: {integrity: sha512-He0ZWr41gLa4vD30Au3yuwpe0HXaCZbclvl8RBieUiJ9aFnPMWUPIyvw3RU8+1Crjfcrauvitae2a4tUzRAGsw==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + unconfig@7.3.2: + resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + vite-node@3.2.3: + resolution: {integrity: sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.2.3: + resolution: {integrity: sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.3 + '@vitest/ui': 3.2.3 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + +snapshots: + + '@babel/generator@7.27.5': + dependencies: + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.27.5': + dependencies: + '@babel/types': 7.27.6 + + '@babel/types@7.27.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@biomejs/biome@1.9.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.9.4 + '@biomejs/cli-darwin-x64': 1.9.4 + '@biomejs/cli-linux-arm64': 1.9.4 + '@biomejs/cli-linux-arm64-musl': 1.9.4 + '@biomejs/cli-linux-x64': 1.9.4 + '@biomejs/cli-linux-x64-musl': 1.9.4 + '@biomejs/cli-win32-arm64': 1.9.4 + '@biomejs/cli-win32-x64': 1.9.4 + + '@biomejs/cli-darwin-arm64@1.9.4': + optional: true + + '@biomejs/cli-darwin-x64@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64@1.9.4': + optional: true + + '@biomejs/cli-linux-x64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-x64@1.9.4': + optional: true + + '@biomejs/cli-win32-arm64@1.9.4': + optional: true + + '@biomejs/cli-win32-x64@1.9.4': + optional: true + + '@emnapi/core@1.4.3': + dependencies: + '@emnapi/wasi-threads': 1.0.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.0.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.5': + optional: true + + '@esbuild/android-arm64@0.25.5': + optional: true + + '@esbuild/android-arm@0.25.5': + optional: true + + '@esbuild/android-x64@0.25.5': + optional: true + + '@esbuild/darwin-arm64@0.25.5': + optional: true + + '@esbuild/darwin-x64@0.25.5': + optional: true + + '@esbuild/freebsd-arm64@0.25.5': + optional: true + + '@esbuild/freebsd-x64@0.25.5': + optional: true + + '@esbuild/linux-arm64@0.25.5': + optional: true + + '@esbuild/linux-arm@0.25.5': + optional: true + + '@esbuild/linux-ia32@0.25.5': + optional: true + + '@esbuild/linux-loong64@0.25.5': + optional: true + + '@esbuild/linux-mips64el@0.25.5': + optional: true + + '@esbuild/linux-ppc64@0.25.5': + optional: true + + '@esbuild/linux-riscv64@0.25.5': + optional: true + + '@esbuild/linux-s390x@0.25.5': + optional: true + + '@esbuild/linux-x64@0.25.5': + optional: true + + '@esbuild/netbsd-arm64@0.25.5': + optional: true + + '@esbuild/netbsd-x64@0.25.5': + optional: true + + '@esbuild/openbsd-arm64@0.25.5': + optional: true + + '@esbuild/openbsd-x64@0.25.5': + optional: true + + '@esbuild/sunos-x64@0.25.5': + optional: true + + '@esbuild/win32-arm64@0.25.5': + optional: true + + '@esbuild/win32-ia32@0.25.5': + optional: true + + '@esbuild/win32-x64@0.25.5': + optional: true + + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.4.3 + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@napi-rs/wasm-runtime@0.2.11': + dependencies: + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.3 + '@tybys/wasm-util': 0.9.0 + optional: true + + '@oxc-project/types@0.70.0': {} + + '@quansync/fs@0.1.3': + dependencies: + quansync: 0.2.10 + + '@rolldown/binding-darwin-arm64@1.0.0-beta.9': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.9': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.9': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.9': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.9': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.9': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.9': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.9': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.9': + dependencies: + '@napi-rs/wasm-runtime': 0.2.11 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.9': + optional: true + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.9': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.9': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.9': {} + + '@rollup/rollup-android-arm-eabi@4.43.0': + optional: true + + '@rollup/rollup-android-arm64@4.43.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.43.0': + optional: true + + '@rollup/rollup-darwin-x64@4.43.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.43.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.43.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.43.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.43.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.43.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.43.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.43.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.43.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.43.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.43.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.43.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.43.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.43.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.43.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.43.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.43.0': + optional: true + + '@tybys/wasm-util@0.9.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + optional: true + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.7': {} + + '@types/estree@1.0.8': {} + + '@types/ms@2.1.0': + optional: true + + '@types/node@22.15.31': + dependencies: + undici-types: 6.21.0 + + '@vitest/expect@3.2.3': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.3 + '@vitest/utils': 3.2.3 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.3(vite@6.3.5(@types/node@22.15.31)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 3.2.3 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.5(@types/node@22.15.31)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0) + + '@vitest/pretty-format@3.2.3': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.3': + dependencies: + '@vitest/utils': 3.2.3 + pathe: 2.0.3 + strip-literal: 3.0.0 + + '@vitest/snapshot@3.2.3': + dependencies: + '@vitest/pretty-format': 3.2.3 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.2.3': + dependencies: + tinyspy: 4.0.3 + + '@vitest/utils@3.2.3': + dependencies: + '@vitest/pretty-format': 3.2.3 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + + ansis@4.1.0: {} + + args-tokenizer@0.3.0: {} + + assertion-error@2.0.1: {} + + ast-kit@2.1.0: + dependencies: + '@babel/parser': 7.27.5 + pathe: 2.0.3 + + birpc@2.3.0: {} + + bumpp@10.1.1: + dependencies: + ansis: 4.1.0 + args-tokenizer: 0.3.0 + c12: 3.0.4 + cac: 6.7.14 + escalade: 3.2.0 + jsonc-parser: 3.3.1 + package-manager-detector: 1.3.0 + semver: 7.7.2 + tinyexec: 1.0.1 + tinyglobby: 0.2.14 + yaml: 2.8.0 + transitivePeerDependencies: + - magicast + + c12@3.0.4: + dependencies: + chokidar: 4.0.3 + confbox: 0.2.2 + defu: 6.1.4 + dotenv: 16.5.0 + exsolve: 1.0.5 + giget: 2.0.0 + jiti: 2.4.2 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 1.0.0 + pkg-types: 2.1.0 + rc9: 2.1.2 + + cac@6.7.14: {} + + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + + check-error@2.1.1: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + confbox@0.2.2: {} + + consola@3.4.2: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + defu@6.1.4: {} + + destr@2.0.5: {} + + detect-libc@2.0.4: {} + + diff@8.0.2: {} + + dotenv@16.5.0: {} + + dts-resolver@2.1.1: {} + + empathic@1.1.0: {} + + es-module-lexer@1.7.0: {} + + esbuild@0.25.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 + + escalade@3.2.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + expect-type@1.2.1: {} + + exsolve@1.0.5: {} + + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + giget@2.0.0: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.4 + node-fetch-native: 1.6.6 + nypm: 0.6.0 + pathe: 2.0.3 + + happy-dom@17.6.3: + dependencies: + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + + hookable@5.5.3: {} + + is-arrayish@0.3.2: {} + + jiti@2.4.2: {} + + js-tokens@9.0.1: {} + + jsesc@3.1.0: {} + + jsonc-parser@3.3.1: {} + + loupe@3.1.3: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + node-fetch-native@1.6.6: {} + + nypm@0.6.0: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 2.0.3 + pkg-types: 2.1.0 + tinyexec: 0.3.2 + + ohash@2.0.11: {} + + package-manager-detector@1.3.0: {} + + pathe@2.0.3: {} + + pathval@2.0.0: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.2: {} + + pkg-types@2.1.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.5 + pathe: 2.0.3 + + postcss@8.5.5: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + quansync@0.2.10: {} + + rc9@2.1.2: + dependencies: + defu: 6.1.4 + destr: 2.0.5 + + readdirp@4.1.2: {} + + resolve-pkg-maps@1.0.0: {} + + rolldown-plugin-dts@0.13.11(rolldown@1.0.0-beta.9)(typescript@5.8.3): + dependencies: + '@babel/generator': 7.27.5 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + ast-kit: 2.1.0 + birpc: 2.3.0 + debug: 4.4.1 + dts-resolver: 2.1.1 + get-tsconfig: 4.10.1 + rolldown: 1.0.0-beta.9 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - oxc-resolver + - supports-color + + rolldown@1.0.0-beta.9: + dependencies: + '@oxc-project/types': 0.70.0 + '@rolldown/pluginutils': 1.0.0-beta.9 + ansis: 4.1.0 + optionalDependencies: + '@rolldown/binding-darwin-arm64': 1.0.0-beta.9 + '@rolldown/binding-darwin-x64': 1.0.0-beta.9 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.9 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.9 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.9 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.9 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.9 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.9 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.9 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.9 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.9 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.9 + + rollup@4.43.0: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.43.0 + '@rollup/rollup-android-arm64': 4.43.0 + '@rollup/rollup-darwin-arm64': 4.43.0 + '@rollup/rollup-darwin-x64': 4.43.0 + '@rollup/rollup-freebsd-arm64': 4.43.0 + '@rollup/rollup-freebsd-x64': 4.43.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.43.0 + '@rollup/rollup-linux-arm-musleabihf': 4.43.0 + '@rollup/rollup-linux-arm64-gnu': 4.43.0 + '@rollup/rollup-linux-arm64-musl': 4.43.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.43.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.43.0 + '@rollup/rollup-linux-riscv64-gnu': 4.43.0 + '@rollup/rollup-linux-riscv64-musl': 4.43.0 + '@rollup/rollup-linux-s390x-gnu': 4.43.0 + '@rollup/rollup-linux-x64-gnu': 4.43.0 + '@rollup/rollup-linux-x64-musl': 4.43.0 + '@rollup/rollup-win32-arm64-msvc': 4.43.0 + '@rollup/rollup-win32-ia32-msvc': 4.43.0 + '@rollup/rollup-win32-x64-msvc': 4.43.0 + fsevents: 2.3.3 + + semver@7.7.2: {} + + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + + siginfo@2.0.0: {} + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@3.9.0: {} + + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyexec@1.0.1: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.1.0: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.3: {} + + tsdown@0.11.13(typescript@5.8.3): + dependencies: + ansis: 4.1.0 + cac: 6.7.14 + chokidar: 4.0.3 + debug: 4.4.1 + diff: 8.0.2 + empathic: 1.1.0 + hookable: 5.5.3 + rolldown: 1.0.0-beta.9 + rolldown-plugin-dts: 0.13.11(rolldown@1.0.0-beta.9)(typescript@5.8.3) + semver: 7.7.2 + tinyexec: 1.0.1 + tinyglobby: 0.2.14 + unconfig: 7.3.2 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - '@oxc-project/runtime' + - '@typescript/native-preview' + - oxc-resolver + - supports-color + - vue-tsc + + tslib@2.8.1: + optional: true + + tsx@4.20.2: + dependencies: + esbuild: 0.25.5 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.8.3: {} + + unconfig@7.3.2: + dependencies: + '@quansync/fs': 0.1.3 + defu: 6.1.4 + jiti: 2.4.2 + quansync: 0.2.10 + + undici-types@6.21.0: {} + + vite-node@3.2.3(@types/node@22.15.31)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0): + dependencies: + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.3.5(@types/node@22.15.31)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@6.3.5(@types/node@22.15.31)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0): + dependencies: + esbuild: 0.25.5 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.5 + rollup: 4.43.0 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 22.15.31 + fsevents: 2.3.3 + jiti: 2.4.2 + tsx: 4.20.2 + yaml: 2.8.0 + + vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.3 + '@vitest/mocker': 3.2.3(vite@6.3.5(@types/node@22.15.31)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0)) + '@vitest/pretty-format': 3.2.3 + '@vitest/runner': 3.2.3 + '@vitest/snapshot': 3.2.3 + '@vitest/spy': 3.2.3 + '@vitest/utils': 3.2.3 + chai: 5.2.0 + debug: 4.4.1 + expect-type: 1.2.1 + magic-string: 0.30.17 + pathe: 2.0.3 + picomatch: 4.0.2 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.0 + tinyrainbow: 2.0.0 + vite: 6.3.5(@types/node@22.15.31)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0) + vite-node: 3.2.3(@types/node@22.15.31)(jiti@2.4.2)(tsx@4.20.2)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.15.31 + happy-dom: 17.6.3 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + webidl-conversions@7.0.0: {} + + whatwg-mimetype@3.0.0: {} + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + yaml@2.8.0: {} diff --git a/libs/computer/typescript/pnpm-workspace.yaml b/libs/computer/typescript/pnpm-workspace.yaml new file mode 100644 index 00000000..e9796de5 --- /dev/null +++ b/libs/computer/typescript/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +onlyBuiltDependencies: + - "@biomejs/biome" + - sharp diff --git a/libs/computer/typescript/src/computer/computer.ts b/libs/computer/typescript/src/computer/computer.ts new file mode 100644 index 00000000..f4c9cffd --- /dev/null +++ b/libs/computer/typescript/src/computer/computer.ts @@ -0,0 +1,644 @@ +import type { Display, ComputerConfig } from '../models'; +import type { BaseComputerInterface } from '../interface/base'; +import { InterfaceFactory } from '../interface/factory'; +import type { BaseVMProvider } from '../providers/base'; +import { VMProviderType } from '../providers/base'; +import { VMProviderFactory } from '../providers/factory'; +import { Logger, LogLevel } from '../logger'; +import { recordComputerInitialization, recordVMStart, recordVMStop } from '../telemetry'; +import { setDefaultComputer } from '../helpers'; +import { parseDisplayString, parseImageString, sleep, withTimeout } from '../utils'; +import sharp from 'sharp'; + +export type OSType = 'macos' | 'linux' | 'windows'; + +export interface ComputerOptions { + display?: Display | { width: number; height: number } | string; + memory?: string; + cpu?: string; + osType?: OSType; + name?: string; + image?: string; + sharedDirectories?: string[]; + useHostComputerServer?: boolean; + verbosity?: number | LogLevel; + telemetryEnabled?: boolean; + providerType?: VMProviderType | string; + port?: number; + noVNCPort?: number; + host?: string; + storage?: string; + ephemeral?: boolean; + apiKey?: string; + experiments?: string[]; +} + +/** + * Computer is the main class for interacting with the computer. + */ +export class Computer { + private logger: Logger; + private vmLogger: Logger; + private interfaceLogger: Logger; + + private image: string; + private port?: number; + private noVNCPort?: number; + private host: string; + private osType: OSType; + private providerType: VMProviderType | string; + private ephemeral: boolean; + private apiKey?: string; + private experiments: string[]; + private storage?: string; + private sharedPath?: string; + private sharedDirectories: string[]; + private _telemetryEnabled: boolean; + private _initialized: boolean = false; + private _running: boolean = false; + private verbosity: number | LogLevel; + private useHostComputerServer: boolean; + private config?: ComputerConfig; + private _providerContext?: BaseVMProvider; + private _interface?: BaseComputerInterface; + private _stopEvent?: Promise; + private _keepAliveTask?: Promise; + + /** + * Initialize a new Computer instance. + * + * @param options Configuration options for the Computer + */ + constructor(options: ComputerOptions = {}) { + const { + display = '1024x768', + memory = '8GB', + cpu = '4', + osType = 'macos', + name = '', + image = 'macos-sequoia-cua:latest', + sharedDirectories = [], + useHostComputerServer = false, + verbosity = LogLevel.NORMAL, + telemetryEnabled = true, + providerType = VMProviderType.LUME, + port = 7777, + noVNCPort = 8006, + host = process.env.PYLUME_HOST || 'localhost', + storage, + ephemeral = false, + apiKey, + experiments = [] + } = options; + + this.verbosity = verbosity; + this.logger = new Logger('cua.computer', verbosity); + this.logger.info('Initializing Computer...'); + + // Store original parameters + this.image = image; + this.port = port; + this.noVNCPort = noVNCPort; + this.host = host; + this.osType = osType; + this.providerType = providerType; + this.ephemeral = ephemeral; + this.apiKey = apiKey; + this.experiments = experiments; + + if (this.experiments.includes('app-use')) { + if (this.osType !== 'macos') { + throw new Error('App use experiment is only supported on macOS'); + } + } + + // The default is currently to use non-ephemeral storage + if (storage && ephemeral && storage !== 'ephemeral') { + throw new Error('Storage path and ephemeral flag cannot be used together'); + } + this.storage = ephemeral ? 'ephemeral' : storage; + + // For Lumier provider, store the first shared directory path to use + // for VM file sharing + this.sharedPath = undefined; + if (sharedDirectories && sharedDirectories.length > 0) { + this.sharedPath = sharedDirectories[0]; + this.logger.info(`Using first shared directory for VM file sharing: ${this.sharedPath}`); + } + + // Store telemetry preference + this._telemetryEnabled = telemetryEnabled; + + // Configure component loggers with proper hierarchy + this.vmLogger = new Logger('cua.vm', verbosity); + this.interfaceLogger = new Logger('cua.interface', verbosity); + + this.useHostComputerServer = useHostComputerServer; + + if (!useHostComputerServer) { + const imageInfo = parseImageString(image); + + const vmName = name || image.replace(':', '_'); + + // Convert display parameter to Display object + let displayConfig: Display; + if (typeof display === 'string') { + const { width, height } = parseDisplayString(display); + displayConfig = { width, height }; + } else if ('width' in display && 'height' in display) { + displayConfig = display as Display; + } else { + displayConfig = display as Display; + } + + this.config = { + image: imageInfo.name, + tag: imageInfo.tag, + name: vmName, + display: displayConfig, + memory, + cpu, + }; + } + + // Store shared directories config + this.sharedDirectories = sharedDirectories; + + // Record initialization in telemetry (if enabled) + if (telemetryEnabled) { + recordComputerInitialization(); + } else { + this.logger.debug('Telemetry disabled - skipping initialization tracking'); + } + } + + /** + * Create a virtual desktop from a list of app names, returning a DioramaComputer + * that proxies Diorama.Interface but uses diorama_cmds via the computer interface. + * + * @param apps List of application names to include in the desktop. + * @returns A proxy object with the Diorama interface, but using diorama_cmds. + */ + createDesktopFromApps(apps: string[]): any { + if (!this.experiments.includes('app-use')) { + throw new Error("App Usage is an experimental feature. Enable it by passing experiments=['app-use'] to Computer()"); + } + // DioramaComputer would be imported and used here + throw new Error('DioramaComputer not yet implemented'); + } + + /** + * Start the computer (async context manager enter). + */ + async __aenter__(): Promise { + await this.run(); + return this; + } + + /** + * Stop the computer (async context manager exit). + */ + async __aexit__(excType: any, excVal: any, excTb: any): Promise { + await this.disconnect(); + } + + /** + * Initialize the VM and computer interface. + */ + async run(): Promise { + // If already initialized, just log and return + if (this._initialized) { + this.logger.info('Computer already initialized, skipping initialization'); + return; + } + + this.logger.info('Starting computer...'); + const startTime = Date.now(); + + try { + let ipAddress: string; + + // If using host computer server + if (this.useHostComputerServer) { + this.logger.info('Using host computer server'); + ipAddress = 'localhost'; + + // Create the interface + this._interface = InterfaceFactory.createInterfaceForOS( + this.osType, + ipAddress + ); + + this.logger.info('Waiting for host computer server to be ready...'); + await this._interface.waitForReady(); + this.logger.info('Host computer server ready'); + } else { + // Start or connect to VM + this.logger.info(`Starting VM: ${this.image}`); + + if (!this._providerContext) { + try { + const providerTypeName = typeof this.providerType === 'object' + ? this.providerType + : this.providerType; + + this.logger.verbose(`Initializing ${providerTypeName} provider context...`); + + // Create VM provider instance with explicit parameters + const providerOptions = { + port: this.port, + host: this.host, + storage: this.storage, + sharedPath: this.sharedPath, + image: this.image, + verbose: this.verbosity >= LogLevel.DEBUG, + ephemeral: this.ephemeral, + noVNCPort: this.noVNCPort, + apiKey: this.apiKey + }; + + if (!this.config) { + throw new Error('Computer config not initialized'); + } + + this.config.vm_provider = await VMProviderFactory.createProvider( + this.providerType, + providerOptions + ); + + this._providerContext = await this.config.vm_provider.__aenter__(); + this.logger.verbose('VM provider context initialized successfully'); + } catch (error) { + this.logger.error(`Failed to import provider dependencies: ${error}`); + throw error; + } + } + + // Run the VM + if (!this.config || !this.config.vm_provider) { + throw new Error('VM provider not initialized'); + } + + const runOpts = { + display: this.config.display, + memory: this.config.memory, + cpu: this.config.cpu, + shared_directories: this.sharedDirectories + }; + + this.logger.info(`Running VM ${this.config.name} with options:`, runOpts); + + if (this._telemetryEnabled) { + recordVMStart(this.config.name, String(this.providerType)); + } + + const storageParam = this.ephemeral ? 'ephemeral' : this.storage; + + try { + await this.config.vm_provider.runVM( + this.image, + this.config.name, + runOpts, + storageParam + ); + } catch (error: any) { + if (error.message?.includes('already running')) { + this.logger.info(`VM ${this.config.name} is already running`); + } else { + throw error; + } + } + + // Wait for VM to be ready + try { + this.logger.info('Waiting for VM to be ready...'); + await this.waitVMReady(); + + // Get IP address + ipAddress = await this.getIP(); + this.logger.info(`VM is ready with IP: ${ipAddress}`); + } catch (error) { + this.logger.error(`Error waiting for VM: ${error}`); + throw new Error(`VM failed to become ready: ${error}`); + } + } + + // Initialize the interface + try { + // Verify we have a valid IP before initializing the interface + if (!ipAddress || ipAddress === 'unknown' || ipAddress === '0.0.0.0') { + throw new Error(`Cannot initialize interface - invalid IP address: ${ipAddress}`); + } + + this.logger.info(`Initializing interface for ${this.osType} at ${ipAddress}`); + + // Pass authentication credentials if using cloud provider + if (this.providerType === VMProviderType.CLOUD && this.apiKey && this.config?.name) { + this._interface = InterfaceFactory.createInterfaceForOS( + this.osType, + ipAddress, + this.apiKey, + this.config.name + ); + } else { + this._interface = InterfaceFactory.createInterfaceForOS( + this.osType, + ipAddress + ); + } + + // Wait for the WebSocket interface to be ready + this.logger.info('Connecting to WebSocket interface...'); + + try { + await withTimeout( + this._interface.waitForReady(), + 30000, + `Could not connect to WebSocket interface at ${ipAddress}:8000/ws` + ); + this.logger.info('WebSocket interface connected successfully'); + } catch (error) { + this.logger.error(`Failed to connect to WebSocket interface at ${ipAddress}`); + throw error; + } + + // Create an event to keep the VM running in background if needed + if (!this.useHostComputerServer) { + // In TypeScript, we'll use a Promise instead of asyncio.Event + let resolveStop: () => void; + this._stopEvent = new Promise(resolve => { + resolveStop = resolve; + }); + this._keepAliveTask = this._stopEvent; + } + + this.logger.info('Computer is ready'); + + // Set the initialization flag + this._initialized = true; + + // Set this instance as the default computer for remote decorators + setDefaultComputer(this); + + this.logger.info('Computer successfully initialized'); + } catch (error) { + throw error; + } finally { + // Log initialization time for performance monitoring + const durationMs = Date.now() - startTime; + this.logger.debug(`Computer initialization took ${durationMs.toFixed(2)}ms`); + } + } catch (error) { + this.logger.error(`Failed to initialize computer: ${error}`); + throw new Error(`Failed to initialize computer: ${error}`); + } + + return; + } + + /** + * Disconnect from the computer's WebSocket interface. + */ + async disconnect(): Promise { + if (this._interface) { + // Note: The interface close method would need to be implemented + // this._interface.close(); + } + } + + /** + * Disconnect from the computer's WebSocket interface and stop the computer. + */ + async stop(): Promise { + const startTime = Date.now(); + + try { + this.logger.info('Stopping Computer...'); + + // In VM mode, first explicitly stop the VM, then exit the provider context + if (!this.useHostComputerServer && this._providerContext && this.config?.vm_provider) { + try { + this.logger.info(`Stopping VM ${this.config.name}...`); + await this.config.vm_provider.stopVM( + this.config.name, + this.storage + ); + } catch (error) { + this.logger.error(`Error stopping VM: ${error}`); + } + + this.logger.verbose('Closing VM provider context...'); + await this.config.vm_provider.__aexit__(null, null, null); + this._providerContext = undefined; + } + + await this.disconnect(); + this.logger.info('Computer stopped'); + } catch (error) { + this.logger.debug(`Error during cleanup: ${error}`); + } finally { + // Log stop time for performance monitoring + const durationMs = Date.now() - startTime; + this.logger.debug(`Computer stop process took ${durationMs.toFixed(2)}ms`); + + if (this._telemetryEnabled && this.config?.name) { + recordVMStop(this.config.name, durationMs); + } + } + } + + /** + * Get the IP address of the VM or localhost if using host computer server. + */ + async getIP(maxRetries: number = 15, retryDelay: number = 2): Promise { + // For host computer server, always return localhost immediately + if (this.useHostComputerServer) { + return '127.0.0.1'; + } + + // Get IP from the provider + if (!this.config?.vm_provider) { + throw new Error('VM provider is not initialized'); + } + + // Log that we're waiting for the IP + this.logger.info(`Waiting for VM ${this.config.name} to get an IP address...`); + + // Call the provider's get_ip method which will wait indefinitely + const storageParam = this.ephemeral ? 'ephemeral' : this.storage; + + // Log the image being used + this.logger.info(`Running VM using image: ${this.image}`); + + // Call provider.getIP with explicit parameters + const ip = await this.config.vm_provider.getIP( + this.config.name, + storageParam, + retryDelay + ); + + // Log success + this.logger.info(`VM ${this.config.name} has IP address: ${ip}`); + return ip; + } + + /** + * Wait for VM to be ready with an IP address. + */ + async waitVMReady(): Promise | undefined> { + if (this.useHostComputerServer) { + return undefined; + } + + const timeout = 600; // 10 minutes timeout + const interval = 2.0; // 2 seconds between checks + const startTime = Date.now() / 1000; + let lastStatus: string | undefined; + let attempts = 0; + + this.logger.info(`Waiting for VM ${this.config?.name} to be ready (timeout: ${timeout}s)...`); + + while ((Date.now() / 1000) - startTime < timeout) { + attempts++; + const elapsed = (Date.now() / 1000) - startTime; + + try { + // Keep polling for VM info + if (!this.config?.vm_provider) { + this.logger.error('VM provider is not initialized'); + return undefined; + } + + const vm = await this.config.vm_provider.getVM(this.config.name); + + // Log full VM properties for debugging (every 30 attempts) + if (attempts % 30 === 0) { + this.logger.info( + `VM properties at attempt ${attempts}: ${JSON.stringify(vm)}` + ); + } + + // Get current status for logging + const currentStatus = vm?.status; + if (currentStatus !== lastStatus) { + this.logger.info( + `VM status changed to: ${currentStatus} (after ${elapsed.toFixed(1)}s)` + ); + lastStatus = currentStatus; + } + + // Check if VM is ready + if (vm && vm.status === 'running' && vm.ip_address && vm.ip_address !== '0.0.0.0') { + this.logger.info(`VM ${this.config.name} is ready with IP: ${vm.ip_address}`); + return vm; + } + + // Wait before next check + await sleep(interval * 1000); + } catch (error) { + this.logger.error(`Error checking VM status: ${error}`); + await sleep(interval * 1000); + } + } + + throw new Error(`VM ${this.config?.name} failed to become ready within ${timeout} seconds`); + } + + /** + * Update VM settings. + */ + async update(cpu?: number, memory?: string): Promise { + if (this.useHostComputerServer) { + this.logger.warning('Cannot update settings for host computer server'); + return; + } + + if (!this.config?.vm_provider) { + throw new Error('VM provider is not initialized'); + } + + await this.config.vm_provider.updateVM( + this.config.name, + cpu, + memory, + this.storage + ); + } + + /** + * Get the dimensions of a screenshot. + */ + async getScreenshotSize(screenshot: Buffer): Promise<{ width: number; height: number }> { + const metadata = await sharp(screenshot).metadata(); + return { + width: metadata.width || 0, + height: metadata.height || 0 + }; + } + + /** + * Get the computer interface for interacting with the VM. + */ + get interface(): BaseComputerInterface { + if (!this._interface) { + throw new Error('Computer interface not initialized. Call run() first.'); + } + return this._interface; + } + + /** + * Check if telemetry is enabled for this computer instance. + */ + get telemetryEnabled(): boolean { + return this._telemetryEnabled; + } + + /** + * Convert normalized coordinates to screen coordinates. + */ + toScreenCoordinates(x: number, y: number): [number, number] { + if (!this.config?.display) { + throw new Error('Display configuration not available'); + } + return [ + x * this.config.display.width, + y * this.config.display.height + ]; + } + + /** + * Convert screen coordinates to screenshot coordinates. + */ + async toScreenshotCoordinates(x: number, y: number): Promise<[number, number]> { + // In the Python version, this uses the interface to get screenshot dimensions + // For now, we'll assume 1:1 mapping + return [x, y]; + } + + /** + * Install packages in a virtual environment. + */ + async venvInstall(venvName: string, requirements: string[]): Promise<[string, string]> { + // This would be implemented using the interface to run commands + // TODO: Implement venvInstall + throw new Error('venvInstall not yet implemented'); + } + + /** + * Execute a shell command in a virtual environment. + */ + async venvCmd(venvName: string, command: string): Promise<[string, string]> { + // This would be implemented using the interface to run commands + // TODO: Implement venvCmd + throw new Error('venvCmd not yet implemented'); + } + + /** + * Execute function in a virtual environment using source code extraction. + */ + async venvExec(venvName: string, pythonFunc: Function, ...args: any[]): Promise { + // This would be implemented using the interface to run Python code + // TODO: Implement venvExec + throw new Error('venvExec not yet implemented'); + } +} diff --git a/libs/computer/typescript/src/computer/index.ts b/libs/computer/typescript/src/computer/index.ts new file mode 100644 index 00000000..706e2bda --- /dev/null +++ b/libs/computer/typescript/src/computer/index.ts @@ -0,0 +1,2 @@ +// Re-export the Computer class and related types +export * from './computer'; diff --git a/libs/computer/typescript/src/helpers.ts b/libs/computer/typescript/src/helpers.ts new file mode 100644 index 00000000..44da61f8 --- /dev/null +++ b/libs/computer/typescript/src/helpers.ts @@ -0,0 +1,76 @@ +/** + * Helper functions and decorators for the Computer module. + */ + +import type { Computer } from './computer'; + +// Global reference to the default computer instance +let _defaultComputer: Computer | null = null; + +/** + * Set the default computer instance to be used by the remote decorator. + * + * @param computer The computer instance to use as default + */ +export function setDefaultComputer(computer: Computer): void { + _defaultComputer = computer; +} + +/** + * Get the default computer instance. + * + * @returns The default computer instance or null + */ +export function getDefaultComputer(): Computer | null { + return _defaultComputer; +} + +/** + * Decorator that wraps a function to be executed remotely via computer.venvExec + * + * @param venvName Name of the virtual environment to execute in + * @param computer The computer instance to use, or "default" to use the globally set default + * @param maxRetries Maximum number of retries for the remote execution + */ +export function sandboxed( + venvName: string = 'default', + computer: Computer | 'default' = 'default', + maxRetries: number = 3 +) { + return function any>( + target: any, + propertyKey: string, + descriptor: PropertyDescriptor + ) { + const originalMethod = descriptor.value; + + descriptor.value = async function (...args: Parameters): Promise> { + // Determine which computer instance to use + const comp = computer === 'default' ? _defaultComputer : computer; + + if (!comp) { + throw new Error( + 'No computer instance available. Either specify a computer instance or call setDefaultComputer() first.' + ); + } + + for (let i = 0; i < maxRetries; i++) { + try { + return await comp.venvExec(venvName, originalMethod, ...args); + } catch (error) { + console.error(`Attempt ${i + 1} failed:`, error); + if (i < maxRetries - 1) { + await new Promise(resolve => setTimeout(resolve, 1000)); + } else { + throw error; + } + } + } + + // This should never be reached, but satisfies TypeScript's control flow analysis + throw new Error('Unexpected: maxRetries loop completed without returning or throwing'); + }; + + return descriptor; + }; +} diff --git a/libs/computer/typescript/src/index.ts b/libs/computer/typescript/src/index.ts new file mode 100644 index 00000000..551181ca --- /dev/null +++ b/libs/computer/typescript/src/index.ts @@ -0,0 +1,28 @@ +// Core components +export { Computer } from "./computer"; +export type { ComputerOptions, OSType } from "./computer"; + +// Models +export type { Display, ComputerConfig } from "./models"; + +// Provider components +export { VMProviderType, BaseVMProviderImpl } from "./providers"; +export type { BaseVMProvider } from "./providers"; +export { VMProviderFactory } from "./providers"; +export type { VMProviderOptions } from "./providers"; + +// Interface components +export type { BaseComputerInterface } from "./interface"; +export { InterfaceFactory } from "./interface"; +export type { InterfaceOptions } from "./interface"; +export { Key } from "./interface"; +export type { + KeyType, + MouseButton, + NavigationKey, + SpecialKey, + ModifierKey, + FunctionKey, + AccessibilityWindow, + AccessibilityTree +} from "./interface"; diff --git a/libs/computer/typescript/src/interface/base.ts b/libs/computer/typescript/src/interface/base.ts new file mode 100644 index 00000000..ef4dfe6c --- /dev/null +++ b/libs/computer/typescript/src/interface/base.ts @@ -0,0 +1,41 @@ +import type { KeyType, MouseButton, AccessibilityTree } from './models'; + +/** + * Base interface for computer control implementations. + */ +export interface BaseComputerInterface { + /** + * Wait for the interface to be ready. + */ + waitForReady(): Promise; + + /** + * Get a screenshot of the current screen. + */ + getScreenshot(): Promise; + + /** + * Move the mouse to the specified coordinates. + */ + moveMouse(x: number, y: number): Promise; + + /** + * Click the mouse at the current position. + */ + click(button?: MouseButton): Promise; + + /** + * Type text at the current cursor position. + */ + typeText(text: string): Promise; + + /** + * Press a key. + */ + pressKey(key: KeyType): Promise; + + /** + * Get the accessibility tree. + */ + getAccessibilityTree(): Promise; +} diff --git a/libs/computer/typescript/src/interface/factory.ts b/libs/computer/typescript/src/interface/factory.ts new file mode 100644 index 00000000..8f3fffba --- /dev/null +++ b/libs/computer/typescript/src/interface/factory.ts @@ -0,0 +1,56 @@ +import type { BaseComputerInterface } from './base'; +import type { OSType } from '../computer'; + +export interface InterfaceOptions { + ipAddress: string; + apiKey?: string; + vmName?: string; +} + +/** + * Factory for creating OS-specific computer interfaces. + */ +export class InterfaceFactory { + /** + * Create an interface for the specified OS. + * + * @param os The operating system type ('macos', 'linux', 'windows') + * @param ipAddress The IP address to connect to + * @param apiKey Optional API key for cloud providers + * @param vmName Optional VM name for cloud providers + * @returns An instance of the appropriate computer interface + */ + static createInterfaceForOS( + os: OSType, + ipAddress: string, + apiKey?: string, + vmName?: string + ): BaseComputerInterface { + const options: InterfaceOptions = { + ipAddress, + apiKey, + vmName + }; + + switch (os) { + case 'macos': + // Dynamic import would be used in real implementation + // TODO: Implement macOS interface + throw new Error('macOS interface not yet implemented'); + + case 'linux': + // Dynamic import would be used in real implementation + // TODO: Implement Linux interface + throw new Error('Linux interface not yet implemented'); + + case 'windows': + // Dynamic import would be used in real implementation + // TODO: Implement Windows interface + throw new Error('Windows interface not yet implemented'); + + default: + // TODO: Implement interface for this OS + throw new Error(`Interface for OS ${os} not implemented`); + } + } +} diff --git a/libs/computer/typescript/src/interface/index.ts b/libs/computer/typescript/src/interface/index.ts new file mode 100644 index 00000000..ac96f84e --- /dev/null +++ b/libs/computer/typescript/src/interface/index.ts @@ -0,0 +1,5 @@ +export * from "./base"; +export * from "./factory"; +export * from "./models"; +export * from "./macos"; +export * from "./linux"; diff --git a/libs/computer/typescript/src/interface/linux.ts b/libs/computer/typescript/src/interface/linux.ts new file mode 100644 index 00000000..3713cb7b --- /dev/null +++ b/libs/computer/typescript/src/interface/linux.ts @@ -0,0 +1,47 @@ +import type { BaseComputerInterface } from './base'; +import type { KeyType, MouseButton, AccessibilityTree } from './models'; + +/** + * Linux-specific implementation of the computer interface. + */ +export class LinuxComputerInterface implements BaseComputerInterface { + private ip_address: string; + + constructor(ip_address: string) { + this.ip_address = ip_address; + } + + async waitForReady(): Promise { + // Implementation will go here + } + + async getScreenshot(): Promise { + // Implementation will go here + return Buffer.from([]); + } + + async moveMouse(x: number, y: number): Promise { + // Implementation will go here + } + + async click(button: MouseButton = 'left'): Promise { + // Implementation will go here + } + + async typeText(text: string): Promise { + // Implementation will go here + } + + async pressKey(key: KeyType): Promise { + // Implementation will go here + } + + async getAccessibilityTree(): Promise { + // Implementation will go here + return { + success: false, + frontmost_application: '', + windows: [] + }; + } +} diff --git a/libs/computer/typescript/src/interface/macos.ts b/libs/computer/typescript/src/interface/macos.ts new file mode 100644 index 00000000..a031fa49 --- /dev/null +++ b/libs/computer/typescript/src/interface/macos.ts @@ -0,0 +1,47 @@ +import type { BaseComputerInterface } from './base'; +import type { KeyType, MouseButton, AccessibilityTree } from './models'; + +/** + * macOS-specific implementation of the computer interface. + */ +export class MacOSComputerInterface implements BaseComputerInterface { + private ip_address: string; + + constructor(ip_address: string) { + this.ip_address = ip_address; + } + + async waitForReady(): Promise { + // Implementation will go here + } + + async getScreenshot(): Promise { + // Implementation will go here + return Buffer.from([]); + } + + async moveMouse(x: number, y: number): Promise { + // Implementation will go here + } + + async click(button: MouseButton = 'left'): Promise { + // Implementation will go here + } + + async typeText(text: string): Promise { + // Implementation will go here + } + + async pressKey(key: KeyType): Promise { + // Implementation will go here + } + + async getAccessibilityTree(): Promise { + // Implementation will go here + return { + success: false, + frontmost_application: '', + windows: [] + }; + } +} diff --git a/libs/computer/typescript/src/interface/models.ts b/libs/computer/typescript/src/interface/models.ts new file mode 100644 index 00000000..80f2b0a7 --- /dev/null +++ b/libs/computer/typescript/src/interface/models.ts @@ -0,0 +1,96 @@ +/** + * Navigation key literals + */ +export type NavigationKey = 'pagedown' | 'pageup' | 'home' | 'end' | 'left' | 'right' | 'up' | 'down'; + +/** + * Special key literals + */ +export type SpecialKey = 'enter' | 'esc' | 'tab' | 'space' | 'backspace' | 'del'; + +/** + * Modifier key literals + */ +export type ModifierKey = 'ctrl' | 'alt' | 'shift' | 'win' | 'command' | 'option'; + +/** + * Function key literals + */ +export type FunctionKey = 'f1' | 'f2' | 'f3' | 'f4' | 'f5' | 'f6' | 'f7' | 'f8' | 'f9' | 'f10' | 'f11' | 'f12'; + +/** + * Keyboard keys that can be used with press_key. + */ +export enum Key { + // Navigation + PAGE_DOWN = 'pagedown', + PAGE_UP = 'pageup', + HOME = 'home', + END = 'end', + LEFT = 'left', + RIGHT = 'right', + UP = 'up', + DOWN = 'down', + + // Special keys + RETURN = 'enter', + ENTER = 'enter', + ESCAPE = 'esc', + ESC = 'esc', + TAB = 'tab', + SPACE = 'space', + BACKSPACE = 'backspace', + DELETE = 'del', + + // Modifier keys + ALT = 'alt', + CTRL = 'ctrl', + SHIFT = 'shift', + WIN = 'win', + COMMAND = 'command', + OPTION = 'option', + + // Function keys + F1 = 'f1', + F2 = 'f2', + F3 = 'f3', + F4 = 'f4', + F5 = 'f5', + F6 = 'f6', + F7 = 'f7', + F8 = 'f8', + F9 = 'f9', + F10 = 'f10', + F11 = 'f11', + F12 = 'f12' +} + +/** + * Combined key type + */ +export type KeyType = Key | NavigationKey | SpecialKey | ModifierKey | FunctionKey | string; + +/** + * Key type for mouse actions + */ +export type MouseButton = 'left' | 'right' | 'middle'; + +/** + * Information about a window in the accessibility tree. + */ +export interface AccessibilityWindow { + app_name: string; + pid: number; + frontmost: boolean; + has_windows: boolean; + windows: Array>; +} + +/** + * Complete accessibility tree information. + */ +export interface AccessibilityTree { + success: boolean; + frontmost_application: string; + windows: AccessibilityWindow[]; +} diff --git a/libs/computer/typescript/src/logger.ts b/libs/computer/typescript/src/logger.ts new file mode 100644 index 00000000..83f74425 --- /dev/null +++ b/libs/computer/typescript/src/logger.ts @@ -0,0 +1,50 @@ +/** + * Logger implementation for the Computer library. + */ + +export enum LogLevel { + DEBUG = 10, + VERBOSE = 15, + INFO = 20, + NORMAL = 20, + WARNING = 30, + ERROR = 40, +} + +export class Logger { + private name: string; + private verbosity: number; + + constructor(name: string, verbosity: number | LogLevel = LogLevel.NORMAL) { + this.name = name; + this.verbosity = typeof verbosity === 'number' ? verbosity : verbosity; + } + + private log(level: LogLevel, message: string, ...args: any[]): void { + if (level >= this.verbosity) { + const timestamp = new Date().toISOString(); + const levelName = LogLevel[level]; + console.log(`[${timestamp}] [${this.name}] [${levelName}] ${message}`, ...args); + } + } + + debug(message: string, ...args: any[]): void { + this.log(LogLevel.DEBUG, message, ...args); + } + + info(message: string, ...args: any[]): void { + this.log(LogLevel.INFO, message, ...args); + } + + verbose(message: string, ...args: any[]): void { + this.log(LogLevel.VERBOSE, message, ...args); + } + + warning(message: string, ...args: any[]): void { + this.log(LogLevel.WARNING, message, ...args); + } + + error(message: string, ...args: any[]): void { + this.log(LogLevel.ERROR, message, ...args); + } +} diff --git a/libs/computer/typescript/src/models.ts b/libs/computer/typescript/src/models.ts new file mode 100644 index 00000000..b0749cc5 --- /dev/null +++ b/libs/computer/typescript/src/models.ts @@ -0,0 +1,21 @@ +/** + * Display configuration for the computer. + */ +export interface Display { + width: number; + height: number; + scale_factor?: number; +} + +/** + * Computer configuration model. + */ +export interface ComputerConfig { + image: string; + tag: string; + name: string; + display: Display; + memory: string; + cpu: string; + vm_provider?: any; // Will be properly typed when implemented +} diff --git a/libs/computer/typescript/src/providers/base.ts b/libs/computer/typescript/src/providers/base.ts new file mode 100644 index 00000000..d5368a93 --- /dev/null +++ b/libs/computer/typescript/src/providers/base.ts @@ -0,0 +1,140 @@ +/** + * Types of VM providers available. + */ +export enum VMProviderType { + LUME = 'lume', + LUMIER = 'lumier', + CLOUD = 'cloud', + UNKNOWN = 'unknown' +} + +/** + * Base interface for VM providers. + * All VM provider implementations must implement this interface. + */ +export interface BaseVMProvider { + /** + * Get the provider type. + */ + readonly providerType: VMProviderType; + + /** + * Get VM information by name. + * + * @param name Name of the VM to get information for + * @param storage Optional storage path override + * @returns Dictionary with VM information including status, IP address, etc. + */ + getVM(name: string, storage?: string): Promise>; + + /** + * List all available VMs. + */ + listVMs(): Promise>>; + + /** + * Run a VM by name with the given options. + * + * @param image VM image to run + * @param name Name for the VM + * @param runOpts Run options for the VM + * @param storage Optional storage path + * @returns VM run response + */ + runVM( + image: string, + name: string, + runOpts: Record, + storage?: string + ): Promise>; + + /** + * Stop a VM by name. + * + * @param name Name of the VM to stop + * @param storage Optional storage path + */ + stopVM(name: string, storage?: string): Promise; + + /** + * Get the IP address of a VM. + * + * @param name Name of the VM + * @param storage Optional storage path + * @param retryDelay Delay between retries in seconds + * @returns IP address of the VM + */ + getIP(name: string, storage?: string, retryDelay?: number): Promise; + + /** + * Update VM settings. + * + * @param name Name of the VM + * @param cpu New CPU allocation + * @param memory New memory allocation + * @param storage Optional storage path + */ + updateVM( + name: string, + cpu?: number, + memory?: string, + storage?: string + ): Promise; + + /** + * Context manager enter method + */ + __aenter__(): Promise; + + /** + * Context manager exit method + */ + __aexit__( + excType: any, + excVal: any, + excTb: any + ): Promise; +} + +/** + * Abstract base class for VM providers that implements context manager + */ +export abstract class BaseVMProviderImpl implements BaseVMProvider { + abstract readonly providerType: VMProviderType; + + abstract getVM(name: string, storage?: string): Promise>; + abstract listVMs(): Promise>>; + abstract runVM( + image: string, + name: string, + runOpts: Record, + storage?: string + ): Promise>; + abstract stopVM(name: string, storage?: string): Promise; + abstract getIP(name: string, storage?: string, retryDelay?: number): Promise; + abstract updateVM( + name: string, + cpu?: number, + memory?: string, + storage?: string + ): Promise; + + async __aenter__(): Promise { + // Default implementation - can be overridden + return this; + } + + /** + * Async context manager exit. + * + * This method is called when exiting an async context manager block. + * It handles proper cleanup of resources, including stopping any running containers. + */ + async __aexit__( + _excType: any, + _excVal: any, + _excTb: any + ): Promise { + // Default implementation - can be overridden + } +} diff --git a/libs/computer/typescript/src/providers/cloud/index.ts b/libs/computer/typescript/src/providers/cloud/index.ts new file mode 100644 index 00000000..6b434c36 --- /dev/null +++ b/libs/computer/typescript/src/providers/cloud/index.ts @@ -0,0 +1,5 @@ +/** + * Cloud VM provider implementation. + */ + +export { CloudProvider } from "./provider"; diff --git a/libs/computer/typescript/src/providers/cloud/provider.ts b/libs/computer/typescript/src/providers/cloud/provider.ts new file mode 100644 index 00000000..573f699c --- /dev/null +++ b/libs/computer/typescript/src/providers/cloud/provider.ts @@ -0,0 +1,68 @@ +/** + * Cloud VM provider implementation. + * + * This provider is a placeholder for cloud-based VM provisioning. + * It will be implemented to support cloud VM services in the future. + */ + +import { BaseVMProviderImpl, VMProviderType } from '../base'; + +export interface CloudProviderOptions { + verbose?: boolean; + apiKey?: string; + region?: string; + [key: string]: any; +} + +export class CloudProvider extends BaseVMProviderImpl { + readonly providerType = VMProviderType.CLOUD; + + private verbose: boolean; + private options: CloudProviderOptions; + + constructor(options: CloudProviderOptions = {}) { + super(); + this.verbose = options.verbose || false; + this.options = options; + } + + // TODO: Implement getVM for cloud provider + async getVM(name: string, storage?: string): Promise> { + throw new Error('CloudProvider is not fully implemented yet. Please use LUME or LUMIER provider instead.'); + } + + // TODO: Implement listVMs for cloud provider + async listVMs(): Promise>> { + throw new Error('CloudProvider is not fully implemented yet. Please use LUME or LUMIER provider instead.'); + } + + // TODO: Implement runVM for cloud provider + async runVM( + image: string, + name: string, + runOpts: Record, + storage?: string + ): Promise> { + throw new Error('CloudProvider is not fully implemented yet. Please use LUME or LUMIER provider instead.'); + } + + // TODO: Implement stopVM for cloud provider + async stopVM(name: string, storage?: string): Promise { + throw new Error('CloudProvider is not fully implemented yet. Please use LUME or LUMIER provider instead.'); + } + + // TODO: Implement getIP for cloud provider + async getIP(name: string, storage?: string, retryDelay?: number): Promise { + throw new Error('CloudProvider is not fully implemented yet. Please use LUME or LUMIER provider instead.'); + } + + // TODO: Implement updateVM for cloud provider + async updateVM( + name: string, + cpu?: number, + memory?: string, + storage?: string + ): Promise { + throw new Error('CloudProvider is not fully implemented yet. Please use LUME or LUMIER provider instead.'); + } +} diff --git a/libs/computer/typescript/src/providers/factory.ts b/libs/computer/typescript/src/providers/factory.ts new file mode 100644 index 00000000..a2efb149 --- /dev/null +++ b/libs/computer/typescript/src/providers/factory.ts @@ -0,0 +1,150 @@ +/** + * Factory for creating VM providers. + */ + +import type { BaseVMProvider } from './base'; +import { VMProviderType } from './base'; + +export interface VMProviderOptions { + port?: number; + host?: string; + binPath?: string; + storage?: string; + sharedPath?: string; + image?: string; + verbose?: boolean; + ephemeral?: boolean; + noVNCPort?: number; + [key: string]: any; // Allow additional provider-specific options +} + +export class VMProviderFactory { + /** + * Create a VM provider instance based on the provider type. + * + * @param providerType The type of provider to create + * @param options Provider-specific options + * @returns The created VM provider instance + * @throws Error if the provider type is not supported or dependencies are missing + */ + static async createProvider( + providerType: VMProviderType | string, + options: VMProviderOptions = {} + ): Promise { + // Convert string to enum if needed + let type: VMProviderType; + if (typeof providerType === 'string') { + const normalizedType = providerType.toLowerCase(); + if (Object.values(VMProviderType).includes(normalizedType as VMProviderType)) { + type = normalizedType as VMProviderType; + } else { + type = VMProviderType.UNKNOWN; + } + } else { + type = providerType; + } + + // Extract common options with defaults + const { + port = 7777, + host = 'localhost', + binPath, + storage, + sharedPath, + image, + verbose = false, + ephemeral = false, + noVNCPort, + ...additionalOptions + } = options; + + switch (type) { + case VMProviderType.LUME: { + try { + // Dynamic import for Lume provider + const { LumeProvider, HAS_LUME } = await import('./lume'); + + if (!HAS_LUME) { + throw new Error( + 'The required dependencies for LumeProvider are not available. ' + + 'Please ensure curl is installed and in your PATH.' + ); + } + + return new LumeProvider({ + port, + host, + storage, + verbose, + ephemeral + }); + } catch (error) { + if (error instanceof Error && error.message.includes('Cannot find module')) { + throw new Error( + 'The LumeProvider module is not available. ' + + 'Please install it with: npm install @cua/computer-lume' + ); + } + throw error; + } + } + + case VMProviderType.LUMIER: { + try { + // Dynamic import for Lumier provider + const { LumierProvider, HAS_LUMIER } = await import('./lumier'); + + if (!HAS_LUMIER) { + throw new Error( + 'Docker is required for LumierProvider. ' + + 'Please install Docker for Apple Silicon and Lume CLI before using this provider.' + ); + } + + return new LumierProvider({ + port, + host, + storage, + sharedPath, + image: image || 'macos-sequoia-cua:latest', + verbose, + ephemeral, + noVNCPort + }); + } catch (error) { + if (error instanceof Error && error.message.includes('Cannot find module')) { + throw new Error( + 'The LumierProvider module is not available. ' + + 'Docker and Lume CLI are required for LumierProvider. ' + + 'Please install Docker for Apple Silicon and run the Lume installer script.' + ); + } + throw error; + } + } + + case VMProviderType.CLOUD: { + try { + // Dynamic import for Cloud provider + const { CloudProvider } = await import('./cloud'); + + return new CloudProvider({ + verbose, + ...additionalOptions + }); + } catch (error) { + if (error instanceof Error && error.message.includes('Cannot find module')) { + throw new Error( + 'The CloudProvider is not fully implemented yet. ' + + 'Please use LUME or LUMIER provider instead.' + ); + } + throw error; + } + } + + default: + throw new Error(`Unsupported provider type: ${providerType}`); + } + } +} diff --git a/libs/computer/typescript/src/providers/index.ts b/libs/computer/typescript/src/providers/index.ts new file mode 100644 index 00000000..078b4cbd --- /dev/null +++ b/libs/computer/typescript/src/providers/index.ts @@ -0,0 +1,12 @@ +/** + * Export all provider-related modules. + */ + +export * from './base'; +export * from './factory'; +export * from './lume_api'; + +// Export provider implementations +export * from './lume'; +export * from './lumier'; +export * from './cloud'; diff --git a/libs/computer/typescript/src/providers/lume/index.ts b/libs/computer/typescript/src/providers/lume/index.ts new file mode 100644 index 00000000..9b020d8e --- /dev/null +++ b/libs/computer/typescript/src/providers/lume/index.ts @@ -0,0 +1,16 @@ +/** + * Lume VM provider implementation. + */ + +export let HAS_LUME = false; + +try { + // Check if curl is available + const { execSync } = require('child_process'); + execSync('which curl', { stdio: 'ignore' }); + HAS_LUME = true; +} catch { + HAS_LUME = false; +} + +export { LumeProvider } from './provider'; diff --git a/libs/computer/typescript/src/providers/lume/provider.ts b/libs/computer/typescript/src/providers/lume/provider.ts new file mode 100644 index 00000000..7c8c980a --- /dev/null +++ b/libs/computer/typescript/src/providers/lume/provider.ts @@ -0,0 +1,182 @@ +/** + * Lume VM provider implementation using curl commands. + * + * This provider uses direct curl commands to interact with the Lume API, + * removing the dependency on the pylume Python package. + */ + +import { BaseVMProviderImpl, VMProviderType } from '../base'; +import type { LumeRunOptions } from '../lume_api'; +import { + HAS_CURL, + lumeApiGet, + lumeApiRun, + lumeApiStop, + lumeApiUpdate, + lumeApiPull, + parseMemory +} from '../lume_api'; + +export interface LumeProviderOptions { + port?: number; + host?: string; + storage?: string; + verbose?: boolean; + ephemeral?: boolean; +} + +export class LumeProvider extends BaseVMProviderImpl { + readonly providerType = VMProviderType.LUME; + + private host: string; + private port: number; + private storage?: string; + private verbose: boolean; + private ephemeral: boolean; + + constructor(options: LumeProviderOptions = {}) { + super(); + + if (!HAS_CURL) { + throw new Error( + 'curl is required for LumeProvider. ' + + 'Please ensure it is installed and in your PATH.' + ); + } + + this.host = options.host || 'localhost'; + this.port = options.port || 7777; + this.storage = options.storage; + this.verbose = options.verbose || false; + this.ephemeral = options.ephemeral || false; + } + + async getVM(name: string, storage?: string): Promise> { + return lumeApiGet( + name, + storage || this.storage, + this.host, + this.port, + this.verbose + ); + } + + async listVMs(): Promise>> { + const response = await lumeApiGet( + '', + this.storage, + this.host, + this.port, + this.verbose + ); + + // The response should be an array of VMs + if (Array.isArray(response)) { + return response; + } + + // If it's an object with a vms property + if (response.vms && Array.isArray(response.vms)) { + return response.vms; + } + + // Otherwise return empty array + return []; + } + + async runVM( + image: string, + name: string, + runOpts: LumeRunOptions, + storage?: string + ): Promise> { + // Ensure the image is available + if (this.verbose) { + console.log(`Pulling image ${image} if needed...`); + } + + try { + await lumeApiPull(image, this.host, this.port, this.verbose); + } catch (error) { + if (this.verbose) { + console.log(`Failed to pull image: ${error}`); + } + } + + // Run the VM + return lumeApiRun( + image, + name, + runOpts, + storage || this.storage, + this.host, + this.port, + this.verbose + ); + } + + async stopVM(name: string, storage?: string): Promise { + await lumeApiStop( + name, + storage || this.storage, + this.host, + this.port, + this.verbose + ); + + // If ephemeral, the VM should be automatically deleted after stopping + if (this.ephemeral && this.verbose) { + console.log(`VM ${name} stopped and removed (ephemeral mode)`); + } + } + + async getIP(name: string, storage?: string, retryDelay: number = 1): Promise { + const maxRetries = 30; + let retries = 0; + + while (retries < maxRetries) { + try { + const vmInfo = await this.getVM(name, storage); + + if (vmInfo.ip && vmInfo.ip !== '') { + return vmInfo.ip; + } + + if (vmInfo.status === 'stopped' || vmInfo.status === 'error') { + throw new Error(`VM ${name} is in ${vmInfo.status} state`); + } + } catch (error) { + if (retries === maxRetries - 1) { + throw error; + } + } + + retries++; + await new Promise(resolve => setTimeout(resolve, retryDelay * 1000)); + } + + throw new Error(`Failed to get IP for VM ${name} after ${maxRetries} retries`); + } + + async updateVM( + name: string, + cpu?: number, + memory?: string, + storage?: string + ): Promise { + // Validate memory format if provided + if (memory) { + parseMemory(memory); // This will throw if invalid + } + + await lumeApiUpdate( + name, + cpu, + memory, + storage || this.storage, + this.host, + this.port, + this.verbose + ); + } +} diff --git a/libs/computer/typescript/src/providers/lume_api.ts b/libs/computer/typescript/src/providers/lume_api.ts new file mode 100644 index 00000000..c198583d --- /dev/null +++ b/libs/computer/typescript/src/providers/lume_api.ts @@ -0,0 +1,265 @@ +/** + * Lume API utilities for interacting with the Lume VM API. + * This module provides low-level API functions used by the Lume provider. + */ + +import { exec, execSync } from "child_process"; +import { promisify } from "util"; + +const execAsync = promisify(exec); + +export let HAS_CURL = false; + +// Check for curl availability +try { + execSync("which curl", { stdio: "ignore" }); + HAS_CURL = true; +} catch { + HAS_CURL = false; +} + +/** + * Parse memory string to bytes. + * Supports formats like "2GB", "512MB", "1024KB", etc. + * Defaults to 1GB + */ +export function parseMemory(memory = "1GB"): number { + const match = memory.match(/^(\d+(?:\.\d+)?)\s*([KMGT]?B?)$/i); + if (!match) { + throw new Error(`Invalid memory format: ${memory}`); + } + + const value = parseFloat(match[1]!); + const unit = match[2]!.toUpperCase(); + + const multipliers: Record = { + B: 1, + KB: 1024, + MB: 1024 * 1024, + GB: 1024 * 1024 * 1024, + TB: 1024 * 1024 * 1024 * 1024, + K: 1024, + M: 1024 * 1024, + G: 1024 * 1024 * 1024, + T: 1024 * 1024 * 1024 * 1024, + }; + + return Math.floor(value * (multipliers[unit] || 1)); +} + +/** + * Execute a curl command and return the result. + */ +async function executeCurl( + command: string +): Promise<{ stdout: string; stderr: string }> { + try { + const { stdout, stderr } = await execAsync(command); + return { stdout, stderr }; + } catch (error: any) { + throw new Error(`Curl command failed: ${error.message}`); + } +} + +/** + * Get VM information using Lume API. + */ +export async function lumeApiGet( + vmName: string = "", + storage?: string, + host: string = "localhost", + port: number = 7777, + debug: boolean = false +): Promise> { + let url = `http://${host}:${port}/vms`; + if (vmName) { + url += `/${encodeURIComponent(vmName)}`; + } + + const params = new URLSearchParams(); + if (storage) { + params.append("storage", storage); + } + + if (params.toString()) { + url += `?${params.toString()}`; + } + + const command = `curl -s -X GET "${url}"`; + + if (debug) { + console.log(`Executing: ${command}`); + } + + const { stdout } = await executeCurl(command); + + try { + return JSON.parse(stdout); + } catch (error) { + throw new Error(`Failed to parse API response: ${stdout}`); + } +} + +/** + * Options for running a VM using the Lume API. + */ +export interface LumeRunOptions { + /** CPU cores to allocate to the VM */ + cpu?: number; + /** Memory to allocate to the VM (e.g., "8GB", "512MB") */ + memory?: string; + /** Display configuration for the VM */ + display?: { + width?: number; + height?: number; + dpi?: number; + color_depth?: number; + }; + /** Environment variables to set in the VM */ + env?: Record; + /** Directories to share with the VM */ + shared_directories?: Record; + /** Network configuration */ + network?: { + type?: string; + bridge?: string; + nat?: boolean; + }; + /** Whether to run the VM in headless mode */ + headless?: boolean; + /** Whether to enable GPU acceleration */ + gpu?: boolean; + /** Storage location for the VM */ + storage?: string; + /** Custom VM configuration options */ + vm_options?: Record; + /** Additional provider-specific options */ + [key: string]: any; +} + +/** + * Run a VM using Lume API. + */ +export async function lumeApiRun( + image: string, + name: string, + runOpts: LumeRunOptions, + storage?: string, + host: string = "localhost", + port: number = 7777, + debug: boolean = false +): Promise> { + const url = `http://${host}:${port}/vms/run`; + + const body: LumeRunOptions = { + image, + name, + ...runOpts, + }; + + if (storage) { + body.storage = storage; + } + + const command = `curl -s -X POST "${url}" -H "Content-Type: application/json" -d '${JSON.stringify( + body + )}'`; + + if (debug) { + console.log(`Executing: ${command}`); + } + + const { stdout } = await executeCurl(command); + + try { + return JSON.parse(stdout); + } catch (error) { + throw new Error(`Failed to parse API response: ${stdout}`); + } +} + +/** + * Stop a VM using Lume API. + */ +export async function lumeApiStop( + vmName: string, + storage?: string, + host: string = "localhost", + port: number = 7777, + debug: boolean = false +): Promise { + const url = `http://${host}:${port}/vms/${encodeURIComponent(vmName)}/stop`; + + const params = new URLSearchParams(); + if (storage) { + params.append("storage", storage); + } + + const fullUrl = params.toString() ? `${url}?${params.toString()}` : url; + const command = `curl -s -X POST "${fullUrl}"`; + + if (debug) { + console.log(`Executing: ${command}`); + } + + await executeCurl(command); +} + +/** + * Update VM settings using Lume API. + */ +export async function lumeApiUpdate( + vmName: string, + cpu?: number, + memory?: string, + storage?: string, + host: string = "localhost", + port: number = 7777, + debug: boolean = false +): Promise { + const url = `http://${host}:${port}/vms/${encodeURIComponent(vmName)}/update`; + + const body: LumeRunOptions = {}; + if (cpu !== undefined) { + body.cpu = cpu; + } + if (memory !== undefined) { + body.memory = memory; + } + if (storage) { + body.storage = storage; + } + + const command = `curl -s -X POST "${url}" -H "Content-Type: application/json" -d '${JSON.stringify( + body + )}'`; + + if (debug) { + console.log(`Executing: ${command}`); + } + + await executeCurl(command); +} + +/** + * Pull a VM image using Lume API. + */ +export async function lumeApiPull( + image: string, + host: string = "localhost", + port: number = 7777, + debug: boolean = false +): Promise { + const url = `http://${host}:${port}/images/pull`; + + const body: LumeRunOptions = { image }; + const command = `curl -s -X POST "${url}" -H "Content-Type: application/json" -d '${JSON.stringify( + body + )}'`; + + if (debug) { + console.log(`Executing: ${command}`); + } + + await executeCurl(command); +} diff --git a/libs/computer/typescript/src/providers/lumier/index.ts b/libs/computer/typescript/src/providers/lumier/index.ts new file mode 100644 index 00000000..33345619 --- /dev/null +++ b/libs/computer/typescript/src/providers/lumier/index.ts @@ -0,0 +1,16 @@ +/** + * Lumier VM provider implementation. + */ + +export let HAS_LUMIER = false; + +try { + // Check if Docker is available + const { execSync } = require("child_process"); + execSync("which docker", { stdio: "ignore" }); + HAS_LUMIER = true; +} catch { + HAS_LUMIER = false; +} + +export { LumierProvider } from "./provider"; diff --git a/libs/computer/typescript/src/providers/lumier/provider.ts b/libs/computer/typescript/src/providers/lumier/provider.ts new file mode 100644 index 00000000..3581b1fd --- /dev/null +++ b/libs/computer/typescript/src/providers/lumier/provider.ts @@ -0,0 +1,401 @@ +/** + * Lumier VM provider implementation. + * + * This provider uses Docker containers running the Lumier image to create + * macOS and Linux VMs. It handles VM lifecycle operations through Docker + * commands and container management. + */ + +import { exec } from "child_process"; +import { promisify } from "util"; +import { BaseVMProviderImpl, VMProviderType } from "../base"; +import { + lumeApiGet, + lumeApiRun, + lumeApiStop, + lumeApiUpdate, +} from "../lume_api"; + +const execAsync = promisify(exec); + +export interface LumierProviderOptions { + port?: number; + host?: string; + storage?: string; + sharedPath?: string; + image?: string; + verbose?: boolean; + ephemeral?: boolean; + noVNCPort?: number; +} + +export class LumierProvider extends BaseVMProviderImpl { + readonly providerType = VMProviderType.LUMIER; + + private host: string; + private apiPort: number; + private vncPort?: number; + private ephemeral: boolean; + private storage?: string; + private sharedPath?: string; + private image: string; + private verbose: boolean; + private containerName?: string; + private containerId?: string; + + constructor(options: LumierProviderOptions = {}) { + super(); + + this.host = options.host || "localhost"; + this.apiPort = options.port || 7777; + this.vncPort = options.noVNCPort; + this.ephemeral = options.ephemeral || false; + + // Handle ephemeral storage + if (this.ephemeral) { + this.storage = "ephemeral"; + } else { + this.storage = options.storage; + } + + this.sharedPath = options.sharedPath; + this.image = options.image || "macos-sequoia-cua:latest"; + this.verbose = options.verbose || false; + } + + /** + * Parse memory string to MB integer. + */ + private parseMemory(memoryStr: string | number): number { + if (typeof memoryStr === "number") { + return memoryStr; + } + + const match = memoryStr.match(/^(\d+)([A-Za-z]*)$/); + if (match) { + const value = parseInt(match[1] || "0"); + const unit = match[2]?.toUpperCase() || ""; + + if (unit === "GB" || unit === "G") { + return value * 1024; + } else if (unit === "MB" || unit === "M" || unit === "") { + return value; + } + } + + console.warn( + `Could not parse memory string '${memoryStr}', using 8GB default` + ); + return 8192; // Default to 8GB + } + + /** + * Check if a Docker container exists. + */ + private async containerExists(name: string): Promise { + try { + await execAsync(`docker inspect ${name}`); + return true; + } catch { + return false; + } + } + + /** + * Get container status. + */ + private async getContainerStatus(name: string): Promise { + try { + const { stdout } = await execAsync( + `docker inspect -f '{{.State.Status}}' ${name}` + ); + return stdout.trim(); + } catch { + return "not_found"; + } + } + + /** + * Start the Lumier container. + */ + private async startContainer( + name: string, + cpu: number = 4, + memory: string = "8GB", + runOpts: Record = {} + ): Promise { + const memoryMB = this.parseMemory(memory); + + // Build Docker run command + let dockerCmd = `docker run -d --name ${name}`; + + // Add resource limits + dockerCmd += ` --cpus=${cpu}`; + dockerCmd += ` --memory=${memoryMB}m`; + + // Add port mappings + dockerCmd += ` -p ${this.apiPort}:7777`; + if (this.vncPort) { + dockerCmd += ` -p ${this.vncPort}:8006`; + } + + // Add storage volume if not ephemeral + if (this.storage && this.storage !== "ephemeral") { + dockerCmd += ` -v ${this.storage}:/storage`; + } + + // Add shared path if specified + if (this.sharedPath) { + dockerCmd += ` -v ${this.sharedPath}:/shared`; + } + + // Add environment variables + if (runOpts.env) { + for (const [key, value] of Object.entries(runOpts.env)) { + dockerCmd += ` -e ${key}=${value}`; + } + } + + // Add the image + dockerCmd += ` ${this.image}`; + + if (this.verbose) { + console.log(`Starting container with command: ${dockerCmd}`); + } + + try { + const { stdout } = await execAsync(dockerCmd); + this.containerId = stdout.trim(); + this.containerName = name; + } catch (error: any) { + throw new Error(`Failed to start container: ${error.message}`); + } + } + + /** + * Wait for the API to be ready. + */ + private async waitForAPI(maxRetries: number = 30): Promise { + for (let i = 0; i < maxRetries; i++) { + try { + await lumeApiGet("", undefined, this.host, this.apiPort, false); + return; + } catch { + if (i === maxRetries - 1) { + throw new Error("API failed to become ready"); + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } + } + + async getVM(name: string, storage?: string): Promise> { + // First check if container exists + const containerStatus = await this.getContainerStatus( + this.containerName || name + ); + + if (containerStatus === "not_found") { + throw new Error(`Container ${name} not found`); + } + + // If container is not running, return basic status + if (containerStatus !== "running") { + return { + name, + status: containerStatus, + ip: "", + cpu: 0, + memory: "0MB", + }; + } + + // Get VM info from API + return lumeApiGet( + name, + storage || this.storage, + this.host, + this.apiPort, + this.verbose + ); + } + + async listVMs(): Promise>> { + // Check if our container is running + if (!this.containerName) { + return []; + } + + const containerStatus = await this.getContainerStatus(this.containerName); + if (containerStatus !== "running") { + return []; + } + + // Get VMs from API + const response = await lumeApiGet( + "", + this.storage, + this.host, + this.apiPort, + this.verbose + ); + + if (Array.isArray(response)) { + return response; + } + + if (response.vms && Array.isArray(response.vms)) { + return response.vms; + } + + return []; + } + + async runVM( + image: string, + name: string, + runOpts: Record, + storage?: string + ): Promise> { + // Check if container already exists + const exists = await this.containerExists(name); + + if (exists) { + const status = await this.getContainerStatus(name); + if (status === "running") { + // Container already running, just run VM through API + return lumeApiRun( + image, + name, + runOpts, + storage || this.storage, + this.host, + this.apiPort, + this.verbose + ); + } else { + // Start existing container + await execAsync(`docker start ${name}`); + } + } else { + // Create and start new container + await this.startContainer( + name, + runOpts.cpu || 4, + runOpts.memory || "8GB", + runOpts + ); + } + + // Wait for API to be ready + await this.waitForAPI(); + + // Run VM through API + return lumeApiRun( + image, + name, + runOpts, + storage || this.storage, + this.host, + this.apiPort, + this.verbose + ); + } + + async stopVM(name: string, storage?: string): Promise { + // First stop VM through API + try { + await lumeApiStop( + name, + storage || this.storage, + this.host, + this.apiPort, + this.verbose + ); + } catch (error) { + if (this.verbose) { + console.log(`Failed to stop VM through API: ${error}`); + } + } + + // Stop the container + if (this.containerName) { + try { + await execAsync(`docker stop ${this.containerName}`); + + // Remove container if ephemeral + if (this.ephemeral) { + await execAsync(`docker rm ${this.containerName}`); + if (this.verbose) { + console.log( + `Container ${this.containerName} stopped and removed (ephemeral mode)` + ); + } + } + } catch (error: any) { + throw new Error(`Failed to stop container: ${error.message}`); + } + } + } + + async getIP( + name: string, + storage?: string, + retryDelay: number = 1 + ): Promise { + const maxRetries = 30; + + for (let i = 0; i < maxRetries; i++) { + try { + const vmInfo = await this.getVM(name, storage); + + if (vmInfo.ip && vmInfo.ip !== "") { + return vmInfo.ip; + } + + if (vmInfo.status === "stopped" || vmInfo.status === "error") { + throw new Error(`VM ${name} is in ${vmInfo.status} state`); + } + } catch (error) { + if (i === maxRetries - 1) { + throw error; + } + } + + await new Promise((resolve) => setTimeout(resolve, retryDelay * 1000)); + } + + throw new Error( + `Failed to get IP for VM ${name} after ${maxRetries} retries` + ); + } + + async updateVM( + name: string, + cpu?: number, + memory?: string, + storage?: string + ): Promise { + await lumeApiUpdate( + name, + cpu, + memory, + storage || this.storage, + this.host, + this.apiPort, + this.verbose + ); + } + + async __aexit__(excType: any, excVal: any, excTb: any): Promise { + // Clean up container if ephemeral + if (this.ephemeral && this.containerName) { + try { + await execAsync(`docker stop ${this.containerName}`); + await execAsync(`docker rm ${this.containerName}`); + } catch { + // Ignore errors during cleanup + } + } + } +} diff --git a/libs/computer/typescript/src/telemetry.ts b/libs/computer/typescript/src/telemetry.ts new file mode 100644 index 00000000..7f598721 --- /dev/null +++ b/libs/computer/typescript/src/telemetry.ts @@ -0,0 +1,115 @@ +/** + * Telemetry tracking for Computer usage. + */ + +interface TelemetryEvent { + event: string; + timestamp: Date; + properties?: Record; +} + +export class TelemetryManager { + private enabled: boolean = true; + private events: TelemetryEvent[] = []; + + setEnabled(enabled: boolean): void { + this.enabled = enabled; + } + + isEnabled(): boolean { + return this.enabled; + } + + track(event: string, properties?: Record): void { + if (!this.enabled) { + return; + } + + const telemetryEvent: TelemetryEvent = { + event, + timestamp: new Date(), + properties, + }; + + this.events.push(telemetryEvent); + + // In a real implementation, this would send to a telemetry service + // For now, just log to debug + if (process.env.NODE_ENV === "development") { + console.debug("[Telemetry]", event, properties); + } + } + + getEvents(): TelemetryEvent[] { + return [...this.events]; + } + + clear(): void { + this.events = []; + } +} + +// Singleton instance +const telemetryManager = new TelemetryManager(); + +/** + * Record computer initialization event + */ +export function recordComputerInitialization(): void { + telemetryManager.track("computer_initialized", { + timestamp: new Date().toISOString(), + version: process.env.npm_package_version || "unknown", + }); +} + +/** + * Record VM start event + */ +export function recordVMStart(vmName: string, provider: string): void { + telemetryManager.track("vm_started", { + vm_name: vmName, + provider, + timestamp: new Date().toISOString(), + }); +} + +/** + * Record VM stop event + */ +export function recordVMStop(vmName: string, duration: number): void { + telemetryManager.track("vm_stopped", { + vm_name: vmName, + duration_ms: duration, + timestamp: new Date().toISOString(), + }); +} + +/** + * Record interface action + */ +export function recordInterfaceAction( + action: string, + details?: Record +): void { + telemetryManager.track("interface_action", { + action, + ...details, + timestamp: new Date().toISOString(), + }); +} + +/** + * Set telemetry enabled/disabled + */ +export function setTelemetryEnabled(enabled: boolean): void { + telemetryManager.setEnabled(enabled); +} + +/** + * Check if telemetry is enabled + */ +export function isTelemetryEnabled(): boolean { + return telemetryManager.isEnabled(); +} + +export { telemetryManager }; diff --git a/libs/computer/typescript/src/utils.ts b/libs/computer/typescript/src/utils.ts new file mode 100644 index 00000000..fe1d398f --- /dev/null +++ b/libs/computer/typescript/src/utils.ts @@ -0,0 +1,118 @@ +/** + * Utility functions for the Computer module. + */ + +/** + * Sleep for a specified number of milliseconds + * @param ms Number of milliseconds to sleep + */ +export function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +/** + * Parse a display string in format "WIDTHxHEIGHT" + * @param display Display string to parse + * @returns Object with width and height + */ +export function parseDisplayString(display: string): { + width: number; + height: number; +} { + const match = display.match(/^(\d+)x(\d+)$/); + if (!match || !match[1] || !match[2]) { + throw new Error( + "Display string must be in format 'WIDTHxHEIGHT' (e.g. '1024x768')" + ); + } + return { + width: parseInt(match[1], 10), + height: parseInt(match[2], 10), + }; +} + +/** + * Validate image format (should be in format "image:tag") + * @param image Image string to validate + * @returns Object with image name and tag + */ +export function parseImageString(image: string): { name: string; tag: string } { + const parts = image.split(":"); + if (parts.length !== 2 || !parts[0] || !parts[1]) { + throw new Error("Image must be in the format :"); + } + return { + name: parts[0], + tag: parts[1], + }; +} + +/** + * Convert bytes to human-readable format + * @param bytes Number of bytes + * @returns Human-readable string + */ +export function formatBytes(bytes: number): string { + const units = ["B", "KB", "MB", "GB", "TB"]; + let size = bytes; + let unitIndex = 0; + + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024; + unitIndex++; + } + + return `${size.toFixed(2)} ${units[unitIndex]}`; +} + +/** + * Parse memory string (e.g., "8GB") to bytes + * @param memory Memory string + * @returns Number of bytes + */ +export function parseMemoryString(memory: string): number { + const match = memory.match(/^(\d+)(B|KB|MB|GB|TB)?$/i); + if (!match || !match[1] || !match[2]) { + throw new Error("Invalid memory format. Use format like '8GB' or '1024MB'"); + } + + const value = parseInt(match[1], 10); + const unit = match[2].toUpperCase(); + + const multipliers: Record = { + B: 1, + KB: 1024, + MB: 1024 * 1024, + GB: 1024 * 1024 * 1024, + TB: 1024 * 1024 * 1024 * 1024, + }; + + return value * (multipliers[unit] || 1); +} + +/** + * Create a timeout promise that rejects after specified milliseconds + * @param ms Timeout in milliseconds + * @param message Optional error message + */ +export function timeout(ms: number, message?: string): Promise { + return new Promise((_, reject) => { + setTimeout(() => { + reject(new Error(message || `Timeout after ${ms}ms`)); + }, ms); + }); +} + +/** + * Race a promise against a timeout + * @param promise The promise to race + * @param ms Timeout in milliseconds + * @param message Optional timeout error message + */ +export async function withTimeout( + promise: Promise, + ms: number, + message?: string +): Promise { + return Promise.race([promise, timeout(ms, message)]); +} diff --git a/libs/computer/typescript/tsconfig.json b/libs/computer/typescript/tsconfig.json new file mode 100644 index 00000000..cdcd74de --- /dev/null +++ b/libs/computer/typescript/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["es2023"], + "moduleDetection": "force", + "module": "preserve", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "types": ["node"], + "allowSyntheticDefaultImports": true, + "strict": true, + "noUnusedLocals": true, + "declaration": true, + "emitDeclarationOnly": true, + "esModuleInterop": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/libs/computer/typescript/tsdown.config.ts b/libs/computer/typescript/tsdown.config.ts new file mode 100644 index 00000000..19e6e5b9 --- /dev/null +++ b/libs/computer/typescript/tsdown.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig([ + { + entry: ["./src/index.ts"], + platform: "node", + dts: true, + external: ["child_process", "util"], + }, +]); diff --git a/libs/computer/typescript/vitest.config.ts b/libs/computer/typescript/vitest.config.ts new file mode 100644 index 00000000..abed6b21 --- /dev/null +++ b/libs/computer/typescript/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({})