diff --git a/README.md b/README.md index f4d42e6..9bf16ee 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ create-kernel-app [app-name] [options] - `browser-use`: Template with Browser Use SDK (Python only) - `stagehand`: Template with Stagehand SDK (Typescript only) - `advanced-sample`: Implements sample apps using advanced Kernel configs - - `computer-use`: Implements a prompt loop using Anthropic Computer Use + - `claude-cu`: Implements a prompt loop using Anthropic Computer Use - `cua`: Implements a Computer Use Agent (OpenAI CUA) sample ### Examples @@ -61,9 +61,9 @@ Create a Typescript application with Stagehand template: npx @onkernel/create-kernel-app my-app --language typescript --template stagehand ``` -Create a Typescript application with Computer Use template: +Create a Typescript application with Claude Computer Use template: ```bash -npx @onkernel/create-kernel-app my-app --language typescript --template computer-use +npx @onkernel/create-kernel-app my-app --language typescript --template claude-cu ``` Create a Python application with a sample app: @@ -75,6 +75,10 @@ Create a Python application with Browser Use template: ```bash npx @onkernel/create-kernel-app my-app --language python --template browser-use ``` + +Create a Python application with Claude Computer Use template: +```bash +npx @onkernel/create-kernel-app my-app --language python --template claude-cu ``` ## Next Steps @@ -98,10 +102,10 @@ export KERNEL_API_KEY= 4. Deploy your application: ```bash # Typscript -kernel deploy index.ts # --env OPENAI_API_KEY=XXX if Stagehand; --env ANTHROPIC_API_KEY=XXX if Computer Use +kernel deploy index.ts # --env OPENAI_API_KEY=XXX if Stagehand; --env ANTHROPIC_API_KEY=XXX if Claude Computer Use # Python -kernel deploy main.py # --env OPENAI_API_KEY=XXX if Browser Use +kernel deploy main.py # --env OPENAI_API_KEY=XXX if Browser Use; --env ANTHROPIC_API_KEY=XXX if Claude Computer Use ``` If deploying an app that requires environment variables, make sure to [set them](https://docs.onkernel.com/launch/deploy#environment-variables) when you `deploy`. @@ -114,8 +118,8 @@ kernel invoke ts-basic get-page-title --payload '{"url": "https://www.google.com # Typescript + Stagehand kernel invoke ts-stagehand stagehand-task --payload '{"query": "Best wired earbuds"}' -# Typescript + Computer Use -kernel invoke ts-cu cu-task --payload '{"query": "Search for the top 3 restaurants in NYC according to Pete Wells"}' +# Typescript + Claude Computer Use +kernel invoke ts-claude-cu computer-use-query --payload '{"query": "Search for the top 3 restaurants in NYC according to Pete Wells"}' # Python + Sample App kernel invoke python-basic get-page-title --payload '{"url": "https://www.google.com"}' @@ -123,6 +127,9 @@ kernel invoke python-basic get-page-title --payload '{"url": "https://www.google # Python + Browser Use kernel invoke python-bu bu-task --payload '{"task": "Compare the price of gpt-4o and DeepSeek-V3"}' +# Python + Claude Computer Use +kernel invoke python-claude-cu cu-task --payload '{"query": "Return the first url of a search result for NYC restaurant reviews Pete Wells"}' + # Typescript + CUA Sample kernel invoke ts-cua cua-task --payload '{"task": "Go to https://news.ycombinator.com and get the top 5 articles"}' @@ -140,7 +147,7 @@ These are the sample apps currently available when you run `npx @onkernel/create | **browser-use** | Completes a specified task | Browser Use | `{ task }` | | **stagehand** | Returns the first result of a specified Google search | Stagehand | `{ query }` | | **advanced-sample** | Implements sample apps using advanced Kernel configs | n/a | -| **computer-use** | Implements a prompt loop | Anthropic Computer Use API | `{ query }` | +| **claude-cu** | Implements a prompt loop | Anthropic Computer Use API | `{ query }` | | **cua** | Implements the OpenAI Computer Using Agent (CUA) | OpenAI CUA | `{ task }` | ## Documentation diff --git a/index.ts b/index.ts index 1eaed85..d3c872f 100644 --- a/index.ts +++ b/index.ts @@ -18,7 +18,7 @@ type TemplateKey = | "browser-use" | "stagehand" | "advanced-sample" - | "computer-use" + | "claude-cu" | "cua"; type LanguageInfo = { name: string; shorthand: string }; type TemplateInfo = { @@ -34,7 +34,8 @@ const TEMPLATE_SAMPLE_APP = "sample-app"; const TEMPLATE_BROWSER_USE = "browser-use"; const TEMPLATE_STAGEHAND = "stagehand"; const TEMPLATE_ADVANCED_SAMPLE = "advanced-sample"; -const TEMPLATE_COMPUTER_USE = "computer-use"; +const TEMPLATE_COMPUTER_USE = "claude-cu"; +const TEMPLATE_ANTHROPIC_COMPUTER_USE = "claude-cu"; const TEMPLATE_CUA = "cua"; const LANGUAGE_SHORTHAND_TS = "ts"; const LANGUAGE_SHORTHAND_PY = "py"; @@ -70,8 +71,8 @@ const TEMPLATES: Record = { "Implements sample actions with advanced Kernel configs", languages: [LANGUAGE_TYPESCRIPT, LANGUAGE_PYTHON], }, - [TEMPLATE_COMPUTER_USE]: { - name: "Computer Use", + ["claude-cu"]: { + name: "Claude Computer Use", description: "Implements the Anthropic Computer Use SDK", languages: [LANGUAGE_TYPESCRIPT, LANGUAGE_PYTHON], }, @@ -93,8 +94,8 @@ const INVOKE_SAMPLES: Record< 'kernel invoke ts-stagehand stagehand-task --payload \'{"query": "Best wired earbuds"}\'', [TEMPLATE_ADVANCED_SAMPLE]: 'kernel invoke ts-advanced test-captcha-solver', - [TEMPLATE_COMPUTER_USE]: - 'kernel invoke ts-cu cu-task --payload \'{"query": "Return the first url of a search result for NYC restaurant reviews Pete Wells"}\'', + ["claude-cu"]: + 'kernel invoke ts-claude-cu computer-use-query --payload \'{"query": "Search for the top 3 restaurants in NYC according to Pete Wells"}\'', [TEMPLATE_CUA]: 'kernel invoke ts-cua cua-task --payload \'{"query": "Go to https://news.ycombinator.com and get the top 5 articles"}\'', }, @@ -105,8 +106,8 @@ const INVOKE_SAMPLES: Record< 'kernel invoke python-bu bu-task --payload \'{"task": "Compare the price of gpt-4o and DeepSeek-V3"}\'', [TEMPLATE_ADVANCED_SAMPLE]: 'kernel invoke python-advanced test-captcha-solver', - [TEMPLATE_COMPUTER_USE]: - 'kernel invoke python-cu cu-task --payload \'{"query": "Return the first url of a search result for NYC restaurant reviews Pete Wells"}\'', + ["claude-cu"]: + 'kernel invoke python-claude-cu cu-task --payload \'{"query": "Return the first url of a search result for NYC restaurant reviews Pete Wells"}\'', [TEMPLATE_CUA]: 'kernel invoke python-cua cua-task --payload \'{"query": "Go to https://news.ycombinator.com and get the top 5 articles"}\'', }, @@ -123,8 +124,8 @@ const REGISTERED_APP_NAMES: Record< 'ts-stagehand', [TEMPLATE_ADVANCED_SAMPLE]: 'ts-advanced', - [TEMPLATE_COMPUTER_USE]: - 'ts-cu', + ["claude-cu"]: + 'ts-claude-cu', [TEMPLATE_CUA]: 'ts-cua', }, @@ -135,8 +136,8 @@ const REGISTERED_APP_NAMES: Record< 'python-bu', [TEMPLATE_ADVANCED_SAMPLE]: 'python-advanced', - [TEMPLATE_COMPUTER_USE]: - 'python-cu', + ["claude-cu"]: + 'python-claude-cu', [TEMPLATE_CUA]: 'python-cua', }, diff --git a/templates/python/computer-use/README.md b/templates/python/claude-cu/README.md similarity index 100% rename from templates/python/computer-use/README.md rename to templates/python/claude-cu/README.md diff --git a/templates/python/computer-use/_gitignore b/templates/python/claude-cu/_gitignore similarity index 100% rename from templates/python/computer-use/_gitignore rename to templates/python/claude-cu/_gitignore diff --git a/templates/python/computer-use/loop.py b/templates/python/claude-cu/loop.py similarity index 100% rename from templates/python/computer-use/loop.py rename to templates/python/claude-cu/loop.py diff --git a/templates/python/computer-use/main.py b/templates/python/claude-cu/main.py similarity index 98% rename from templates/python/computer-use/main.py rename to templates/python/claude-cu/main.py index 10a1b45..def0f17 100644 --- a/templates/python/computer-use/main.py +++ b/templates/python/claude-cu/main.py @@ -20,7 +20,7 @@ class QueryOutput(TypedDict): raise ValueError("ANTHROPIC_API_KEY is not set") client = Kernel() -app = kernel.App("python-cu") +app = kernel.App("python-claude-cu") @app.action("cu-task") async def cu_task( diff --git a/templates/python/computer-use/pyproject.toml b/templates/python/claude-cu/pyproject.toml similarity index 100% rename from templates/python/computer-use/pyproject.toml rename to templates/python/claude-cu/pyproject.toml diff --git a/templates/python/computer-use/tools/__init__.py b/templates/python/claude-cu/tools/__init__.py similarity index 100% rename from templates/python/computer-use/tools/__init__.py rename to templates/python/claude-cu/tools/__init__.py diff --git a/templates/python/computer-use/tools/base.py b/templates/python/claude-cu/tools/base.py similarity index 100% rename from templates/python/computer-use/tools/base.py rename to templates/python/claude-cu/tools/base.py diff --git a/templates/python/computer-use/tools/collection.py b/templates/python/claude-cu/tools/collection.py similarity index 100% rename from templates/python/computer-use/tools/collection.py rename to templates/python/claude-cu/tools/collection.py diff --git a/templates/python/computer-use/tools/computer.py b/templates/python/claude-cu/tools/computer.py similarity index 100% rename from templates/python/computer-use/tools/computer.py rename to templates/python/claude-cu/tools/computer.py diff --git a/templates/python/computer-use/tools/groups.py b/templates/python/claude-cu/tools/groups.py similarity index 100% rename from templates/python/computer-use/tools/groups.py rename to templates/python/claude-cu/tools/groups.py diff --git a/templates/python/computer-use/uv.lock b/templates/python/claude-cu/uv.lock similarity index 100% rename from templates/python/computer-use/uv.lock rename to templates/python/claude-cu/uv.lock diff --git a/templates/typescript/claude-cu/.prettierrc b/templates/typescript/claude-cu/.prettierrc new file mode 100644 index 0000000..d485a2a --- /dev/null +++ b/templates/typescript/claude-cu/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true, + "arrowParens": "avoid" +} diff --git a/templates/typescript/claude-cu/README.md b/templates/typescript/claude-cu/README.md new file mode 100644 index 0000000..0631bbd --- /dev/null +++ b/templates/typescript/claude-cu/README.md @@ -0,0 +1,3 @@ +# Anthropic Computer Use Sample + +This sample app demonstrates how to use the `@onkernel/cu-playwright` package to perform a simple search query. diff --git a/templates/typescript/claude-cu/_gitignore b/templates/typescript/claude-cu/_gitignore new file mode 100644 index 0000000..6394993 --- /dev/null +++ b/templates/typescript/claude-cu/_gitignore @@ -0,0 +1,17 @@ +# Node +node_modules +dist +.DS_Store +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor +.vscode +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? \ No newline at end of file diff --git a/templates/typescript/claude-cu/eslint.config.js b/templates/typescript/claude-cu/eslint.config.js new file mode 100644 index 0000000..c0aa404 --- /dev/null +++ b/templates/typescript/claude-cu/eslint.config.js @@ -0,0 +1,38 @@ +import eslint from '@eslint/js'; +import tseslint from '@typescript-eslint/eslint-plugin'; +import tsparser from '@typescript-eslint/parser'; +import prettierConfig from 'eslint-config-prettier'; + +export default [ + eslint.configs.recommended, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parser: tsparser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + globals: { + process: 'readonly', + console: 'readonly', + Buffer: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + }, + }, + plugins: { + '@typescript-eslint': tseslint, + }, + rules: { + ...tseslint.configs.recommended.rules, + '@typescript-eslint/no-unused-vars': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + 'no-console': 'off', + }, + }, + prettierConfig, + { + ignores: ['node_modules/**', 'dist/**'], + }, +]; diff --git a/templates/typescript/claude-cu/index.ts b/templates/typescript/claude-cu/index.ts new file mode 100644 index 0000000..56525a4 --- /dev/null +++ b/templates/typescript/claude-cu/index.ts @@ -0,0 +1,59 @@ +import 'dotenv/config'; +import { Kernel, type KernelContext } from '@onkernel/sdk'; +import { chromium } from 'playwright'; +import { ComputerUseAgent } from '@onkernel/cu-playwright'; + +const kernel = new Kernel(); + +const app = kernel.app('ts-claude-cu'); + +interface QueryInput { + query: string; +} + +interface QueryOutput { + result: string; +} + +app.action( + 'computer-use-query', + async (ctx: KernelContext, payload?: QueryInput): Promise => { + const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY; + + if (!ANTHROPIC_API_KEY) { + throw new Error('ANTHROPIC_API_KEY is not set'); + } + + if (!payload?.query) { + throw new Error('Query is required'); + } + + const kernelBrowser = await kernel.browsers.create({ + invocation_id: ctx.invocation_id, + }); + + const browser = await chromium.connectOverCDP(kernelBrowser.cdp_ws_url); + + try { + const context = browser.contexts()[0]; + if (!context) { + throw new Error('No browser context found.'); + } + const page = context.pages()[0]; + if (!page) { + throw new Error('No page found in browser context.'); + } + + const agent = new ComputerUseAgent({ + apiKey: ANTHROPIC_API_KEY, + page, + }); + + const result = await agent.execute(payload.query); + + return { result }; + } finally { + await browser.close(); + } + } +); diff --git a/templates/typescript/claude-cu/package.json b/templates/typescript/claude-cu/package.json new file mode 100644 index 0000000..f3c8eba --- /dev/null +++ b/templates/typescript/claude-cu/package.json @@ -0,0 +1,30 @@ +{ + "name": "ts-claude-cu", + "module": "index.ts", + "type": "module", + "private": true, + "scripts": { + "format": "prettier --write .", + "lint": "eslint .", + "lint:fix": "eslint . --fix" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "@onkernel/cu-playwright": "^0.1.0", + "@onkernel/sdk": "^0.6.0", + "dotenv": "^16.6.0", + "playwright": "^1.52.0", + "zod": "^3.25.0" + }, + "devDependencies": { + "@eslint/js": "^9.27.0", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.5", + "prettier": "^3.0.0", + "typescript": "^5.0.0" + } +} diff --git a/templates/typescript/claude-cu/pnpm-lock.yaml b/templates/typescript/claude-cu/pnpm-lock.yaml new file mode 100644 index 0000000..479cb05 --- /dev/null +++ b/templates/typescript/claude-cu/pnpm-lock.yaml @@ -0,0 +1,1527 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + '@onkernel/cu-playwright': + specifier: ^0.1.0 + version: 0.1.1(playwright@1.53.1)(typescript@5.8.3) + '@onkernel/sdk': + specifier: ^0.6.0 + version: 0.6.3 + dotenv: + specifier: ^16.6.0 + version: 16.6.0 + playwright: + specifier: ^1.52.0 + version: 1.53.1 + zod: + specifier: ^3.25.0 + version: 3.25.67 + devDependencies: + '@eslint/js': + specifier: ^9.27.0 + version: 9.29.0 + '@typescript-eslint/eslint-plugin': + specifier: ^8.32.1 + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/parser': + specifier: ^8.32.1 + version: 8.35.0(eslint@9.29.0)(typescript@5.8.3) + eslint: + specifier: ^9.27.0 + version: 9.29.0 + eslint-config-prettier: + specifier: ^10.1.5 + version: 10.1.5(eslint@9.29.0) + prettier: + specifier: ^3.0.0 + version: 3.6.2 + typescript: + specifier: ^5.0.0 + version: 5.8.3 + +packages: + '@anthropic-ai/sdk@0.52.0': + resolution: + { + integrity: sha512-d4c+fg+xy9e46c8+YnrrgIQR45CZlAi7PwdzIfDXDM6ACxEZli1/fxhURsq30ZpMZy6LvSkr41jGq5aF5TD7rQ==, + } + hasBin: true + + '@eslint-community/eslint-utils@4.7.0': + resolution: + { + integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: + { + integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==, + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + '@eslint/config-array@0.20.1': + resolution: + { + integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/config-helpers@0.2.3': + resolution: + { + integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/core@0.14.0': + resolution: + { + integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/core@0.15.1': + resolution: + { + integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/eslintrc@3.3.1': + resolution: + { + integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/js@9.29.0': + resolution: + { + integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/object-schema@2.1.6': + resolution: + { + integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/plugin-kit@0.3.3': + resolution: + { + integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@humanfs/core@0.19.1': + resolution: + { + integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, + } + engines: { node: '>=18.18.0' } + + '@humanfs/node@0.16.6': + resolution: + { + integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==, + } + engines: { node: '>=18.18.0' } + + '@humanwhocodes/module-importer@1.0.1': + resolution: + { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, + } + engines: { node: '>=12.22' } + + '@humanwhocodes/retry@0.3.1': + resolution: + { + integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==, + } + engines: { node: '>=18.18' } + + '@humanwhocodes/retry@0.4.3': + resolution: + { + integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, + } + engines: { node: '>=18.18' } + + '@nodelib/fs.scandir@2.1.5': + resolution: + { + integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, + } + engines: { node: '>= 8' } + + '@nodelib/fs.stat@2.0.5': + resolution: + { + integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, + } + engines: { node: '>= 8' } + + '@nodelib/fs.walk@1.2.8': + resolution: + { + integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, + } + engines: { node: '>= 8' } + + '@onkernel/cu-playwright@0.1.1': + resolution: + { + integrity: sha512-BSjeU49FW0gDl7NbV/OtwzN8fFwUGAEr3nMKoRy5k875fZnx/CNlqkmB+meFs7JmT87EcVhfd419zjr3Qk+YAQ==, + } + peerDependencies: + playwright: ^1.52.0 + typescript: ^5 + + '@onkernel/sdk@0.6.3': + resolution: + { + integrity: sha512-6F6SyuP0HmNRVr2v0F34Fsk2whgUdzYxpJc+u38Ua+9rmQ1RBpeWh85g1j7zGjZKPcpOGIK+tmjsJ2H0SSuo2A==, + } + + '@types/estree@1.0.8': + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, + } + + '@types/json-schema@7.0.15': + resolution: + { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, + } + + '@typescript-eslint/eslint-plugin@8.35.0': + resolution: + { + integrity: sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + '@typescript-eslint/parser': ^8.35.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.35.0': + resolution: + { + integrity: sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.35.0': + resolution: + { + integrity: sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.35.0': + resolution: + { + integrity: sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/tsconfig-utils@8.35.0': + resolution: + { + integrity: sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.35.0': + resolution: + { + integrity: sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.35.0': + resolution: + { + integrity: sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/typescript-estree@8.35.0': + resolution: + { + integrity: sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.35.0': + resolution: + { + integrity: sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.35.0': + resolution: + { + integrity: sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + acorn-jsx@5.3.2: + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: + { + integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==, + } + engines: { node: '>=0.4.0' } + hasBin: true + + ajv@6.12.6: + resolution: + { + integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, + } + + ansi-styles@4.3.0: + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: '>=8' } + + argparse@2.0.1: + resolution: + { + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, + } + + balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } + + brace-expansion@1.1.12: + resolution: + { + integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, + } + + brace-expansion@2.0.2: + resolution: + { + integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==, + } + + braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: '>=8' } + + callsites@3.1.0: + resolution: + { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, + } + engines: { node: '>=6' } + + chalk@4.1.2: + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: '>=10' } + + 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==, + } + + concat-map@0.0.1: + resolution: + { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, + } + + cross-spawn@7.0.6: + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: '>= 8' } + + debug@4.4.1: + resolution: + { + integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==, + } + engines: { node: '>=6.0' } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: + { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, + } + + dotenv@16.6.0: + resolution: + { + integrity: sha512-Omf1L8paOy2VJhILjyhrhqwLIdstqm1BvcDPKg4NGAlkwEu9ODyrFbvk8UymUOMCT+HXo31jg1lArIrVAAhuGA==, + } + engines: { node: '>=12' } + + escape-string-regexp@4.0.0: + resolution: + { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, + } + engines: { node: '>=10' } + + eslint-config-prettier@10.1.5: + resolution: + { + integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==, + } + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-scope@8.4.0: + resolution: + { + integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@3.4.3: + resolution: + { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@4.2.1: + resolution: + { + integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint@9.29.0: + resolution: + { + integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: + { + integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + esquery@1.6.0: + resolution: + { + integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==, + } + engines: { node: '>=0.10' } + + esrecurse@4.3.0: + resolution: + { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, + } + engines: { node: '>=4.0' } + + estraverse@5.3.0: + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, + } + engines: { node: '>=4.0' } + + esutils@2.0.3: + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, + } + engines: { node: '>=0.10.0' } + + fast-deep-equal@3.1.3: + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, + } + + fast-glob@3.3.3: + resolution: + { + integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, + } + engines: { node: '>=8.6.0' } + + fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, + } + + fast-levenshtein@2.0.6: + resolution: + { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, + } + + fastq@1.19.1: + resolution: + { + integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==, + } + + file-entry-cache@8.0.0: + resolution: + { + integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, + } + engines: { node: '>=16.0.0' } + + fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: '>=8' } + + find-up@5.0.0: + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, + } + engines: { node: '>=10' } + + flat-cache@4.0.1: + resolution: + { + integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, + } + engines: { node: '>=16' } + + flatted@3.3.3: + resolution: + { + integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==, + } + + fsevents@2.3.2: + resolution: + { + integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + glob-parent@5.1.2: + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, + } + engines: { node: '>= 6' } + + glob-parent@6.0.2: + resolution: + { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, + } + engines: { node: '>=10.13.0' } + + globals@14.0.0: + resolution: + { + integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, + } + engines: { node: '>=18' } + + graphemer@1.4.0: + resolution: + { + integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, + } + + has-flag@4.0.0: + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: '>=8' } + + ignore@5.3.2: + resolution: + { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, + } + engines: { node: '>= 4' } + + ignore@7.0.5: + resolution: + { + integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, + } + engines: { node: '>= 4' } + + import-fresh@3.3.1: + resolution: + { + integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, + } + engines: { node: '>=6' } + + imurmurhash@0.1.4: + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, + } + engines: { node: '>=0.8.19' } + + is-extglob@2.1.1: + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, + } + engines: { node: '>=0.10.0' } + + is-glob@4.0.3: + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, + } + engines: { node: '>=0.10.0' } + + is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: '>=0.12.0' } + + isexe@2.0.0: + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } + + js-yaml@4.1.0: + resolution: + { + integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, + } + hasBin: true + + json-buffer@3.0.1: + resolution: + { + integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, + } + + json-schema-traverse@0.4.1: + resolution: + { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, + } + + keyv@4.5.4: + resolution: + { + integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, + } + + levn@0.4.1: + resolution: + { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, + } + engines: { node: '>= 0.8.0' } + + locate-path@6.0.0: + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, + } + engines: { node: '>=10' } + + lodash.merge@4.6.2: + resolution: + { + integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, + } + + luxon@3.6.0: + resolution: + { + integrity: sha512-WE7p0p7W1xji9qxkLYsvcIxZyfP48GuFrWIBQZIsbjCyf65dG1rv4n83HcOyEyhvzxJCrUoObCRNFgRNIQ5KNA==, + } + engines: { node: '>=12' } + + merge2@1.4.1: + resolution: + { + integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, + } + engines: { node: '>= 8' } + + micromatch@4.0.8: + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: '>=8.6' } + + minimatch@3.1.2: + resolution: + { + integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, + } + + minimatch@9.0.5: + resolution: + { + integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==, + } + engines: { node: '>=16 || 14 >=14.17' } + + ms@2.1.3: + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } + + natural-compare@1.4.0: + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, + } + + optionator@0.9.4: + resolution: + { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, + } + engines: { node: '>= 0.8.0' } + + p-limit@3.1.0: + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: '>=10' } + + p-locate@5.0.0: + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, + } + engines: { node: '>=10' } + + parent-module@1.0.1: + resolution: + { + integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, + } + engines: { node: '>=6' } + + path-exists@4.0.0: + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: '>=8' } + + path-key@3.1.1: + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: '>=8' } + + picomatch@2.3.1: + resolution: + { + integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, + } + engines: { node: '>=8.6' } + + playwright-core@1.53.1: + resolution: + { + integrity: sha512-Z46Oq7tLAyT0lGoFx4DOuB1IA9D1TPj0QkYxpPVUnGDqHHvDpCftu1J2hM2PiWsNMoZh8+LQaarAWcDfPBc6zg==, + } + engines: { node: '>=18' } + hasBin: true + + playwright@1.53.1: + resolution: + { + integrity: sha512-LJ13YLr/ocweuwxyGf1XNFWIU4M2zUSo149Qbp+A4cpwDjsxRPj7k6H25LBrEHiEwxvRbD8HdwvQmRMSvquhYw==, + } + engines: { node: '>=18' } + hasBin: true + + prelude-ls@1.2.1: + resolution: + { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, + } + engines: { node: '>= 0.8.0' } + + prettier@3.6.2: + resolution: + { + integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==, + } + engines: { node: '>=14' } + hasBin: true + + punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: '>=6' } + + queue-microtask@1.2.3: + resolution: + { + integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, + } + + resolve-from@4.0.0: + resolution: + { + integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, + } + engines: { node: '>=4' } + + reusify@1.1.0: + resolution: + { + integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, + } + engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + + run-parallel@1.2.0: + resolution: + { + integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, + } + + semver@7.7.2: + resolution: + { + integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==, + } + engines: { node: '>=10' } + hasBin: true + + shebang-command@2.0.0: + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: '>=8' } + + shebang-regex@3.0.0: + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: '>=8' } + + strip-json-comments@3.1.1: + resolution: + { + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, + } + engines: { node: '>=8' } + + supports-color@7.2.0: + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: '>=8' } + + to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: '>=8.0' } + + ts-api-utils@2.1.0: + resolution: + { + integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==, + } + engines: { node: '>=18.12' } + peerDependencies: + typescript: '>=4.8.4' + + type-check@0.4.0: + resolution: + { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, + } + engines: { node: '>= 0.8.0' } + + typescript@5.8.3: + resolution: + { + integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==, + } + engines: { node: '>=14.17' } + hasBin: true + + uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, + } + + which@2.0.2: + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: '>= 8' } + hasBin: true + + word-wrap@1.2.5: + resolution: + { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, + } + engines: { node: '>=0.10.0' } + + yocto-queue@0.1.0: + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: '>=10' } + + zod-to-json-schema@3.24.6: + resolution: + { + integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==, + } + peerDependencies: + zod: ^3.24.1 + + zod@3.25.67: + resolution: + { + integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==, + } + +snapshots: + '@anthropic-ai/sdk@0.52.0': {} + + '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0)': + dependencies: + eslint: 9.29.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.20.1': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.3': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.29.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.3': + dependencies: + '@eslint/core': 0.15.1 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@onkernel/cu-playwright@0.1.1(playwright@1.53.1)(typescript@5.8.3)': + dependencies: + '@anthropic-ai/sdk': 0.52.0 + luxon: 3.6.0 + playwright: 1.53.1 + typescript: 5.8.3 + zod: 3.25.67 + zod-to-json-schema: 3.24.6(zod@3.25.67) + + '@onkernel/sdk@0.6.3': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/type-utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + eslint: 9.29.0 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + debug: 4.4.1 + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + debug: 4.4.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.35.0': + dependencies: + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + + '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + debug: 4.4.1 + eslint: 9.29.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.35.0': {} + + '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.35.0': + dependencies: + '@typescript-eslint/types': 8.35.0 + eslint-visitor-keys: 4.2.1 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + dotenv@16.6.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.5(eslint@9.29.0): + dependencies: + eslint: 9.29.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.29.0: + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.14.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.29.0 + '@eslint/plugin-kit': 0.3.3 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + fsevents@2.3.2: + optional: true + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + luxon@3.6.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picomatch@2.3.1: {} + + playwright-core@1.53.1: {} + + playwright@1.53.1: + dependencies: + playwright-core: 1.53.1 + optionalDependencies: + fsevents: 2.3.2 + + prelude-ls@1.2.1: {} + + prettier@3.6.2: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + resolve-from@4.0.0: {} + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + semver@7.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript@5.8.3: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} + + zod-to-json-schema@3.24.6(zod@3.25.67): + dependencies: + zod: 3.25.67 + + zod@3.25.67: {} diff --git a/templates/typescript/computer-use/tsconfig.json b/templates/typescript/claude-cu/tsconfig.json similarity index 99% rename from templates/typescript/computer-use/tsconfig.json rename to templates/typescript/claude-cu/tsconfig.json index 39959d0..e709276 100644 --- a/templates/typescript/computer-use/tsconfig.json +++ b/templates/typescript/claude-cu/tsconfig.json @@ -28,4 +28,3 @@ "include": ["./**/*.ts", "./**/*.tsx"], "exclude": ["node_modules", "dist"] } - \ No newline at end of file diff --git a/templates/typescript/computer-use/README.md b/templates/typescript/computer-use/README.md deleted file mode 100644 index 7465e25..0000000 --- a/templates/typescript/computer-use/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Kernel Typscript Sample App - Computer Use - -This is a simple Kernel application that implements a prompt loop using Anthropic Computer Use. - -It generally follows the [Anthropic Reference Implementation](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo) but replaces `xodotool` and `gnome-screenshot` with Playwright. - -See the [docs](https://docs.onkernel.com/quickstart) for information. \ No newline at end of file diff --git a/templates/typescript/computer-use/_gitignore b/templates/typescript/computer-use/_gitignore deleted file mode 100644 index 9325515..0000000 --- a/templates/typescript/computer-use/_gitignore +++ /dev/null @@ -1,39 +0,0 @@ -# Dependencies -node_modules/ -package-lock.json - -# TypeScript -*.tsbuildinfo -dist/ -build/ - -# Environment -.env -.env.local -.env.*.local - -# IDE -.vscode/ -.idea/ -*.swp -*.swo - -# OS -.DS_Store -Thumbs.db - -# Logs -logs/ -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Testing -coverage/ -.nyc_output/ - -# Misc -.cache/ -.temp/ -.tmp/ \ No newline at end of file diff --git a/templates/typescript/computer-use/index.ts b/templates/typescript/computer-use/index.ts deleted file mode 100644 index 8774dd1..0000000 --- a/templates/typescript/computer-use/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Kernel, type KernelContext } from '@onkernel/sdk'; -import { samplingLoop } from './loop'; -import { chromium } from 'playwright'; - -const kernel = new Kernel(); - -const app = kernel.app('ts-cu'); - -interface QueryInput { - query: string; -} - -interface QueryOutput { - result: string; -} - -// LLM API Keys are set in the environment during `kernel deploy -e ANTHROPIC_API_KEY=XXX` -// See https://docs.onkernel.com/launch/deploy#environment-variables -const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY; - -if (!ANTHROPIC_API_KEY) { - throw new Error('ANTHROPIC_API_KEY is not set'); -} - -app.action( - 'cu-task', - async (ctx: KernelContext, payload?: QueryInput): Promise => { - if (!payload?.query) { - throw new Error('Query is required'); - } - - const kernelBrowser = await kernel.browsers.create({ - invocation_id: ctx.invocation_id, - stealth: true, - }); - - console.log("Kernel browser live view url: ", kernelBrowser.browser_live_view_url); - - const browser = await chromium.connectOverCDP(kernelBrowser.cdp_ws_url); - const context = await browser.contexts()[0]; - const page = await context?.pages()[0]; - if (!page) { - throw new Error('Error getting initial page'); - } - - try { - // Run the sampling loop - const finalMessages = await samplingLoop({ - model: 'claude-sonnet-4-20250514', - messages: [{ - role: 'user', - content: payload.query, - }], - apiKey: ANTHROPIC_API_KEY, - thinkingBudget: 1024, - playwrightPage: page, - }); - - // Extract the final result from the messages - if (finalMessages.length === 0) { - throw new Error('No messages were generated during the sampling loop'); - } - - const lastMessage = finalMessages[finalMessages.length - 1]; - if (!lastMessage) { - throw new Error('Failed to get the last message from the sampling loop'); - } - - const result = typeof lastMessage.content === 'string' - ? lastMessage.content - : lastMessage.content.map(block => - block.type === 'text' ? block.text : '' - ).join(''); - - return { result }; - } catch (error) { - console.error('Error in sampling loop:', error); - throw error; - } finally { - await browser.close(); - } - }, -); diff --git a/templates/typescript/computer-use/loop.ts b/templates/typescript/computer-use/loop.ts deleted file mode 100644 index 11ffbe9..0000000 --- a/templates/typescript/computer-use/loop.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { Anthropic } from '@anthropic-ai/sdk'; -import { DateTime } from 'luxon'; -import type { Page } from 'playwright'; -import type { BetaMessageParam, BetaTextBlock } from './types/beta'; -import { ToolCollection, DEFAULT_TOOL_VERSION, TOOL_GROUPS_BY_VERSION, type ToolVersion } from './tools/collection'; -import { responseToParams, maybeFilterToNMostRecentImages, injectPromptCaching, PROMPT_CACHING_BETA_FLAG } from './utils/message-processing'; -import { makeApiToolResult } from './utils/tool-results'; -import { ComputerTool20241022, ComputerTool20250124 } from './tools/computer'; -import type { ActionParams } from './tools/types/computer'; -import { Action } from './tools/types/computer'; - -// System prompt optimized for the environment -const SYSTEM_PROMPT = ` -* You are utilising an Ubuntu virtual machine using ${process.arch} architecture with internet access. -* When you connect to the display, CHROMIUM IS ALREADY OPEN. The url bar is not visible but it is there. -* If you need to navigate to a new page, use ctrl+l to focus the url bar and then enter the url. -* You won't be able to see the url bar from the screenshot but ctrl-l still works. -* As the initial step click on the search bar. -* When viewing a page it can be helpful to zoom out so that you can see everything on the page. -* Either that, or make sure you scroll down to see everything before deciding something isn't available. -* When using your computer function calls, they take a while to run and send back to you. -* Where possible/feasible, try to chain multiple of these calls all into one function calls request. -* The current date is ${DateTime.now().toFormat('EEEE, MMMM d, yyyy')}. -* After each step, take a screenshot and carefully evaluate if you have achieved the right outcome. -* Explicitly show your thinking: "I have evaluated step X..." If not correct, try again. -* Only when you confirm a step was executed correctly should you move on to the next one. - - - -* When using Chromium, if a startup wizard appears, IGNORE IT. Do not even click "skip this step". -* Instead, click on the search bar on the center of the screen where it says "Search or enter address", and enter the appropriate search term or URL there. -`; - -// Add new type definitions -interface ThinkingConfig { - type: 'enabled'; - budget_tokens: number; -} - -interface ExtraBodyConfig { - thinking?: ThinkingConfig; -} - -interface ToolUseInput extends Record { - action: Action; -} - -export async function samplingLoop({ - model, - systemPromptSuffix, - messages, - apiKey, - onlyNMostRecentImages, - maxTokens = 4096, - toolVersion, - thinkingBudget, - tokenEfficientToolsBeta = false, - playwrightPage, -}: { - model: string; - systemPromptSuffix?: string; - messages: BetaMessageParam[]; - apiKey: string; - onlyNMostRecentImages?: number; - maxTokens?: number; - toolVersion?: ToolVersion; - thinkingBudget?: number; - tokenEfficientToolsBeta?: boolean; - playwrightPage: Page; -}): Promise { - const selectedVersion = toolVersion || DEFAULT_TOOL_VERSION; - const toolGroup = TOOL_GROUPS_BY_VERSION[selectedVersion]; - const toolCollection = new ToolCollection(...toolGroup.tools.map((Tool: typeof ComputerTool20241022 | typeof ComputerTool20250124) => new Tool(playwrightPage))); - - const system: BetaTextBlock = { - type: 'text', - text: `${SYSTEM_PROMPT}${systemPromptSuffix ? ' ' + systemPromptSuffix : ''}`, - }; - - while (true) { - const betas: string[] = toolGroup.beta_flag ? [toolGroup.beta_flag] : []; - - if (tokenEfficientToolsBeta) { - betas.push('token-efficient-tools-2025-02-19'); - } - - let imageTruncationThreshold = onlyNMostRecentImages || 0; - - const client = new Anthropic({ apiKey, maxRetries: 4 }); - const enablePromptCaching = true; - - if (enablePromptCaching) { - betas.push(PROMPT_CACHING_BETA_FLAG); - injectPromptCaching(messages); - onlyNMostRecentImages = 0; - (system as BetaTextBlock).cache_control = { type: 'ephemeral' }; - } - - if (onlyNMostRecentImages) { - maybeFilterToNMostRecentImages( - messages, - onlyNMostRecentImages, - imageTruncationThreshold - ); - } - - const extraBody: ExtraBodyConfig = {}; - if (thinkingBudget) { - extraBody.thinking = { type: 'enabled', budget_tokens: thinkingBudget }; - } - - const toolParams = toolCollection.toParams(); - - const response = await client.beta.messages.create({ - max_tokens: maxTokens, - messages, - model, - system: [system], - tools: toolParams, - betas, - ...extraBody, - }); - - const responseParams = responseToParams(response); - - const loggableContent = responseParams.map(block => { - if (block.type === 'tool_use') { - return { - type: 'tool_use', - name: block.name, - input: block.input - }; - } - return block; - }); - console.log('=== LLM RESPONSE ==='); - console.log('Stop reason:', response.stop_reason); - console.log(loggableContent); - console.log("===") - - messages.push({ - role: 'assistant', - content: responseParams, - }); - - if (response.stop_reason === 'end_turn') { - console.log('LLM has completed its task, ending loop'); - return messages; - } - - const toolResultContent = []; - let hasToolUse = false; - - for (const contentBlock of responseParams) { - if (contentBlock.type === 'tool_use' && contentBlock.name && contentBlock.input && typeof contentBlock.input === 'object') { - const input = contentBlock.input as ToolUseInput; - if ('action' in input && typeof input.action === 'string') { - hasToolUse = true; - const toolInput: ActionParams = { - action: input.action as Action, - ...Object.fromEntries( - Object.entries(input).filter(([key]) => key !== 'action') - ) - }; - - try { - const result = await toolCollection.run( - contentBlock.name, - toolInput - ); - - const toolResult = makeApiToolResult(result, contentBlock.id!); - toolResultContent.push(toolResult); - } catch (error) { - console.error(error); - throw error; - } - } - } - } - - if (toolResultContent.length === 0 && !hasToolUse && response.stop_reason !== 'tool_use') { - console.log('No tool use or results, and not waiting for tool use, ending loop'); - return messages; - } - - if (toolResultContent.length > 0) { - messages.push({ - role: 'user', - content: toolResultContent, - }); - } - } -} diff --git a/templates/typescript/computer-use/package.json b/templates/typescript/computer-use/package.json deleted file mode 100644 index fa8b15e..0000000 --- a/templates/typescript/computer-use/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "ts-cu", - "module": "index.ts", - "type": "module", - "private": true, - "peerDependencies": { - "typescript": "^5" - }, - "dependencies": { - "@onkernel/sdk": "^0.6.0", - "playwright": "^1.52.0", - "@anthropic-ai/sdk": "0.52.0", - "luxon": "3.6.0" - } -} \ No newline at end of file diff --git a/templates/typescript/computer-use/pnpm-lock.yaml b/templates/typescript/computer-use/pnpm-lock.yaml deleted file mode 100644 index 344fa3e..0000000 --- a/templates/typescript/computer-use/pnpm-lock.yaml +++ /dev/null @@ -1,79 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@anthropic-ai/sdk': - specifier: 0.52.0 - version: 0.52.0 - '@onkernel/sdk': - specifier: '>=0.5.0' - version: 0.5.0 - luxon: - specifier: 3.6.0 - version: 3.6.0 - playwright: - specifier: ^1.52.0 - version: 1.52.0 - typescript: - specifier: ^5 - version: 5.8.3 - -packages: - - '@anthropic-ai/sdk@0.52.0': - resolution: {integrity: sha512-d4c+fg+xy9e46c8+YnrrgIQR45CZlAi7PwdzIfDXDM6ACxEZli1/fxhURsq30ZpMZy6LvSkr41jGq5aF5TD7rQ==} - hasBin: true - - '@onkernel/sdk@0.5.0': - resolution: {integrity: sha512-n7gwc7rU0GY/XcDnEV0piHPd76bHTSfuTjQW4qFKUWQji0UK9YUVKDFklqAWbyGlXPUezWCfxh79ELv2cFYOBA==} - - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - luxon@3.6.0: - resolution: {integrity: sha512-WE7p0p7W1xji9qxkLYsvcIxZyfP48GuFrWIBQZIsbjCyf65dG1rv4n83HcOyEyhvzxJCrUoObCRNFgRNIQ5KNA==} - engines: {node: '>=12'} - - playwright-core@1.52.0: - resolution: {integrity: sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==} - engines: {node: '>=18'} - hasBin: true - - playwright@1.52.0: - resolution: {integrity: sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==} - engines: {node: '>=18'} - hasBin: true - - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} - engines: {node: '>=14.17'} - hasBin: true - -snapshots: - - '@anthropic-ai/sdk@0.52.0': {} - - '@onkernel/sdk@0.5.0': {} - - fsevents@2.3.2: - optional: true - - luxon@3.6.0: {} - - playwright-core@1.52.0: {} - - playwright@1.52.0: - dependencies: - playwright-core: 1.52.0 - optionalDependencies: - fsevents: 2.3.2 - - typescript@5.8.3: {} diff --git a/templates/typescript/computer-use/tools/collection.ts b/templates/typescript/computer-use/tools/collection.ts deleted file mode 100644 index 45f3afe..0000000 --- a/templates/typescript/computer-use/tools/collection.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { ComputerTool20241022, ComputerTool20250124 } from './computer'; -import { Action } from './types/computer'; -import type { ActionParams, ToolResult } from './types/computer'; - -export type ToolVersion = 'computer_use_20250124' | 'computer_use_20241022' | 'computer_use_20250429'; - -export const DEFAULT_TOOL_VERSION: ToolVersion = 'computer_use_20250429'; - -interface ToolGroup { - readonly version: ToolVersion; - readonly tools: (typeof ComputerTool20241022 | typeof ComputerTool20250124)[]; - readonly beta_flag: string; -} - -export const TOOL_GROUPS: ToolGroup[] = [ - { - version: 'computer_use_20241022', - tools: [ComputerTool20241022], - beta_flag: 'computer-use-2024-10-22', - }, - { - version: 'computer_use_20250124', - tools: [ComputerTool20250124], - beta_flag: 'computer-use-2025-01-24', - }, - // 20250429 version inherits from 20250124 - { - version: 'computer_use_20250429', - tools: [ComputerTool20250124], - beta_flag: 'computer-use-2025-01-24', - }, -]; - -export const TOOL_GROUPS_BY_VERSION: Record = Object.fromEntries( - TOOL_GROUPS.map(group => [group.version, group]) -) as Record; - -export class ToolCollection { - private tools: Map; - - constructor(...tools: (ComputerTool20241022 | ComputerTool20250124)[]) { - this.tools = new Map(tools.map(tool => [tool.name, tool])); - } - - toParams(): ActionParams[] { - return Array.from(this.tools.values()).map(tool => tool.toParams()); - } - - async run(name: string, toolInput: { action: Action } & Record): Promise { - const tool = this.tools.get(name); - if (!tool) { - throw new Error(`Tool ${name} not found`); - } - - if (!Object.values(Action).includes(toolInput.action)) { - throw new Error(`Invalid action ${toolInput.action} for tool ${name}`); - } - - return await tool.call(toolInput); - } -} \ No newline at end of file diff --git a/templates/typescript/computer-use/tools/computer.ts b/templates/typescript/computer-use/tools/computer.ts deleted file mode 100644 index df8e021..0000000 --- a/templates/typescript/computer-use/tools/computer.ts +++ /dev/null @@ -1,250 +0,0 @@ -import type { Page } from 'playwright'; -import { Action, ToolError } from './types/computer'; -import type { ActionParams, BaseAnthropicTool, ToolResult } from './types/computer'; -import { KeyboardUtils } from './utils/keyboard'; -import { ActionValidator } from './utils/validator'; - -const TYPING_DELAY_MS = 12; - -export class ComputerTool implements BaseAnthropicTool { - name: 'computer' = 'computer'; - protected page: Page; - protected _screenshotDelay = 2.0; - protected version: '20241022' | '20250124'; - - private readonly mouseActions = new Set([ - Action.LEFT_CLICK, - Action.RIGHT_CLICK, - Action.MIDDLE_CLICK, - Action.DOUBLE_CLICK, - Action.TRIPLE_CLICK, - Action.MOUSE_MOVE, - Action.LEFT_CLICK_DRAG, - Action.LEFT_MOUSE_DOWN, - Action.LEFT_MOUSE_UP, - ]); - - private readonly keyboardActions = new Set([ - Action.KEY, - Action.TYPE, - Action.HOLD_KEY, - ]); - - private readonly systemActions = new Set([ - Action.SCREENSHOT, - Action.CURSOR_POSITION, - Action.SCROLL, - Action.WAIT, - ]); - - constructor(page: Page, version: '20241022' | '20250124' = '20250124') { - this.page = page; - this.version = version; - } - - get apiType(): 'computer_20241022' | 'computer_20250124' { - return this.version === '20241022' ? 'computer_20241022' : 'computer_20250124'; - } - - toParams(): ActionParams { - const params = { - name: this.name, - type: this.apiType, - display_width_px: 1280, - display_height_px: 720, - display_number: null, - }; - return params; - } - - private getMouseButton(action: Action): 'left' | 'right' | 'middle' { - switch (action) { - case Action.LEFT_CLICK: - case Action.DOUBLE_CLICK: - case Action.TRIPLE_CLICK: - case Action.LEFT_CLICK_DRAG: - case Action.LEFT_MOUSE_DOWN: - case Action.LEFT_MOUSE_UP: - return 'left'; - case Action.RIGHT_CLICK: - return 'right'; - case Action.MIDDLE_CLICK: - return 'middle'; - default: - throw new ToolError(`Invalid mouse action: ${action}`); - } - } - - private async handleMouseAction(action: Action, coordinate: [number, number]): Promise { - const [x, y] = ActionValidator.validateAndGetCoordinates(coordinate); - await this.page.mouse.move(x, y); - await this.page.waitForTimeout(100); - - if (action === Action.LEFT_MOUSE_DOWN) { - await this.page.mouse.down(); - } else if (action === Action.LEFT_MOUSE_UP) { - await this.page.mouse.up(); - } else { - const button = this.getMouseButton(action); - if (action === Action.DOUBLE_CLICK) { - await this.page.mouse.dblclick(x, y, { button }); - } else if (action === Action.TRIPLE_CLICK) { - await this.page.mouse.click(x, y, { button, clickCount: 3 }); - } else { - await this.page.mouse.click(x, y, { button }); - } - } - - await this.page.waitForTimeout(500); - return await this.screenshot(); - } - - private async handleKeyboardAction(action: Action, text: string, duration?: number): Promise { - if (action === Action.HOLD_KEY) { - const key = KeyboardUtils.getPlaywrightKey(text); - await this.page.keyboard.down(key); - await new Promise(resolve => setTimeout(resolve, duration! * 1000)); - await this.page.keyboard.up(key); - } else if (action === Action.KEY) { - const keys = KeyboardUtils.parseKeyCombination(text); - for (const key of keys) { - await this.page.keyboard.down(key); - } - for (const key of keys.reverse()) { - await this.page.keyboard.up(key); - } - } else { - await this.page.keyboard.type(text, { delay: TYPING_DELAY_MS }); - } - - await this.page.waitForTimeout(500); - return await this.screenshot(); - } - - async screenshot(): Promise { - try { - console.log('Starting screenshot...'); - await new Promise(resolve => setTimeout(resolve, this._screenshotDelay * 1000)); - const screenshot = await this.page.screenshot({ type: 'png' }); - console.log('Screenshot taken, size:', screenshot.length, 'bytes'); - - return { - base64Image: screenshot.toString('base64'), - }; - } catch (error) { - throw new ToolError(`Failed to take screenshot: ${error}`); - } - } - - async call(params: ActionParams): Promise { - const { - action, - text, - coordinate, - scrollDirection: scrollDirectionParam, - scroll_amount, - scrollAmount, - duration, - ...kwargs - } = params; - - ActionValidator.validateActionParams(params, this.mouseActions, this.keyboardActions); - - if (action === Action.SCREENSHOT) { - return await this.screenshot(); - } - - if (action === Action.CURSOR_POSITION) { - const position = await this.page.evaluate(() => { - const selection = window.getSelection(); - const range = selection?.getRangeAt(0); - const rect = range?.getBoundingClientRect(); - return rect ? { x: rect.x, y: rect.y } : null; - }); - - if (!position) { - throw new ToolError('Failed to get cursor position'); - } - - return { output: `X=${position.x},Y=${position.y}` }; - } - - if (action === Action.SCROLL) { - if (this.version !== '20250124') { - throw new ToolError(`${action} is only available in version 20250124`); - } - - const scrollDirection = scrollDirectionParam || kwargs.scroll_direction; - const scrollAmountValue = scrollAmount || scroll_amount; - - if (!scrollDirection || !['up', 'down', 'left', 'right'].includes(scrollDirection)) { - throw new ToolError(`Scroll direction "${scrollDirection}" must be 'up', 'down', 'left', or 'right'`); - } - if (typeof scrollAmountValue !== 'number' || scrollAmountValue < 0) { - throw new ToolError(`Scroll amount "${scrollAmountValue}" must be a non-negative number`); - } - - if (coordinate) { - const [x, y] = ActionValidator.validateAndGetCoordinates(coordinate); - await this.page.mouse.move(x, y); - await this.page.waitForTimeout(100); - } - - const pageDimensions = await this.page.evaluate(() => { - return { h: window.innerHeight, w: window.innerWidth }; - }); - const pagePartitions = 25; - const scrollFactor = (scrollAmountValue || 10) / pagePartitions; - - if (scrollDirection === 'down' || scrollDirection === 'up') { - const amount = pageDimensions.h * scrollFactor; - console.log(`Scrolling ${amount.toFixed(2)} pixels ${scrollDirection}`); - await this.page.mouse.wheel(0, scrollDirection === 'down' ? amount : -amount); - } else { - const amount = pageDimensions.w * scrollFactor; - console.log(`Scrolling ${amount.toFixed(2)} pixels ${scrollDirection}`); - await this.page.mouse.wheel(scrollDirection === 'right' ? amount : -amount, 0); - } - - await this.page.waitForTimeout(500); - return await this.screenshot(); - } - - if (action === Action.WAIT) { - if (this.version !== '20250124') { - throw new ToolError(`${action} is only available in version 20250124`); - } - await new Promise(resolve => setTimeout(resolve, duration! * 1000)); - return await this.screenshot(); - } - - if (this.mouseActions.has(action)) { - if (!coordinate) { - throw new ToolError(`coordinate is required for ${action}`); - } - return await this.handleMouseAction(action, coordinate); - } - - if (this.keyboardActions.has(action)) { - if (!text) { - throw new ToolError(`text is required for ${action}`); - } - return await this.handleKeyboardAction(action, text, duration); - } - - throw new ToolError(`Invalid action: ${action}`); - } -} - -// For backward compatibility -export class ComputerTool20241022 extends ComputerTool { - constructor(page: Page) { - super(page, '20241022'); - } -} - -export class ComputerTool20250124 extends ComputerTool { - constructor(page: Page) { - super(page, '20250124'); - } -} diff --git a/templates/typescript/computer-use/tools/types/computer.ts b/templates/typescript/computer-use/tools/types/computer.ts deleted file mode 100644 index f4b061f..0000000 --- a/templates/typescript/computer-use/tools/types/computer.ts +++ /dev/null @@ -1,64 +0,0 @@ -export enum Action { - // Mouse actions - MOUSE_MOVE = 'mouse_move', - LEFT_CLICK = 'left_click', - RIGHT_CLICK = 'right_click', - MIDDLE_CLICK = 'middle_click', - DOUBLE_CLICK = 'double_click', - TRIPLE_CLICK = 'triple_click', - LEFT_CLICK_DRAG = 'left_click_drag', - LEFT_MOUSE_DOWN = 'left_mouse_down', - LEFT_MOUSE_UP = 'left_mouse_up', - - // Keyboard actions - KEY = 'key', - TYPE = 'type', - HOLD_KEY = 'hold_key', - - // System actions - SCREENSHOT = 'screenshot', - CURSOR_POSITION = 'cursor_position', - SCROLL = 'scroll', - WAIT = 'wait', -} - -// For backward compatibility -export type Action_20241022 = Action; -export type Action_20250124 = Action; - -export type MouseButton = 'left' | 'right' | 'middle'; -export type ScrollDirection = 'up' | 'down' | 'left' | 'right'; -export type Coordinate = [number, number]; -export type Duration = number; - -export interface ActionParams { - action: Action; - text?: string; - coordinate?: Coordinate; - scrollDirection?: ScrollDirection; - scroll_amount?: number; - scrollAmount?: number; - duration?: Duration; - key?: string; - [key: string]: Action | string | Coordinate | ScrollDirection | number | Duration | undefined; -} - -export interface ToolResult { - output?: string; - error?: string; - base64Image?: string; - system?: string; -} - -export interface BaseAnthropicTool { - name: string; - apiType: string; - toParams(): ActionParams; -} - -export class ToolError extends Error { - constructor(message: string) { - super(message); - this.name = 'ToolError'; - } -} \ No newline at end of file diff --git a/templates/typescript/computer-use/tools/utils/keyboard.ts b/templates/typescript/computer-use/tools/utils/keyboard.ts deleted file mode 100644 index 244cddf..0000000 --- a/templates/typescript/computer-use/tools/utils/keyboard.ts +++ /dev/null @@ -1,88 +0,0 @@ -export class KeyboardUtils { - // Only map alternative names to standard Playwright modifier keys - private static readonly modifierKeyMap: Record = { - 'ctrl': 'Control', - 'alt': 'Alt', - 'cmd': 'Meta', - 'command': 'Meta', - 'win': 'Meta', - }; - - // Essential key mappings for Playwright compatibility - private static readonly keyMap: Record = { - 'return': 'Enter', - 'space': ' ', - 'left': 'ArrowLeft', - 'right': 'ArrowRight', - 'up': 'ArrowUp', - 'down': 'ArrowDown', - 'home': 'Home', - 'end': 'End', - 'pageup': 'PageUp', - 'page_up': 'PageUp', - 'pagedown': 'PageDown', - 'page_down': 'PageDown', - 'delete': 'Delete', - 'backspace': 'Backspace', - 'tab': 'Tab', - 'esc': 'Escape', - 'escape': 'Escape', - 'insert': 'Insert', - 'super_l': 'Meta', - 'f1': 'F1', - 'f2': 'F2', - 'f3': 'F3', - 'f4': 'F4', - 'f5': 'F5', - 'f6': 'F6', - 'f7': 'F7', - 'f8': 'F8', - 'f9': 'F9', - 'f10': 'F10', - 'f11': 'F11', - 'f12': 'F12', - 'minus': '-', - 'equal': '=', - 'plus': '+', - }; - - static isModifierKey(key: string | undefined): boolean { - if (!key) return false; - const normalizedKey = this.modifierKeyMap[key.toLowerCase()] || key; - return ['Control', 'Alt', 'Shift', 'Meta'].includes(normalizedKey); - } - - static getPlaywrightKey(key: string | undefined): string { - if (!key) { - throw new Error('Key cannot be undefined'); - } - - const normalizedKey = key.toLowerCase(); - - // Handle special cases - if (normalizedKey in this.keyMap) { - return this.keyMap[normalizedKey] as string; - } - - // Normalize modifier keys - if (normalizedKey in this.modifierKeyMap) { - return this.modifierKeyMap[normalizedKey] as string; - } - - // Return the key as is - Playwright handles standard key names - return key; - } - - static parseKeyCombination(combo: string): string[] { - if (!combo) { - throw new Error('Key combination cannot be empty'); - } - return combo.toLowerCase().split('+').map(key => { - const trimmedKey = key.trim(); - if (!trimmedKey) { - throw new Error('Invalid key combination: empty key'); - } - return this.getPlaywrightKey(trimmedKey); - }); - } -} \ No newline at end of file diff --git a/templates/typescript/computer-use/tools/utils/validator.ts b/templates/typescript/computer-use/tools/utils/validator.ts deleted file mode 100644 index b8522c8..0000000 --- a/templates/typescript/computer-use/tools/utils/validator.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Action, ToolError } from '../types/computer'; -import type { ActionParams, Coordinate, Duration } from '../types/computer'; - -export class ActionValidator { - static validateText(text: string | undefined, required: boolean, action: string): void { - if (required && text === undefined) { - throw new ToolError(`text is required for ${action}`); - } - if (text !== undefined && typeof text !== 'string') { - throw new ToolError(`${text} must be a string`); - } - } - - static validateCoordinate(coordinate: Coordinate | undefined, required: boolean, action: string): void { - if (required && !coordinate) { - throw new ToolError(`coordinate is required for ${action}`); - } - if (coordinate) { - this.validateAndGetCoordinates(coordinate); - } - } - - static validateDuration(duration: Duration | undefined): void { - if (duration === undefined || typeof duration !== 'number') { - throw new ToolError(`${duration} must be a number`); - } - if (duration < 0) { - throw new ToolError(`${duration} must be non-negative`); - } - if (duration > 100) { - throw new ToolError(`${duration} is too long`); - } - } - - static validateAndGetCoordinates(coordinate: Coordinate): Coordinate { - if (!Array.isArray(coordinate) || coordinate.length !== 2) { - throw new ToolError(`${coordinate} must be a tuple of length 2`); - } - if (!coordinate.every(i => typeof i === 'number' && i >= 0)) { - throw new ToolError(`${coordinate} must be a tuple of non-negative numbers`); - } - return coordinate; - } - - static validateActionParams(params: ActionParams, mouseActions: Set, keyboardActions: Set): void { - const { action, text, coordinate, duration } = params; - - // Validate text parameter - if (keyboardActions.has(action)) { - this.validateText(text, true, action); - } else { - this.validateText(text, false, action); - } - - // Validate coordinate parameter - if (mouseActions.has(action)) { - this.validateCoordinate(coordinate, true, action); - } else { - this.validateCoordinate(coordinate, false, action); - } - - // Validate duration parameter - if (action === Action.HOLD_KEY || action === Action.WAIT) { - this.validateDuration(duration); - } - } -} \ No newline at end of file diff --git a/templates/typescript/computer-use/types/beta.ts b/templates/typescript/computer-use/types/beta.ts deleted file mode 100644 index 35328d7..0000000 --- a/templates/typescript/computer-use/types/beta.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { BetaMessageParam as AnthropicMessageParam, BetaMessage as AnthropicMessage, BetaContentBlock as AnthropicContentBlock } from '@anthropic-ai/sdk/resources/beta/messages/messages'; -import type { ActionParams } from '../tools/types/computer'; - -// Re-export the SDK types -export type BetaMessageParam = AnthropicMessageParam; -export type BetaMessage = AnthropicMessage; -export type BetaContentBlock = AnthropicContentBlock; - -// Keep our local types for internal use -export interface BetaTextBlock { - type: 'text'; - text: string; - id?: string; - cache_control?: { type: 'ephemeral' }; -} - -export interface BetaImageBlock { - type: 'image'; - source: { - type: 'base64'; - media_type: 'image/png'; - data: string; - }; - id?: string; - cache_control?: { type: 'ephemeral' }; -} - -export interface BetaToolUseBlock { - type: 'tool_use'; - name: string; - input: ActionParams; - id?: string; - cache_control?: { type: 'ephemeral' }; -} - -export interface BetaThinkingBlock { - type: 'thinking'; - thinking: { - type: 'enabled'; - budget_tokens: number; - } | { - type: 'disabled'; - }; - signature?: string; - id?: string; - cache_control?: { type: 'ephemeral' }; -} - -export interface BetaToolResultBlock { - type: 'tool_result'; - content: (BetaTextBlock | BetaImageBlock)[] | string; - tool_use_id: string; - is_error: boolean; - id?: string; - cache_control?: { type: 'ephemeral' }; -} - -export type BetaLocalContentBlock = BetaTextBlock | BetaImageBlock | BetaToolUseBlock | BetaThinkingBlock | BetaToolResultBlock; \ No newline at end of file diff --git a/templates/typescript/computer-use/utils/message-processing.ts b/templates/typescript/computer-use/utils/message-processing.ts deleted file mode 100644 index acc5ac4..0000000 --- a/templates/typescript/computer-use/utils/message-processing.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { BetaMessage, BetaMessageParam, BetaToolResultBlock, BetaContentBlock, BetaLocalContentBlock } from '../types/beta'; - -export function responseToParams(response: BetaMessage): BetaContentBlock[] { - return response.content.map(block => { - if (block.type === 'text' && block.text) { - return { type: 'text', text: block.text }; - } - if (block.type === 'thinking') { - const { thinking, signature, ...rest } = block; - return { ...rest, thinking, ...(signature && { signature }) }; - } - return block as BetaContentBlock; - }); -} - -export function maybeFilterToNMostRecentImages( - messages: BetaMessageParam[], - imagesToKeep: number, - minRemovalThreshold: number -): void { - if (!imagesToKeep) return; - - const toolResultBlocks = messages - .flatMap(message => Array.isArray(message?.content) ? message.content : []) - .filter((item): item is BetaToolResultBlock => - typeof item === 'object' && item.type === 'tool_result' - ); - - const totalImages = toolResultBlocks.reduce((count, toolResult) => { - if (!Array.isArray(toolResult.content)) return count; - return count + toolResult.content.filter( - content => typeof content === 'object' && content.type === 'image' - ).length; - }, 0); - - let imagesToRemove = Math.floor((totalImages - imagesToKeep) / minRemovalThreshold) * minRemovalThreshold; - - for (const toolResult of toolResultBlocks) { - if (Array.isArray(toolResult.content)) { - toolResult.content = toolResult.content.filter(content => { - if (typeof content === 'object' && content.type === 'image') { - if (imagesToRemove > 0) { - imagesToRemove--; - return false; - } - } - return true; - }); - } - } -} - -const PROMPT_CACHING_BETA_FLAG = 'prompt-caching-2024-07-31'; - -export function injectPromptCaching(messages: BetaMessageParam[]): void { - let breakpointsRemaining = 3; - - for (let i = messages.length - 1; i >= 0; i--) { - const message = messages[i]; - if (!message) continue; - if (message.role === 'user' && Array.isArray(message.content)) { - if (breakpointsRemaining > 0) { - breakpointsRemaining--; - const lastContent = message.content[message.content.length - 1]; - if (lastContent) { - (lastContent as BetaLocalContentBlock).cache_control = { type: 'ephemeral' }; - } - } else { - const lastContent = message.content[message.content.length - 1]; - if (lastContent) { - delete (lastContent as BetaLocalContentBlock).cache_control; - } - break; - } - } - } -} - -export { PROMPT_CACHING_BETA_FLAG }; \ No newline at end of file diff --git a/templates/typescript/computer-use/utils/tool-results.ts b/templates/typescript/computer-use/utils/tool-results.ts deleted file mode 100644 index c18eab2..0000000 --- a/templates/typescript/computer-use/utils/tool-results.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { ToolResult } from '../tools/types/computer'; -import type { BetaToolResultBlock, BetaTextBlock, BetaImageBlock } from '../types/beta'; - -export function makeApiToolResult( - result: ToolResult, - toolUseId: string -): BetaToolResultBlock { - const toolResultContent: (BetaTextBlock | BetaImageBlock)[] = []; - let isError = false; - - if (result.error) { - isError = true; - toolResultContent.push({ - type: 'text', - text: maybePrependSystemToolResult(result, result.error), - }); - } else { - if (result.output) { - toolResultContent.push({ - type: 'text', - text: maybePrependSystemToolResult(result, result.output), - }); - } - if (result.base64Image) { - toolResultContent.push({ - type: 'image', - source: { - type: 'base64', - media_type: 'image/png', - data: result.base64Image, - }, - }); - } - } - - return { - type: 'tool_result', - content: toolResultContent, - tool_use_id: toolUseId, - is_error: isError, - }; -} - -export function maybePrependSystemToolResult(result: ToolResult, resultText: string): string { - if (result.system) { - return `${result.system}\n${resultText}`; - } - return resultText; -} \ No newline at end of file