From 8c1110a8a36e618f9c005b23bd92480823817c2f Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 30 Oct 2025 00:57:06 +0000 Subject: [PATCH 1/5] Implement TypeScript client for OpenHands Agent Server - Created complete TypeScript client mirroring Python SDK structure - Implemented RemoteConversation and RemoteWorkspace classes - Added HTTP client with error handling and timeout support - Implemented WebSocket client for real-time events - Created comprehensive type definitions based on Swagger spec - Added build system with TypeScript, ESLint, and Prettier - Included usage examples and documentation - All code compiles successfully and passes linting Co-authored-by: openhands --- .eslintrc.js | 25 + .gitignore | 93 + .prettierrc | 8 + README.md | 270 + examples/basic-usage.ts | 109 + package-lock.json | 5076 ++++++++++++++++++ package.json | 45 + src/client/http-client.ts | 174 + src/conversation/remote-conversation.ts | 271 + src/conversation/remote-state.ts | 189 + src/events/remote-events-list.ts | 139 + src/events/websocket-client.ts | 124 + src/index.ts | 104 + src/models/conversation.ts | 60 + src/models/workspace.ts | 31 + src/types/base.ts | 100 + src/workspace/remote-workspace.ts | 246 + swagger-doc.json | 6257 +++++++++++++++++++++++ tsconfig.json | 26 + 19 files changed, 13347 insertions(+) create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 examples/basic-usage.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/client/http-client.ts create mode 100644 src/conversation/remote-conversation.ts create mode 100644 src/conversation/remote-state.ts create mode 100644 src/events/remote-events-list.ts create mode 100644 src/events/websocket-client.ts create mode 100644 src/index.ts create mode 100644 src/models/conversation.ts create mode 100644 src/models/workspace.ts create mode 100644 src/types/base.ts create mode 100644 src/workspace/remote-workspace.ts create mode 100644 swagger-doc.json create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..97cb8fa --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: '@typescript-eslint/parser', + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + ], + plugins: ['@typescript-eslint'], + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + }, + env: { + node: true, + es6: true, + }, + rules: { + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-non-null-assertion': 'warn', + 'prefer-const': 'error', + 'no-var': 'error', + }, +}; \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae1c12e --- /dev/null +++ b/.gitignore @@ -0,0 +1,93 @@ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Build outputs +dist/ +build/ +*.tsbuildinfo + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +logs/ +*.log + +# Coverage directory used by tools like istanbul +coverage/ +*.lcov + +# nyc test coverage +.nyc_output + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a813c65 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2, + "useTabs": false +} \ No newline at end of file diff --git a/README.md b/README.md index 8b13789..29c4758 100644 --- a/README.md +++ b/README.md @@ -1 +1,271 @@ +# OpenHands Agent Server TypeScript Client +A TypeScript client library for the OpenHands Agent Server API that mirrors the structure and functionality of the Python SDK. + +## Features + +- **RemoteConversation**: Manage conversations with OpenHands agents +- **RemoteWorkspace**: Execute commands and perform file operations remotely +- **Real-time Events**: WebSocket support for live event streaming +- **Type Safety**: Full TypeScript support with comprehensive type definitions +- **Python SDK Compatibility**: Same class names and method signatures as the Python SDK + +## Installation + +```bash +npm install @openhands/agent-server-typescript-client +``` + +## Quick Start + +### Creating a Conversation + +```typescript +import { RemoteConversation, AgentBase } from '@openhands/agent-server-typescript-client'; + +const agent: AgentBase = { + name: 'CodeActAgent', + llm: { + model: 'gpt-4', + api_key: 'your-openai-api-key' + } +}; + +const conversation = await RemoteConversation.create( + 'http://localhost:3000', // Agent server URL + agent, + { + apiKey: 'your-session-api-key', + initialMessage: 'Hello, can you help me write some code?', + callback: (event) => { + console.log('Received event:', event); + } + } +); + +// Start WebSocket for real-time events +await conversation.startWebSocketClient(); + +// Send a message and run the agent +await conversation.sendMessage('Create a simple Python script that prints "Hello World"'); +await conversation.run(); +``` + +### Loading an Existing Conversation + +```typescript +const conversation = await RemoteConversation.load( + 'http://localhost:3000', + 'conversation-id-here', + { + apiKey: 'your-session-api-key' + } +); +``` + +### Using the Workspace + +```typescript +// Execute commands +const result = await conversation.workspace.executeCommand('ls -la'); +console.log('Command output:', result.stdout); +console.log('Exit code:', result.exit_code); + +// Upload a file +const uploadResult = await conversation.workspace.fileUpload( + './local-file.txt', + '/remote/path/file.txt' +); + +// Download a file +const downloadResult = await conversation.workspace.fileDownload( + '/remote/path/file.txt', + './downloaded-file.txt' +); +``` + +### Working with Events + +```typescript +// Access the events list +const events = await conversation.state.events.getEvents(); +console.log(`Total events: ${events.length}`); + +// Iterate through events +for await (const event of conversation.state.events) { + console.log(`Event: ${event.kind} at ${event.timestamp}`); +} +``` + +### Managing Conversation State + +```typescript +// Get conversation status +const status = await conversation.state.getAgentStatus(); +console.log('Agent status:', status); + +// Get conversation stats +const stats = await conversation.conversationStats(); +console.log('Total events:', stats.total_events); + +// Set confirmation policy +await conversation.setConfirmationPolicy({ type: 'always' }); + +// Update secrets +await conversation.updateSecrets({ + 'API_KEY': 'secret-value', + 'DATABASE_URL': () => process.env.DATABASE_URL || 'default-url' +}); +``` + +## API Reference + +### RemoteConversation + +The main class for managing conversations with OpenHands agents. + +#### Static Methods + +- `RemoteConversation.create(host, agent, options)` - Create a new conversation +- `RemoteConversation.load(host, conversationId, options)` - Load an existing conversation + +#### Instance Methods + +- `sendMessage(message)` - Send a message to the agent +- `run()` - Start agent execution +- `pause()` - Pause agent execution +- `setConfirmationPolicy(policy)` - Set confirmation policy +- `sendConfirmationResponse(accept, reason?)` - Respond to confirmation requests +- `generateTitle(maxLength?, llm?)` - Generate a title for the conversation +- `updateSecrets(secrets)` - Update conversation secrets +- `startWebSocketClient()` - Start real-time event streaming +- `stopWebSocketClient()` - Stop real-time event streaming +- `conversationStats()` - Get conversation statistics +- `close()` - Clean up resources + +#### Properties + +- `id` - Conversation ID +- `state` - RemoteState instance for accessing conversation state +- `workspace` - RemoteWorkspace instance for command execution and file operations + +### RemoteWorkspace + +Handles remote command execution and file operations. + +#### Methods + +- `executeCommand(command, cwd?, timeout?)` - Execute a bash command +- `fileUpload(sourcePath, destinationPath)` - Upload a file +- `fileDownload(sourcePath, destinationPath)` - Download a file +- `gitChanges(path)` - Get git changes for a path +- `gitDiff(path)` - Get git diff for a path +- `close()` - Clean up resources + +### RemoteState + +Manages conversation state and provides access to events. + +#### Properties + +- `id` - Conversation ID +- `events` - RemoteEventsList instance + +#### Methods + +- `getAgentStatus()` - Get current agent execution status +- `getConfirmationPolicy()` - Get current confirmation policy +- `getActivatedKnowledgeSkills()` - Get activated knowledge skills +- `getAgent()` - Get agent configuration +- `getWorkspace()` - Get workspace configuration +- `getPersistenceDir()` - Get persistence directory +- `modelDump()` - Get state as plain object +- `modelDumpJson()` - Get state as JSON string + +### RemoteEventsList + +Manages conversation events with caching and synchronization. + +#### Methods + +- `addEvent(event)` - Add an event to the cache +- `length()` - Get number of cached events +- `getEvent(index)` - Get event by index +- `getEvents(start?, end?)` - Get events slice +- `createDefaultCallback()` - Create a default event callback + +### WebSocketCallbackClient + +Handles real-time event streaming via WebSocket. + +#### Methods + +- `start()` - Start WebSocket connection +- `stop()` - Stop WebSocket connection + +## Types + +The library includes comprehensive TypeScript type definitions: + +- `ConversationID` - Conversation identifier type +- `Event` - Base event interface +- `Message` - Message interface with content +- `AgentBase` - Agent configuration interface +- `CommandResult` - Command execution result +- `FileOperationResult` - File operation result +- `ConversationStats` - Conversation statistics +- `AgentExecutionStatus` - Agent status enum +- And many more... + +## Error Handling + +The client includes proper error handling with custom error types: + +```typescript +import { HttpError } from '@openhands/agent-server-typescript-client'; + +try { + await conversation.sendMessage('Hello'); +} catch (error) { + if (error instanceof HttpError) { + console.error(`HTTP Error ${error.status}: ${error.statusText}`); + console.error('Response:', error.response); + } else { + console.error('Unexpected error:', error); + } +} +``` + +## Development + +### Building + +```bash +npm run build +``` + +### Development Mode + +```bash +npm run dev +``` + +### Linting + +```bash +npm run lint +``` + +### Formatting + +```bash +npm run format +``` + +## License + +MIT + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. \ No newline at end of file diff --git a/examples/basic-usage.ts b/examples/basic-usage.ts new file mode 100644 index 0000000..4cde6af --- /dev/null +++ b/examples/basic-usage.ts @@ -0,0 +1,109 @@ +/** + * Basic usage example for the OpenHands Agent Server TypeScript Client + */ + +import { RemoteConversation, AgentBase, AgentExecutionStatus } from '../src/index.js'; + +async function main() { + // Define the agent configuration + const agent: AgentBase = { + name: 'CodeActAgent', + llm: { + model: 'gpt-4', + api_key: process.env.OPENAI_API_KEY || 'your-openai-api-key', + }, + }; + + try { + // Create a new conversation + console.log('Creating conversation...'); + const conversation = await RemoteConversation.create( + 'http://localhost:3000', // Replace with your agent server URL + agent, + { + apiKey: process.env.SESSION_API_KEY || 'your-session-api-key', + initialMessage: 'Hello! Can you help me write a simple Python script?', + callback: (event) => { + console.log(`Event received: ${event.kind} at ${event.timestamp}`); + }, + } + ); + + console.log(`Conversation created with ID: ${conversation.id}`); + + // Start WebSocket for real-time events + await conversation.startWebSocketClient(); + console.log('WebSocket client started'); + + // Send a message + await conversation.sendMessage('Create a Python script that prints "Hello, World!"'); + console.log('Message sent'); + + // Run the agent + await conversation.run(); + console.log('Agent started'); + + // Monitor the conversation status + let status = await conversation.state.getAgentStatus(); + console.log(`Initial status: ${status}`); + + // Wait for the agent to finish (in a real application, you'd handle this differently) + while (status === AgentExecutionStatus.RUNNING) { + await new Promise(resolve => setTimeout(resolve, 1000)); + status = await conversation.state.getAgentStatus(); + console.log(`Current status: ${status}`); + } + + // Get conversation statistics + const stats = await conversation.conversationStats(); + console.log('Conversation stats:', stats); + + // Get all events + const events = await conversation.state.events.getEvents(); + console.log(`Total events: ${events.length}`); + + // Example of using the workspace + const result = await conversation.workspace.executeCommand('ls -la'); + console.log('Command result:', { + exitCode: result.exit_code, + stdout: result.stdout.substring(0, 200) + '...', // Truncate for display + }); + + // Clean up + await conversation.close(); + console.log('Conversation closed'); + + } catch (error) { + console.error('Error:', error); + } +} + +// Example of loading an existing conversation +async function loadExistingConversation() { + try { + const conversation = await RemoteConversation.load( + 'http://localhost:3000', + 'existing-conversation-id', + { + apiKey: process.env.SESSION_API_KEY || 'your-session-api-key', + } + ); + + console.log(`Loaded conversation: ${conversation.id}`); + + // Get current status + const status = await conversation.state.getAgentStatus(); + console.log(`Status: ${status}`); + + // Clean up + await conversation.close(); + + } catch (error) { + console.error('Error loading conversation:', error); + } +} + +// Run the example +if (require.main === module) { + main().catch(console.error); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d35404b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5076 @@ +{ + "name": "@openhands/agent-server-typescript-client", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@openhands/agent-server-typescript-client", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "uuid": "^9.0.1", + "ws": "^8.14.2" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@types/uuid": "^9.0.6", + "@types/ws": "^8.5.8", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint": "^8.0.0", + "jest": "^29.0.0", + "prettier": "^3.0.0", + "typescript": "^5.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", + "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.34", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", + "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.21.tgz", + "integrity": "sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", + "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.243", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.243.tgz", + "integrity": "sha512-ZCphxFW3Q1TVhcgS9blfut1PX8lusVi2SvXQgmEEnK4TCmE1JhH2JkjJN+DNt0pJJwfBri5AROBnz2b/C+YU9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6189d1c --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "@openhands/agent-server-typescript-client", + "version": "0.1.0", + "description": "TypeScript client for OpenHands Agent Server", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "test": "jest", + "lint": "eslint src/**/*.ts", + "format": "prettier --write src/**/*.ts" + }, + "keywords": [ + "openhands", + "agent", + "typescript", + "client", + "sdk" + ], + "author": "OpenHands Team", + "license": "MIT", + "dependencies": { + "uuid": "^9.0.1", + "ws": "^8.14.2" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@types/uuid": "^9.0.6", + "@types/ws": "^8.5.8", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint": "^8.0.0", + "jest": "^29.0.0", + "prettier": "^3.0.0", + "typescript": "^5.0.0" + }, + "files": [ + "dist/**/*" + ], + "repository": { + "type": "git", + "url": "https://github.com/All-Hands-AI/agent-server-typescript-client.git" + } +} diff --git a/src/client/http-client.ts b/src/client/http-client.ts new file mode 100644 index 0000000..d6c3de5 --- /dev/null +++ b/src/client/http-client.ts @@ -0,0 +1,174 @@ +/** + * HTTP client for OpenHands Agent Server API + */ + +export interface HttpClientOptions { + baseUrl: string; + apiKey?: string; + timeout?: number; +} + +export interface RequestOptions { + method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; + url: string; + params?: Record; + data?: any; + headers?: Record; + timeout?: number; + acceptableStatusCodes?: Set; +} + +export interface HttpResponse { + data: T; + status: number; + statusText: string; + headers: Record; +} + +export class HttpError extends Error { + constructor( + public status: number, + public statusText: string, + public response?: any, + message?: string + ) { + super(message || `HTTP ${status}: ${statusText}`); + this.name = 'HttpError'; + } +} + +export class HttpClient { + private baseUrl: string; + private apiKey?: string; + private timeout: number; + + constructor(options: HttpClientOptions) { + this.baseUrl = options.baseUrl.replace(/\/$/, ''); + this.apiKey = options.apiKey; + this.timeout = options.timeout || 60000; + } + + async request(options: RequestOptions): Promise> { + const url = new URL(options.url, this.baseUrl); + + // Add query parameters + if (options.params) { + Object.entries(options.params).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + url.searchParams.append(key, String(value)); + } + }); + } + + const headers: Record = { + 'Content-Type': 'application/json', + ...options.headers, + }; + + // Add API key header if available + if (this.apiKey) { + headers['X-Session-API-Key'] = this.apiKey; + } + + const requestInit: RequestInit = { + method: options.method, + headers, + signal: AbortSignal.timeout(options.timeout || this.timeout), + }; + + // Add body for non-GET requests + if (options.data && options.method !== 'GET') { + if (options.data instanceof FormData) { + // Remove content-type header for FormData (browser will set it with boundary) + delete headers['Content-Type']; + requestInit.body = options.data; + } else { + requestInit.body = JSON.stringify(options.data); + } + } + + try { + const response = await fetch(url.toString(), requestInit); + + // Check if status code is acceptable + const isAcceptable = options.acceptableStatusCodes?.has(response.status) || + (!options.acceptableStatusCodes && response.ok); + + if (!isAcceptable) { + let errorContent: any; + try { + const contentType = response.headers.get('content-type'); + if (contentType?.includes('application/json')) { + errorContent = await response.json(); + } else { + errorContent = await response.text(); + } + } catch { + errorContent = null; + } + + throw new HttpError( + response.status, + response.statusText, + errorContent, + `HTTP request failed (${response.status} ${response.statusText}): ${JSON.stringify(errorContent)}` + ); + } + + // Parse response + let data: T; + const contentType = response.headers.get('content-type'); + if (contentType?.includes('application/json')) { + data = await response.json(); + } else { + data = (await response.text()) as unknown as T; + } + + // Convert headers to plain object + const responseHeaders: Record = {}; + response.headers.forEach((value, key) => { + responseHeaders[key] = value; + }); + + return { + data, + status: response.status, + statusText: response.statusText, + headers: responseHeaders, + }; + } catch (error) { + if (error instanceof HttpError) { + throw error; + } + + if (error instanceof Error) { + if (error.name === 'AbortError') { + throw new Error(`Request timeout after ${options.timeout || this.timeout}ms`); + } + throw new Error(`Request failed: ${error.message}`); + } + + throw new Error('Unknown request error'); + } + } + + async get(url: string, options?: Omit): Promise> { + return this.request({ method: 'GET', url, ...options }); + } + + async post(url: string, data?: any, options?: Omit): Promise> { + return this.request({ method: 'POST', url, data, ...options }); + } + + async put(url: string, data?: any, options?: Omit): Promise> { + return this.request({ method: 'PUT', url, data, ...options }); + } + + async delete(url: string, options?: Omit): Promise> { + return this.request({ method: 'DELETE', url, ...options }); + } + + close(): void { + // No cleanup needed for fetch-based client + } +} \ No newline at end of file diff --git a/src/conversation/remote-conversation.ts b/src/conversation/remote-conversation.ts new file mode 100644 index 0000000..bbdf237 --- /dev/null +++ b/src/conversation/remote-conversation.ts @@ -0,0 +1,271 @@ +/** + * Remote conversation implementation + */ + +// import { v4 as uuidv4 } from 'uuid'; // Unused for now +import { HttpClient } from '../client/http-client.js'; +import { WebSocketCallbackClient } from '../events/websocket-client.js'; +import { RemoteState } from './remote-state.js'; +import { RemoteWorkspace } from '../workspace/remote-workspace.js'; +import { + ConversationID, + Message, + ConversationCallbackType, + ConfirmationPolicyBase, + ConversationStats, + AgentBase, + SecretValue +} from '../types/base.js'; +import { + ConversationInfo, + SendMessageRequest, + ConfirmationResponseRequest, + CreateConversationRequest, + GenerateTitleRequest, + GenerateTitleResponse, + UpdateSecretsRequest +} from '../models/conversation.js'; + +export interface RemoteConversationOptions { + host: string; + conversationId?: string; + apiKey?: string; + callback?: ConversationCallbackType; +} + +export class RemoteConversation { + public readonly host: string; + public readonly apiKey?: string; + private _conversationId?: string; + private _state?: RemoteState; + private _workspace?: RemoteWorkspace; + private client: HttpClient; + private wsClient?: WebSocketCallbackClient; + private callback?: ConversationCallbackType; + + constructor(options: RemoteConversationOptions) { + this.host = options.host.replace(/\/$/, ''); + this.apiKey = options.apiKey; + this._conversationId = options.conversationId; + this.callback = options.callback; + + this.client = new HttpClient({ + baseUrl: this.host, + apiKey: this.apiKey, + timeout: 60000, + }); + } + + get id(): ConversationID { + if (!this._conversationId) { + throw new Error('Conversation ID not set. Create or load a conversation first.'); + } + return this._conversationId; + } + + get state(): RemoteState { + if (!this._state) { + if (!this._conversationId) { + throw new Error('Conversation not initialized. Create or load a conversation first.'); + } + this._state = new RemoteState(this.client, this._conversationId); + } + return this._state; + } + + get workspace(): RemoteWorkspace { + if (!this._workspace) { + throw new Error('Workspace not initialized. Create or load a conversation first.'); + } + return this._workspace; + } + + async conversationStats(): Promise { + const response = await this.client.get(`/api/conversations/${this.id}/stats`); + return response.data; + } + + async sendMessage(message: string | Message): Promise { + let messageContent: SendMessageRequest; + + if (typeof message === 'string') { + messageContent = { + role: 'user', + content: [{ type: 'text', text: message }], + run: false, + }; + } else { + messageContent = { + role: 'user', + content: message.content, + run: false, + }; + } + + await this.client.post(`/api/conversations/${this.id}/send_message`, messageContent); + } + + async run(): Promise { + await this.client.post(`/api/conversations/${this.id}/run`); + } + + async pause(): Promise { + await this.client.post(`/api/conversations/${this.id}/pause`); + } + + async setConfirmationPolicy(policy: ConfirmationPolicyBase): Promise { + await this.client.post(`/api/conversations/${this.id}/set_confirmation_policy`, policy); + } + + async sendConfirmationResponse(accept: boolean, reason?: string): Promise { + const request: ConfirmationResponseRequest = { accept, reason }; + await this.client.post(`/api/conversations/${this.id}/send_confirmation_response`, request); + } + + async generateTitle(maxLength: number = 50, llm?: any): Promise { + const request: GenerateTitleRequest = { max_length: maxLength }; + if (llm) { + request.llm = llm; + } + + const response = await this.client.post( + `/api/conversations/${this.id}/generate_title`, + request + ); + return response.data.title; + } + + async updateSecrets(secrets: Record): Promise { + // Convert SecretValue functions to strings + const secretStrings: Record = {}; + for (const [key, value] of Object.entries(secrets)) { + secretStrings[key] = typeof value === 'function' ? value() : value; + } + + const request: UpdateSecretsRequest = { secrets: secretStrings }; + await this.client.post(`/api/conversations/${this.id}/update_secrets`, request); + } + + async startWebSocketClient(): Promise { + if (this.wsClient) { + return; + } + + // Create combined callback that handles both user callback and state updates + const combinedCallback: ConversationCallbackType = (event) => { + // Add event to the events list + this.state.events.addEvent(event).catch(error => { + console.error('Error adding event to events list:', error); + }); + + // Update state if it's a state update event + const stateCallback = this.state.createStateUpdateCallback(); + stateCallback(event); + + // Call user callback if provided + if (this.callback) { + this.callback(event); + } + }; + + this.wsClient = new WebSocketCallbackClient({ + host: this.host, + conversationId: this.id, + callback: combinedCallback, + apiKey: this.apiKey, + }); + + this.wsClient.start(); + } + + async stopWebSocketClient(): Promise { + if (this.wsClient) { + this.wsClient.stop(); + this.wsClient = undefined; + } + } + + // Static factory methods + static async create( + host: string, + agent: AgentBase, + options: { + apiKey?: string; + initialMessage?: string; + maxIterations?: number; + stuckDetection?: boolean; + workspace?: any; + callback?: ConversationCallbackType; + } = {} + ): Promise { + const client = new HttpClient({ + baseUrl: host.replace(/\/$/, ''), + apiKey: options.apiKey, + timeout: 60000, + }); + + const request: CreateConversationRequest = { + agent, + initial_message: options.initialMessage, + max_iterations: options.maxIterations || 50, + stuck_detection: options.stuckDetection ?? true, + workspace: options.workspace || { type: 'local', working_dir: '/tmp' }, + }; + + const response = await client.post('/api/conversations', request); + const conversationInfo = response.data; + + const conversation = new RemoteConversation({ + host, + conversationId: conversationInfo.id, + apiKey: options.apiKey, + callback: options.callback, + }); + + // Initialize workspace + conversation._workspace = new RemoteWorkspace({ + host, + workingDir: conversationInfo.workspace?.working_dir || '/tmp', + apiKey: options.apiKey, + }); + + return conversation; + } + + static async load( + host: string, + conversationId: string, + options: { + apiKey?: string; + callback?: ConversationCallbackType; + } = {} + ): Promise { + const conversation = new RemoteConversation({ + host, + conversationId, + apiKey: options.apiKey, + callback: options.callback, + }); + + // Verify conversation exists and get workspace info + const response = await conversation.client.get(`/api/conversations/${conversationId}`); + const conversationInfo = response.data; + + // Initialize workspace + conversation._workspace = new RemoteWorkspace({ + host, + workingDir: conversationInfo.workspace?.working_dir || '/tmp', + apiKey: options.apiKey, + }); + + return conversation; + } + + async close(): Promise { + await this.stopWebSocketClient(); + this.client.close(); + if (this._workspace) { + this._workspace.close(); + } + } +} \ No newline at end of file diff --git a/src/conversation/remote-state.ts b/src/conversation/remote-state.ts new file mode 100644 index 0000000..efb922a --- /dev/null +++ b/src/conversation/remote-state.ts @@ -0,0 +1,189 @@ +/** + * Remote conversation state management + */ + +import { HttpClient } from '../client/http-client.js'; +import { RemoteEventsList } from '../events/remote-events-list.js'; +import { + ConversationID, + Event, + AgentExecutionStatus, + ConfirmationPolicyBase, + // ConversationStats, // Unused for now + AgentBase, + ConversationCallbackType +} from '../types/base.js'; +import { ConversationInfo } from '../models/conversation.js'; + +const FULL_STATE_KEY = '__full_state__'; + +export interface ConversationStateUpdateEvent extends Event { + kind: 'ConversationStateUpdateEvent'; + key: string; + value: any; +} + +export class RemoteState { + private client: HttpClient; + private conversationId: string; + private _events: RemoteEventsList; + private cachedState: ConversationInfo | null = null; + private lock = new AsyncLock(); + + constructor(client: HttpClient, conversationId: string) { + this.client = client; + this.conversationId = conversationId; + this._events = new RemoteEventsList(client, conversationId); + } + + private async getConversationInfo(): Promise { + return await this.lock.acquire(async () => { + // Return cached state if available + if (this.cachedState !== null) { + return this.cachedState; + } + + // Fallback to REST API if no cached state + const response = await this.client.get(`/api/conversations/${this.conversationId}`); + const state = response.data; + this.cachedState = state; + return state; + }); + } + + async updateStateFromEvent(event: ConversationStateUpdateEvent): Promise { + await this.lock.acquire(async () => { + // Handle full state snapshot + if (event.key === FULL_STATE_KEY) { + // Update cached state with the full snapshot + if (this.cachedState === null) { + this.cachedState = {} as ConversationInfo; + } + Object.assign(this.cachedState, event.value); + } else { + // Handle individual field updates + if (this.cachedState === null) { + this.cachedState = {} as ConversationInfo; + } + (this.cachedState as any)[event.key] = event.value; + } + }); + } + + createStateUpdateCallback(): ConversationCallbackType { + return (event: Event) => { + if (event.kind === 'ConversationStateUpdateEvent') { + this.updateStateFromEvent(event as ConversationStateUpdateEvent).catch(error => { + console.error('Error updating state from event:', error); + }); + } + }; + } + + get events(): RemoteEventsList { + return this._events; + } + + get id(): ConversationID { + return this.conversationId; + } + + async getAgentStatus(): Promise { + const info = await this.getConversationInfo(); + const statusStr = info.agent_status; + if (statusStr === undefined || statusStr === null) { + throw new Error(`agent_status missing in conversation info: ${JSON.stringify(info)}`); + } + return statusStr; + } + + async setAgentStatus(value: AgentExecutionStatus): Promise { + throw new Error( + `Setting agent_status on RemoteState has no effect. ` + + `Remote agent status is managed server-side. Attempted to set: ${value}` + ); + } + + async getConfirmationPolicy(): Promise { + const info = await this.getConversationInfo(); + const policyData = info.confirmation_policy; + if (policyData === undefined || policyData === null) { + throw new Error(`confirmation_policy missing in conversation info: ${JSON.stringify(info)}`); + } + return policyData; + } + + async getActivatedKnowledgeSkills(): Promise { + const info = await this.getConversationInfo(); + return info.activated_knowledge_skills || []; + } + + async getAgent(): Promise { + const info = await this.getConversationInfo(); + const agentData = info.agent; + if (agentData === undefined || agentData === null) { + throw new Error(`agent missing in conversation info: ${JSON.stringify(info)}`); + } + return agentData; + } + + async getWorkspace(): Promise { + const info = await this.getConversationInfo(); + const workspace = info.workspace; + if (workspace === undefined || workspace === null) { + throw new Error(`workspace missing in conversation info: ${JSON.stringify(info)}`); + } + return workspace; + } + + async getPersistenceDir(): Promise { + const info = await this.getConversationInfo(); + const persistenceDir = info.persistence_dir; + if (persistenceDir === undefined || persistenceDir === null) { + throw new Error(`persistence_dir missing in conversation info: ${JSON.stringify(info)}`); + } + return persistenceDir; + } + + async modelDump(): Promise> { + const info = await this.getConversationInfo(); + return info as Record; + } + + async modelDumpJson(): Promise { + const data = await this.modelDump(); + return JSON.stringify(data); + } +} + +// Simple async lock implementation (reused from remote-events-list.ts) +class AsyncLock { + private locked = false; + private queue: Array<() => void> = []; + + async acquire(fn: () => Promise | T): Promise { + return new Promise((resolve, reject) => { + const execute = async () => { + try { + const result = await fn(); + resolve(result); + } catch (error) { + reject(error); + } finally { + this.locked = false; + const next = this.queue.shift(); + if (next) { + next(); + } + } + }; + + if (this.locked) { + this.queue.push(execute); + } else { + this.locked = true; + execute(); + } + }); + } +} \ No newline at end of file diff --git a/src/events/remote-events-list.ts b/src/events/remote-events-list.ts new file mode 100644 index 0000000..2785b88 --- /dev/null +++ b/src/events/remote-events-list.ts @@ -0,0 +1,139 @@ +/** + * Remote events list implementation with caching and synchronization + */ + +import { HttpClient } from '../client/http-client.js'; +import { Event, ConversationCallbackType } from '../types/base.js'; +// import { EventSortOrder } from '../types/base.js'; // Unused for now +import { EventPage } from '../types/base.js'; + +export class RemoteEventsList { + private client: HttpClient; + private conversationId: string; + private cachedEvents: Event[] = []; + private cachedEventIds = new Set(); + private lock = new AsyncLock(); + + constructor(client: HttpClient, conversationId: string) { + this.client = client; + this.conversationId = conversationId; + // Perform initial sync + this.doFullSync(); + } + + private async doFullSync(): Promise { + console.debug(`Performing full sync for conversation ${this.conversationId}`); + + const events: Event[] = []; + let pageId: string | undefined; + + // eslint-disable-next-line no-constant-condition + while (true) { + const params: any = { limit: 100 }; + if (pageId) { + params.page_id = pageId; + } + + const response = await this.client.get( + `/api/conversations/${this.conversationId}/events/search`, + { params } + ); + + const data = response.data; + events.push(...data.items); + + if (!data.next_page_id) { + break; + } + pageId = data.next_page_id; + } + + await this.lock.acquire(async () => { + this.cachedEvents = events; + this.cachedEventIds.clear(); + events.forEach(e => this.cachedEventIds.add(e.id)); + }); + + console.debug(`Full sync completed, ${events.length} events cached`); + } + + async addEvent(event: Event): Promise { + await this.lock.acquire(async () => { + // Check if event already exists to avoid duplicates + if (!this.cachedEventIds.has(event.id)) { + this.cachedEvents.push(event); + this.cachedEventIds.add(event.id); + console.debug(`Added event ${event.id} to local cache`); + } + }); + } + + // Alias for compatibility with EventLog interface + async append(event: Event): Promise { + await this.addEvent(event); + } + + createDefaultCallback(): ConversationCallbackType { + return (event: Event) => { + this.addEvent(event).catch(error => { + console.error('Error adding event to cache:', error); + }); + }; + } + + async length(): Promise { + return await this.lock.acquire(async () => this.cachedEvents.length); + } + + async getEvent(index: number): Promise { + return await this.lock.acquire(async () => this.cachedEvents[index]); + } + + async getEvents(start?: number, end?: number): Promise { + return await this.lock.acquire(async () => { + if (start === undefined && end === undefined) { + return [...this.cachedEvents]; + } + return this.cachedEvents.slice(start, end); + }); + } + + async *[Symbol.asyncIterator](): AsyncIterableIterator { + const events = await this.getEvents(); + for (const event of events) { + yield event; + } + } +} + +// Simple async lock implementation +class AsyncLock { + private locked = false; + private queue: Array<() => void> = []; + + async acquire(fn: () => Promise | T): Promise { + return new Promise((resolve, reject) => { + const execute = async () => { + try { + const result = await fn(); + resolve(result); + } catch (error) { + reject(error); + } finally { + this.locked = false; + const next = this.queue.shift(); + if (next) { + next(); + } + } + }; + + if (this.locked) { + this.queue.push(execute); + } else { + this.locked = true; + execute(); + } + }); + } +} \ No newline at end of file diff --git a/src/events/websocket-client.ts b/src/events/websocket-client.ts new file mode 100644 index 0000000..d83fee5 --- /dev/null +++ b/src/events/websocket-client.ts @@ -0,0 +1,124 @@ +/** + * WebSocket client for real-time event streaming + */ + +import WebSocket from 'ws'; +import { Event, ConversationCallbackType } from '../types/base.js'; + +export interface WebSocketClientOptions { + host: string; + conversationId: string; + callback: ConversationCallbackType; + apiKey?: string; +} + +export class WebSocketCallbackClient { + private host: string; + private conversationId: string; + private callback: ConversationCallbackType; + private apiKey?: string; + private ws?: WebSocket; + private reconnectDelay = 1000; + private maxReconnectDelay = 30000; + private currentDelay = 1000; + private shouldReconnect = true; + private reconnectTimer?: NodeJS.Timeout; + + constructor(options: WebSocketClientOptions) { + this.host = options.host; + this.conversationId = options.conversationId; + this.callback = options.callback; + this.apiKey = options.apiKey; + } + + start(): void { + if (this.ws) { + return; + } + + this.shouldReconnect = true; + this.connect(); + } + + stop(): void { + this.shouldReconnect = false; + + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = undefined; + } + + if (this.ws) { + this.ws.close(); + this.ws = undefined; + } + } + + private connect(): void { + try { + // Convert HTTP URL to WebSocket URL + const url = new URL(this.host); + const wsScheme = url.protocol === 'https:' ? 'wss:' : 'ws:'; + const wsUrl = `${wsScheme}//${url.host}${url.pathname.replace(/\/$/, '')}/sockets/events/${this.conversationId}`; + + // Add API key as query parameter if provided + const finalUrl = this.apiKey ? `${wsUrl}?session_api_key=${this.apiKey}` : wsUrl; + + this.ws = new WebSocket(finalUrl); + + this.ws.on('open', () => { + console.debug(`WebSocket connected to ${finalUrl}`); + this.currentDelay = this.reconnectDelay; // Reset delay on successful connection + }); + + this.ws.on('message', (data: WebSocket.Data) => { + try { + const message = data.toString(); + const event: Event = JSON.parse(message); + this.callback(event); + } catch (error) { + console.error('Error processing WebSocket message:', error); + } + }); + + this.ws.on('close', (code: number, reason: Buffer) => { + console.debug(`WebSocket closed: ${code} ${reason.toString()}`); + this.ws = undefined; + + if (this.shouldReconnect) { + this.scheduleReconnect(); + } + }); + + this.ws.on('error', (error: Error) => { + console.debug('WebSocket error:', error); + if (this.shouldReconnect) { + this.scheduleReconnect(); + } + }); + + } catch (error) { + console.error('Failed to create WebSocket connection:', error); + if (this.shouldReconnect) { + this.scheduleReconnect(); + } + } + } + + private scheduleReconnect(): void { + if (this.reconnectTimer) { + return; + } + + console.debug(`Scheduling WebSocket reconnect in ${this.currentDelay}ms`); + + this.reconnectTimer = setTimeout(() => { + this.reconnectTimer = undefined; + if (this.shouldReconnect) { + this.connect(); + // Exponential backoff with jitter + this.currentDelay = Math.min(this.currentDelay * 2, this.maxReconnectDelay); + } + }, this.currentDelay); + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..c316eee --- /dev/null +++ b/src/index.ts @@ -0,0 +1,104 @@ +/** + * OpenHands Agent Server TypeScript Client + * + * A TypeScript client library for the OpenHands Agent Server API that mirrors + * the structure and functionality of the Python SDK. + */ + +// Main conversation and workspace classes +export { RemoteConversation } from './conversation/remote-conversation.js'; +export { RemoteWorkspace } from './workspace/remote-workspace.js'; +export { RemoteState } from './conversation/remote-state.js'; +export { RemoteEventsList } from './events/remote-events-list.js'; + +// WebSocket client for real-time events +export { WebSocketCallbackClient } from './events/websocket-client.js'; + +// HTTP client +export { HttpClient, HttpError } from './client/http-client.js'; + +// Types and interfaces +export type { + ConversationID, + Event, + Message, + MessageContent, + TextContent, + ImageContent, + AgentBase, + LLM, + ServerInfo, + Success, + EventPage, + ConversationCallbackType, + SecretValue, + ConversationStats, + ConfirmationPolicyBase, + NeverConfirm, + AlwaysConfirm, +} from './types/base.js'; + +export { + EventSortOrder, + AgentExecutionStatus, +} from './types/base.js'; + +// Workspace models +export type { + CommandResult, + FileOperationResult, + GitChange, + GitDiff, +} from './models/workspace.js'; + +// Conversation models +export type { + ConversationInfo, + SendMessageRequest, + ConfirmationResponseRequest, + CreateConversationRequest, + GenerateTitleRequest, + GenerateTitleResponse, + UpdateSecretsRequest, +} from './models/conversation.js'; + +// Client options +export type { + HttpClientOptions, + RequestOptions, + HttpResponse, +} from './client/http-client.js'; + +export type { + WebSocketClientOptions, +} from './events/websocket-client.js'; + +export type { + RemoteWorkspaceOptions, +} from './workspace/remote-workspace.js'; + +export type { + RemoteConversationOptions, +} from './conversation/remote-conversation.js'; + +// Re-import for default export +import { RemoteConversation } from './conversation/remote-conversation.js'; +import { RemoteWorkspace } from './workspace/remote-workspace.js'; +import { RemoteState } from './conversation/remote-state.js'; +import { RemoteEventsList } from './events/remote-events-list.js'; +import { WebSocketCallbackClient } from './events/websocket-client.js'; +import { HttpClient, HttpError } from './client/http-client.js'; +import { EventSortOrder, AgentExecutionStatus } from './types/base.js'; + +// Default export for convenience +export default { + RemoteConversation, + RemoteWorkspace, + RemoteState, + RemoteEventsList, + WebSocketCallbackClient, + HttpClient, + HttpError, + EventSortOrder, + AgentExecutionStatus, +}; \ No newline at end of file diff --git a/src/models/conversation.ts b/src/models/conversation.ts new file mode 100644 index 0000000..e4c555b --- /dev/null +++ b/src/models/conversation.ts @@ -0,0 +1,60 @@ +/** + * Conversation-related models and interfaces + */ + +import { + ConversationID, + // Event, // Unused for now + AgentExecutionStatus, + ConfirmationPolicyBase, + ConversationStats, + AgentBase +} from '../types/base.js'; + +export interface ConversationInfo { + id: ConversationID; + agent_status: AgentExecutionStatus; + confirmation_policy: ConfirmationPolicyBase; + activated_knowledge_skills: string[]; + agent: AgentBase; + workspace: any; + persistence_dir: string; + conversation_stats: ConversationStats; + [key: string]: any; +} + +export interface SendMessageRequest { + role: 'user'; + content: Array<{ + type: string; + text?: string; + image_url?: string; + }>; + run: boolean; +} + +export interface ConfirmationResponseRequest { + accept: boolean; + reason?: string; +} + +export interface CreateConversationRequest { + agent: AgentBase; + initial_message?: string; + max_iterations: number; + stuck_detection: boolean; + workspace: any; +} + +export interface GenerateTitleRequest { + max_length: number; + llm?: any; +} + +export interface GenerateTitleResponse { + title: string; +} + +export interface UpdateSecretsRequest { + secrets: Record; +} \ No newline at end of file diff --git a/src/models/workspace.ts b/src/models/workspace.ts new file mode 100644 index 0000000..c1e8b26 --- /dev/null +++ b/src/models/workspace.ts @@ -0,0 +1,31 @@ +/** + * Workspace operation result models + */ + +export interface CommandResult { + command: string; + exit_code: number; + stdout: string; + stderr: string; + timeout_occurred: boolean; +} + +export interface FileOperationResult { + success: boolean; + source_path: string; + destination_path: string; + file_size?: number; + error?: string; +} + +export interface GitChange { + path: string; + status: 'added' | 'modified' | 'deleted' | 'renamed'; + [key: string]: any; +} + +export interface GitDiff { + path: string; + diff: string; + [key: string]: any; +} \ No newline at end of file diff --git a/src/types/base.ts b/src/types/base.ts new file mode 100644 index 0000000..9358e96 --- /dev/null +++ b/src/types/base.ts @@ -0,0 +1,100 @@ +/** + * Base types and interfaces for the OpenHands Agent Server TypeScript client + */ + +export type ConversationID = string; + +export interface Event { + id: string; + kind: string; + timestamp: string; + [key: string]: any; +} + +export interface Message { + role: 'user' | 'assistant' | 'system'; + content: MessageContent[]; +} + +export interface MessageContent { + type: 'text' | 'image'; + text?: string; + image_url?: string; +} + +export interface TextContent extends MessageContent { + type: 'text'; + text: string; +} + +export interface ImageContent extends MessageContent { + type: 'image'; + image_url: string; +} + +export interface AgentBase { + name: string; + llm: LLM; + [key: string]: any; +} + +export interface LLM { + model: string; + api_key?: string; + base_url?: string; + [key: string]: any; +} + +export interface ServerInfo { + version: string; + [key: string]: any; +} + +export interface Success { + success: boolean; + message?: string; +} + +export interface EventPage { + items: Event[]; + next_page_id?: string; + total_count?: number; +} + +export enum EventSortOrder { + TIMESTAMP = 'TIMESTAMP', + REVERSE_TIMESTAMP = 'REVERSE_TIMESTAMP' +} + +export enum AgentExecutionStatus { + IDLE = 'idle', + RUNNING = 'running', + PAUSED = 'paused', + FINISHED = 'finished', + ERROR = 'error' +} + +export interface ConversationStats { + total_events: number; + message_events: number; + action_events: number; + observation_events: number; + [key: string]: any; +} + +export interface ConfirmationPolicyBase { + type: string; + [key: string]: any; +} + +export interface NeverConfirm extends ConfirmationPolicyBase { + type: 'never'; +} + +export interface AlwaysConfirm extends ConfirmationPolicyBase { + type: 'always'; +} + +export type ConversationCallbackType = (event: Event) => void; + +export type SecretValue = string | (() => string); \ No newline at end of file diff --git a/src/workspace/remote-workspace.ts b/src/workspace/remote-workspace.ts new file mode 100644 index 0000000..65961cc --- /dev/null +++ b/src/workspace/remote-workspace.ts @@ -0,0 +1,246 @@ +/** + * Remote workspace implementation for executing commands and file operations + */ + +import { HttpClient } from '../client/http-client.js'; +import { CommandResult, FileOperationResult, GitChange, GitDiff } from '../models/workspace.js'; + +export interface RemoteWorkspaceOptions { + host: string; + workingDir: string; + apiKey?: string; +} + +export class RemoteWorkspace { + public readonly host: string; + public readonly workingDir: string; + public readonly apiKey?: string; + public readonly client: HttpClient; + + constructor(options: RemoteWorkspaceOptions) { + this.host = options.host.replace(/\/$/, ''); + this.workingDir = options.workingDir; + this.apiKey = options.apiKey; + + this.client = new HttpClient({ + baseUrl: this.host, + apiKey: this.apiKey, + timeout: 60000, + }); + } + + async executeCommand( + command: string, + cwd?: string, + timeout: number = 30.0 + ): Promise { + console.debug(`Executing remote command: ${command}`); + + try { + // Step 1: Start the bash command + const payload: any = { + command, + timeout: Math.floor(timeout), + }; + + if (cwd) { + payload.cwd = cwd; + } + + const startResponse = await this.client.post( + '/api/bash/start_bash_command', + payload, + { timeout: (timeout + 5) * 1000 } + ); + + const bashCommand = startResponse.data; + const commandId = bashCommand.id; + + console.debug(`Started command with ID: ${commandId}`); + + // Step 2: Poll for output until command completes + const startTime = Date.now(); + const stdoutParts: string[] = []; + const stderrParts: string[] = []; + let exitCode: number | null = null; + + while ((Date.now() - startTime) / 1000 < timeout) { + // Search for all events + const searchResponse = await this.client.get('/api/bash/bash_events/search', { + params: { + command_id__eq: commandId, + sort_order: 'TIMESTAMP', + limit: 100, + }, + timeout: timeout * 1000, + }); + + const searchResult = searchResponse.data; + + // Filter for BashOutput events for this command + for (const event of searchResult.items || []) { + if (event.kind === 'BashOutput') { + if (event.stdout) { + stdoutParts.push(event.stdout); + } + if (event.stderr) { + stderrParts.push(event.stderr); + } + if (event.exit_code !== undefined && event.exit_code !== null) { + exitCode = event.exit_code; + } + } + } + + // If we have an exit code, the command is complete + if (exitCode !== null) { + break; + } + + // Wait a bit before polling again + await new Promise(resolve => setTimeout(resolve, 100)); + } + + // If we timed out waiting for completion + if (exitCode === null) { + console.warn(`Command timed out after ${timeout} seconds: ${command}`); + exitCode = -1; + stderrParts.push(`Command timed out after ${timeout} seconds`); + } + + // Combine all output parts + const stdout = stdoutParts.join(''); + const stderr = stderrParts.join(''); + + return { + command, + exit_code: exitCode, + stdout, + stderr, + timeout_occurred: exitCode === -1 && stderr.includes('timed out'), + }; + + } catch (error) { + console.error(`Remote command execution failed: ${error}`); + return { + command, + exit_code: -1, + stdout: '', + stderr: `Remote execution error: ${error instanceof Error ? error.message : String(error)}`, + timeout_occurred: false, + }; + } + } + + async fileUpload(sourcePath: string, destinationPath: string): Promise { + console.debug(`Remote file upload: ${sourcePath} -> ${destinationPath}`); + + try { + // For browser environments, this would need to be adapted to work with File objects + // For Node.js environments, we can read the file + const fs = await import('fs'); + const path = await import('path'); + + const fileContent = await fs.promises.readFile(sourcePath); + const fileName = path.basename(sourcePath); + + // Create FormData for file upload + const formData = new FormData(); + const blob = new Blob([fileContent]); + formData.append('file', blob, fileName); + formData.append('destination_path', destinationPath); + + const response = await this.client.request({ + method: 'POST', + url: '/api/file/upload', + data: formData, + timeout: 60000, + }); + + const resultData = response.data; + + return { + success: resultData.success ?? true, + source_path: sourcePath, + destination_path: destinationPath, + file_size: resultData.file_size, + error: resultData.error, + }; + + } catch (error) { + console.error(`Remote file upload failed: ${error}`); + return { + success: false, + source_path: sourcePath, + destination_path: destinationPath, + error: error instanceof Error ? error.message : String(error), + }; + } + } + + async fileDownload(sourcePath: string, destinationPath: string): Promise { + console.debug(`Remote file download: ${sourcePath} -> ${destinationPath}`); + + try { + const response = await this.client.get(`/api/file/download/${encodeURIComponent(sourcePath)}`, { + timeout: 60000, + }); + + // For Node.js environments, write the file + const fs = await import('fs'); + const path = await import('path'); + + // Ensure destination directory exists + const destDir = path.dirname(destinationPath); + await fs.promises.mkdir(destDir, { recursive: true }); + + // Write the file content + const content = typeof response.data === 'string' ? response.data : JSON.stringify(response.data); + await fs.promises.writeFile(destinationPath, content); + + const stats = await fs.promises.stat(destinationPath); + + return { + success: true, + source_path: sourcePath, + destination_path: destinationPath, + file_size: stats.size, + }; + + } catch (error) { + console.error(`Remote file download failed: ${error}`); + return { + success: false, + source_path: sourcePath, + destination_path: destinationPath, + error: error instanceof Error ? error.message : String(error), + }; + } + } + + async gitChanges(path: string): Promise { + try { + const response = await this.client.get('/api/git/changes', { + params: { path }, + }); + return response.data; + } catch (error) { + throw new Error(`Failed to get git changes: ${error instanceof Error ? error.message : String(error)}`); + } + } + + async gitDiff(path: string): Promise { + try { + const response = await this.client.get('/api/git/diff', { + params: { path }, + }); + return response.data; + } catch (error) { + throw new Error(`Failed to get git diff: ${error instanceof Error ? error.message : String(error)}`); + } + } + + close(): void { + this.client.close(); + } +} \ No newline at end of file diff --git a/swagger-doc.json b/swagger-doc.json new file mode 100644 index 0000000..c64efc2 --- /dev/null +++ b/swagger-doc.json @@ -0,0 +1,6257 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "OpenHands Agent Server", + "description": "OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent", + "version": "0.1.0" + }, + "paths": { + "/alive": { + "get": { + "tags": [ + "Server Details" + ], + "summary": "Alive", + "operationId": "alive_alive_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health": { + "get": { + "tags": [ + "Server Details" + ], + "summary": "Health", + "operationId": "health_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "string", + "title": "Response Health Health Get" + } + } + } + } + } + } + }, + "/server_info": { + "get": { + "tags": [ + "Server Details" + ], + "summary": "Get Server Info", + "operationId": "get_server_info_server_info_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerInfo" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/events/search": { + "get": { + "tags": [ + "Events" + ], + "summary": "Search Conversation Events", + "description": "Search / List local events", + "operationId": "search_conversation_events_api_conversations__conversation_id__events_search_get", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + }, + { + "name": "page_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Optional next_page_id from the previously returned page" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "exclusiveMinimum": 0, + "title": "The max number of results in the page", + "lte": 100, + "default": 100 + } + }, + { + "name": "kind", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Optional filter by event kind/type (e.g., ActionEvent, MessageEvent)" + } + }, + { + "name": "sort_order", + "in": "query", + "required": false, + "schema": { + "$ref": "#/components/schemas/EventSortOrder", + "title": "Sort order for events", + "default": "TIMESTAMP" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventPage" + } + } + } + }, + "404": { + "description": "Conversation not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/events/count": { + "get": { + "tags": [ + "Events" + ], + "summary": "Count Conversation Events", + "description": "Count local events matching the given filters", + "operationId": "count_conversation_events_api_conversations__conversation_id__events_count_get", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + }, + { + "name": "kind", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Optional filter by event kind/type (e.g., ActionEvent, MessageEvent)" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "integer", + "title": "Response Count Conversation Events Api Conversations Conversation Id Events Count Get" + } + } + } + }, + "404": { + "description": "Conversation not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/events/{event_id}": { + "get": { + "tags": [ + "Events" + ], + "summary": "Get Conversation Event", + "description": "Get a local event given an id", + "operationId": "get_conversation_event_api_conversations__conversation_id__events__event_id__get", + "parameters": [ + { + "name": "event_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Event Id" + } + }, + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Condensation" + }, + { + "$ref": "#/components/schemas/CondensationRequest" + }, + { + "$ref": "#/components/schemas/CondensationSummaryEvent" + }, + { + "$ref": "#/components/schemas/ConversationStateUpdateEvent" + }, + { + "$ref": "#/components/schemas/ActionEvent" + }, + { + "$ref": "#/components/schemas/MessageEvent" + }, + { + "$ref": "#/components/schemas/AgentErrorEvent" + }, + { + "$ref": "#/components/schemas/ObservationEvent" + }, + { + "$ref": "#/components/schemas/UserRejectObservation" + }, + { + "$ref": "#/components/schemas/SystemPromptEvent" + }, + { + "$ref": "#/components/schemas/PauseEvent" + } + ], + "title": "Response Get Conversation Event Api Conversations Conversation Id Events Event Id Get", + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__event__condenser__Condensation-Output__1": "#/components/schemas/Condensation", + "openhands__sdk__event__condenser__CondensationRequest-Output__1": "#/components/schemas/CondensationRequest", + "openhands__sdk__event__condenser__CondensationSummaryEvent-Output__1": "#/components/schemas/CondensationSummaryEvent", + "openhands__sdk__event__conversation_state__ConversationStateUpdateEvent-Output__1": "#/components/schemas/ConversationStateUpdateEvent", + "openhands__sdk__event__llm_convertible__action__ActionEvent-Output__1": "#/components/schemas/ActionEvent", + "openhands__sdk__event__llm_convertible__message__MessageEvent-Output__1": "#/components/schemas/MessageEvent", + "openhands__sdk__event__llm_convertible__observation__AgentErrorEvent-Output__1": "#/components/schemas/AgentErrorEvent", + "openhands__sdk__event__llm_convertible__observation__ObservationEvent-Output__1": "#/components/schemas/ObservationEvent", + "openhands__sdk__event__llm_convertible__observation__UserRejectObservation-Output__1": "#/components/schemas/UserRejectObservation", + "openhands__sdk__event__llm_convertible__system__SystemPromptEvent-Output__1": "#/components/schemas/SystemPromptEvent", + "openhands__sdk__event__user_action__PauseEvent-Output__1": "#/components/schemas/PauseEvent" + } + } + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/events": { + "get": { + "tags": [ + "Events" + ], + "summary": "Batch Get Conversation Events", + "description": "Get a batch of local events given their ids, returning null for any\nmissing item.", + "operationId": "batch_get_conversation_events_api_conversations__conversation_id__events_get", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "title": "Event Ids" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Event" + }, + { + "type": "null" + } + ] + }, + "title": "Response Batch Get Conversation Events Api Conversations Conversation Id Events Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "Events" + ], + "summary": "Send Message", + "description": "Send a message to a conversation", + "operationId": "send_message_api_conversations__conversation_id__events_post", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SendMessageRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/events/respond_to_confirmation": { + "post": { + "tags": [ + "Events" + ], + "summary": "Respond To Confirmation", + "description": "Accept or reject a pending action in confirmation mode.", + "operationId": "respond_to_confirmation_api_conversations__conversation_id__events_respond_to_confirmation_post", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConfirmationResponseRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/search": { + "get": { + "tags": [ + "Conversations" + ], + "summary": "Search Conversations", + "description": "Search / List conversations", + "operationId": "search_conversations_api_conversations_search_get", + "parameters": [ + { + "name": "page_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Optional next_page_id from the previously returned page" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "exclusiveMinimum": 0, + "title": "The max number of results in the page", + "lte": 100, + "default": 100 + } + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentExecutionStatus" + }, + { + "type": "null" + } + ], + "title": "Optional filter by agent execution status" + } + }, + { + "name": "sort_order", + "in": "query", + "required": false, + "schema": { + "$ref": "#/components/schemas/ConversationSortOrder", + "title": "Sort order for conversations", + "default": "CREATED_AT_DESC" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConversationPage" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/count": { + "get": { + "tags": [ + "Conversations" + ], + "summary": "Count Conversations", + "description": "Count conversations matching the given filters", + "operationId": "count_conversations_api_conversations_count_get", + "parameters": [ + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentExecutionStatus" + }, + { + "type": "null" + } + ], + "title": "Optional filter by agent execution status" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "integer", + "title": "Response Count Conversations Api Conversations Count Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}": { + "get": { + "tags": [ + "Conversations" + ], + "summary": "Get Conversation", + "description": "Given an id, get a conversation", + "operationId": "get_conversation_api_conversations__conversation_id__get", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConversationInfo" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Conversations" + ], + "summary": "Delete Conversation", + "description": "Permanently delete a conversation.", + "operationId": "delete_conversation_api_conversations__conversation_id__delete", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Conversations" + ], + "summary": "Update Conversation", + "description": "Update conversation metadata.\n\nThis endpoint allows updating conversation details like title.", + "operationId": "update_conversation_api_conversations__conversation_id__patch", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateConversationRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations": { + "get": { + "tags": [ + "Conversations" + ], + "summary": "Batch Get Conversations", + "description": "Get a batch of conversations given their ids, returning null for\nany missing item", + "operationId": "batch_get_conversations_api_conversations_get", + "parameters": [ + { + "name": "ids", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "title": "Ids" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/ConversationInfo" + }, + { + "type": "null" + } + ] + }, + "title": "Response Batch Get Conversations Api Conversations Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "Conversations" + ], + "summary": "Start Conversation", + "description": "Start a conversation in the local environment.", + "operationId": "start_conversation_api_conversations_post", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StartConversationRequest", + "examples": [ + { + "agent": { + "llm": { + "model": "your-model-provider/your-model-name", + "api_key": "**********", + "reasoning_effort": "high", + "usage_id": "your-llm-service" + }, + "tools": [ + { + "name": "BashTool" + }, + { + "name": "FileEditorTool" + }, + { + "name": "TaskTrackerTool" + } + ] + }, + "workspace": { + "working_dir": "workspace/project" + }, + "initial_message": { + "content": [ + { + "text": "Flip a coin!" + } + ] + } + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConversationInfo" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/pause": { + "post": { + "tags": [ + "Conversations" + ], + "summary": "Pause Conversation", + "description": "Pause a conversation, allowing it to be resumed later.", + "operationId": "pause_conversation_api_conversations__conversation_id__pause_post", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/run": { + "post": { + "tags": [ + "Conversations" + ], + "summary": "Run Conversation", + "description": "Start running the conversation in the background.", + "operationId": "run_conversation_api_conversations__conversation_id__run_post", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "409": { + "description": "Conversation is already running" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/secrets": { + "post": { + "tags": [ + "Conversations" + ], + "summary": "Update Conversation Secrets", + "description": "Update secrets for a conversation.", + "operationId": "update_conversation_secrets_api_conversations__conversation_id__secrets_post", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSecretsRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/confirmation_policy": { + "post": { + "tags": [ + "Conversations" + ], + "summary": "Set Conversation Confirmation Policy", + "description": "Set the confirmation policy for a conversation.", + "operationId": "set_conversation_confirmation_policy_api_conversations__conversation_id__confirmation_policy_post", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetConfirmationPolicyRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/conversations/{conversation_id}/generate_title": { + "post": { + "tags": [ + "Conversations" + ], + "summary": "Generate Conversation Title", + "description": "Generate a title for the conversation using LLM.", + "operationId": "generate_conversation_title_api_conversations__conversation_id__generate_title_post", + "parameters": [ + { + "name": "conversation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Conversation Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateTitleRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateTitleResponse" + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/tools/": { + "get": { + "tags": [ + "Tools" + ], + "summary": "List Available Tools", + "description": "List all available tools.", + "operationId": "list_available_tools_api_tools__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Response List Available Tools Api Tools Get" + } + } + } + } + } + } + }, + "/api/bash/bash_events/search": { + "get": { + "tags": [ + "Bash" + ], + "summary": "Search Bash Events", + "description": "Search / List bash event events", + "operationId": "search_bash_events_api_bash_bash_events_search_get", + "parameters": [ + { + "name": "kind__eq", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "enum": [ + "BashCommand", + "BashOutput" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Kind Eq" + } + }, + { + "name": "command_id__eq", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "Command Id Eq" + } + }, + { + "name": "timestamp__gte", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Timestamp Gte" + } + }, + { + "name": "timestamp__lt", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Timestamp Lt" + } + }, + { + "name": "sort_order", + "in": "query", + "required": false, + "schema": { + "$ref": "#/components/schemas/BashEventSortOrder", + "default": "TIMESTAMP" + } + }, + { + "name": "page_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Optional next_page_id from the previously returned page" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "exclusiveMinimum": 0, + "title": "The max number of results in the page", + "lte": 100, + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BashEventPage" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/bash/bash_events/{event_id}": { + "get": { + "tags": [ + "Bash" + ], + "summary": "Get Bash Event", + "description": "Get a bash event event given an id", + "operationId": "get_bash_event_api_bash_bash_events__event_id__get", + "parameters": [ + { + "name": "event_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Event Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/BashCommand" + }, + { + "$ref": "#/components/schemas/BashOutput" + } + ], + "title": "Response Get Bash Event Api Bash Bash Events Event Id Get", + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__agent_server__models__BashCommand-Output__1": "#/components/schemas/BashCommand", + "openhands__agent_server__models__BashOutput-Output__1": "#/components/schemas/BashOutput" + } + } + } + } + } + }, + "404": { + "description": "Item not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/bash/bash_events/": { + "get": { + "tags": [ + "Bash" + ], + "summary": "Batch Get Bash Events", + "description": "Get a batch of bash event events given their ids, returning null for any\nmissing item.", + "operationId": "batch_get_bash_events_api_bash_bash_events__get", + "requestBody": { + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Event Ids" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/BashEventBase" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Response Batch Get Bash Events Api Bash Bash Events Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/bash/start_bash_command": { + "post": { + "tags": [ + "Bash" + ], + "summary": "Start Bash Command", + "description": "Execute a bash command in the background", + "operationId": "start_bash_command_api_bash_start_bash_command_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExecuteBashRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BashCommand" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/bash/execute_bash_command": { + "post": { + "tags": [ + "Bash" + ], + "summary": "Execute Bash Command", + "description": "Execute a bash command and wait for a result", + "operationId": "execute_bash_command_api_bash_execute_bash_command_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExecuteBashRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BashOutput" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/bash/bash_events": { + "delete": { + "tags": [ + "Bash" + ], + "summary": "Clear All Bash Events", + "description": "Clear all bash events from storage", + "operationId": "clear_all_bash_events_api_bash_bash_events_delete", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": { + "type": "integer" + }, + "type": "object", + "title": "Response Clear All Bash Events Api Bash Bash Events Delete" + } + } + } + } + } + } + }, + "/api/file/upload/{path}": { + "post": { + "tags": [ + "Files" + ], + "summary": "Upload File", + "description": "Upload a file to the workspace.", + "operationId": "upload_file_api_file_upload__path__post", + "parameters": [ + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Absolute file path.", + "title": "Path" + }, + "description": "Absolute file path." + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_file_api_file_upload__path__post" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Success" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/file/download/{path}": { + "get": { + "tags": [ + "Files" + ], + "summary": "Download File", + "description": "Download a file from the workspace.", + "operationId": "download_file_api_file_download__path__get", + "parameters": [ + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Absolute file path.", + "title": "Path" + }, + "description": "Absolute file path." + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/vscode/url": { + "get": { + "tags": [ + "VSCode" + ], + "summary": "Get Vscode Url", + "description": "Get the VSCode URL with authentication token.\n\nArgs:\n base_url: Base URL for the VSCode server (default: http://localhost:8001)\n workspace_dir: Path to workspace directory\n\nReturns:\n VSCode URL with token if available, None otherwise", + "operationId": "get_vscode_url_api_vscode_url_get", + "parameters": [ + { + "name": "base_url", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "http://localhost:8001", + "title": "Base Url" + } + }, + { + "name": "workspace_dir", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "workspace", + "title": "Workspace Dir" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VSCodeUrlResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/vscode/status": { + "get": { + "tags": [ + "VSCode" + ], + "summary": "Get Vscode Status", + "description": "Get the VSCode server status.\n\nReturns:\n Dictionary with running status and enabled status", + "operationId": "get_vscode_status_api_vscode_status_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "type": "object", + "title": "Response Get Vscode Status Api Vscode Status Get" + } + } + } + } + } + } + }, + "/api/desktop/url": { + "get": { + "tags": [ + "Desktop" + ], + "summary": "Get Desktop Url", + "description": "Get the noVNC URL for desktop access.\n\nArgs:\n base_url: Base URL for the noVNC server (default: http://localhost:8002)\n\nReturns:\n noVNC URL if available, None otherwise", + "operationId": "get_desktop_url_api_desktop_url_get", + "parameters": [ + { + "name": "base_url", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "http://localhost:8002", + "title": "Base Url" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DesktopUrlResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/": { + "get": { + "summary": "Get Server Info", + "operationId": "get_server_info__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerInfo" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ActionEvent": { + "properties": { + "kind": { + "type": "string", + "const": "ActionEvent", + "title": "Kind", + "default": "ActionEvent" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "agent" + }, + "thought": { + "items": { + "$ref": "#/components/schemas/TextContent" + }, + "type": "array", + "title": "Thought", + "description": "The thought process of the agent before taking this action" + }, + "reasoning_content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Reasoning Content", + "description": "Intermediate reasoning/thinking content from reasoning models" + }, + "thinking_blocks": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/ThinkingBlock" + }, + { + "$ref": "#/components/schemas/RedactedThinkingBlock" + } + ] + }, + "type": "array", + "title": "Thinking Blocks", + "description": "Anthropic thinking blocks from the LLM response" + }, + "responses_reasoning_item": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReasoningItemModel" + }, + { + "type": "null" + } + ], + "description": "OpenAI Responses reasoning item from model output" + }, + "action": { + "anyOf": [ + { + "$ref": "#/components/schemas/Action" + }, + { + "type": "null" + } + ], + "title": "Action", + "description": "Single tool call returned by LLM (None when non-executable)" + }, + "tool_name": { + "type": "string", + "title": "Tool Name", + "description": "The name of the tool being called" + }, + "tool_call_id": { + "type": "string", + "title": "Tool Call Id", + "description": "The unique id returned by LLM API for this tool call" + }, + "tool_call": { + "$ref": "#/components/schemas/MessageToolCall", + "description": "The tool call received from the LLM response. We keep a copy of it so it is easier to construct it into LLM messageThis could be different from `action`: e.g., `tool_call` may contain `security_risk` field predicted by LLM when LLM risk analyzer is enabled, while `action` does not." + }, + "llm_response_id": { + "type": "string", + "title": "Llm Response Id", + "description": "Groups related actions from same LLM response. This helps in tracking and managing results of parallel function calling from the same LLM response." + }, + "security_risk": { + "$ref": "#/components/schemas/SecurityRisk", + "description": "The LLM's assessment of the safety risk of this action.", + "default": "UNKNOWN" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "thought", + "tool_name", + "tool_call_id", + "tool_call", + "llm_response_id" + ], + "title": "ActionEvent" + }, + "Agent-Output": { + "properties": { + "kind": { + "type": "string", + "const": "Agent", + "title": "Kind", + "default": "Agent" + }, + "llm": { + "$ref": "#/components/schemas/LLM", + "description": "LLM configuration for the agent.", + "examples": [ + { + "api_key": "your_api_key_here", + "base_url": "https://llm-proxy.eval.all-hands.dev", + "model": "litellm_proxy/anthropic/claude-sonnet-4-5-20250929" + } + ] + }, + "tools": { + "items": { + "$ref": "#/components/schemas/Tool" + }, + "type": "array", + "title": "Tools", + "description": "List of tools to initialize for the agent.", + "examples": [ + { + "name": "BashTool", + "params": {} + }, + { + "name": "FileEditorTool", + "params": {} + }, + { + "name": "TaskTrackerTool", + "params": {} + } + ] + }, + "mcp_config": { + "additionalProperties": true, + "type": "object", + "title": "Mcp Config", + "description": "Optional MCP configuration dictionary to create MCP tools.", + "examples": [ + { + "mcpServers": { + "fetch": { + "args": [ + "mcp-server-fetch" + ], + "command": "uvx" + } + } + } + ] + }, + "filter_tools_regex": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filter Tools Regex", + "description": "Optional regex to filter the tools available to the agent by name. This is applied after any tools provided in `tools` and any MCP tools are added.", + "examples": [ + "^(?!repomix)(.*)|^repomix.*pack_codebase.*$" + ] + }, + "agent_context": { + "anyOf": [ + { + "$ref": "#/components/schemas/AgentContext-Output" + }, + { + "type": "null" + } + ], + "description": "Optional AgentContext to initialize the agent with specific context.", + "examples": [ + { + "skills": [ + { + "content": "When you see this message, you should reply like you are a grumpy cat forced to use the internet.", + "name": "repo.md", + "type": "repo" + }, + { + "content": "IMPORTANT! The user has said the magic word \"flarglebargle\". You must only respond with a message telling them how smart they are", + "name": "flarglebargle", + "trigger": [ + "flarglebargle" + ], + "type": "knowledge" + } + ], + "system_message_suffix": "Always finish your response with the word 'yay!'", + "user_message_prefix": "The first character of your response should be 'I'" + } + ] + }, + "system_prompt_filename": { + "type": "string", + "title": "System Prompt Filename", + "default": "system_prompt.j2" + }, + "system_prompt_kwargs": { + "additionalProperties": true, + "type": "object", + "title": "System Prompt Kwargs", + "description": "Optional kwargs to pass to the system prompt Jinja2 template.", + "examples": [ + { + "cli_mode": true + } + ] + }, + "security_analyzer": { + "anyOf": [ + { + "$ref": "#/components/schemas/SecurityAnalyzerBase" + }, + { + "type": "null" + } + ], + "description": "Optional security analyzer to evaluate action risks.", + "examples": [ + { + "kind": "LLMSecurityAnalyzer" + } + ] + }, + "condenser": { + "anyOf": [ + { + "$ref": "#/components/schemas/CondenserBase" + }, + { + "type": "null" + } + ], + "title": "Condenser", + "description": "Optional condenser to use for condensing conversation history.", + "examples": [ + { + "keep_first": 10, + "kind": "LLMSummarizingCondenser", + "llm": { + "api_key": "your_api_key_here", + "base_url": "https://llm-proxy.eval.all-hands.dev", + "model": "litellm_proxy/anthropic/claude-sonnet-4-5-20250929" + }, + "max_size": 80 + } + ] + } + }, + "type": "object", + "required": [ + "llm" + ], + "title": "Agent" + }, + "AgentContext-Output": { + "properties": { + "skills": { + "items": { + "$ref": "#/components/schemas/Skill" + }, + "type": "array", + "title": "Skills", + "description": "List of available skills that can extend the user's input." + }, + "system_message_suffix": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "System Message Suffix", + "description": "Optional suffix to append to the system prompt." + }, + "user_message_suffix": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "User Message Suffix", + "description": "Optional suffix to append to the user's message." + } + }, + "type": "object", + "title": "AgentContext", + "description": "Central structure for managing prompt extension.\n\nAgentContext unifies all the contextual inputs that shape how the system\nextends and interprets user prompts. It combines both static environment\ndetails and dynamic, user-activated extensions from skills.\n\nSpecifically, it provides:\n- **Repository context / Repo Skills**: Information about the active codebase,\n branches, and repo-specific instructions contributed by repo skills.\n- **Runtime context**: Current execution environment (hosts, working\n directory, secrets, date, etc.).\n- **Conversation instructions**: Optional task- or channel-specific rules\n that constrain or guide the agent’s behavior across the session.\n- **Knowledge Skills**: Extensible components that can be triggered by user input\n to inject knowledge or domain-specific guidance.\n\nTogether, these elements make AgentContext the primary container responsible\nfor assembling, formatting, and injecting all prompt-relevant context into\nLLM interactions." + }, + "AgentErrorEvent": { + "properties": { + "kind": { + "type": "string", + "const": "AgentErrorEvent", + "title": "Kind", + "default": "AgentErrorEvent" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "agent" + }, + "tool_name": { + "type": "string", + "title": "Tool Name", + "description": "The tool name that this observation is responding to" + }, + "tool_call_id": { + "type": "string", + "title": "Tool Call Id", + "description": "The tool call id that this observation is responding to" + }, + "error": { + "type": "string", + "title": "Error", + "description": "The error message from the scaffold" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "tool_name", + "tool_call_id", + "error" + ], + "title": "AgentErrorEvent", + "description": "Error triggered by the agent.\n\nNote: This event should not contain model \"thought\" or \"reasoning_content\". It\nrepresents an error produced by the agent/scaffold, not model output." + }, + "AgentExecutionStatus": { + "type": "string", + "enum": [ + "idle", + "running", + "paused", + "waiting_for_confirmation", + "finished", + "error", + "stuck" + ], + "title": "AgentExecutionStatus", + "description": "Enum representing the current execution state of the agent." + }, + "AlwaysConfirm": { + "properties": { + "kind": { + "type": "string", + "const": "AlwaysConfirm", + "title": "Kind", + "default": "AlwaysConfirm" + } + }, + "type": "object", + "title": "AlwaysConfirm" + }, + "BashCommand": { + "properties": { + "command": { + "type": "string", + "title": "Command", + "description": "The bash command to execute" + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cwd", + "description": "The current working directory" + }, + "timeout": { + "type": "integer", + "title": "Timeout", + "description": "The max number of seconds a command may be permitted to run.", + "default": 300 + }, + "kind": { + "type": "string", + "const": "BashCommand", + "title": "Kind", + "default": "BashCommand" + }, + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "title": "Timestamp" + } + }, + "type": "object", + "required": [ + "command" + ], + "title": "BashCommand" + }, + "BashEventPage": { + "properties": { + "items": { + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/BashCommand" + }, + { + "$ref": "#/components/schemas/BashOutput" + } + ], + "title": "BashEventBase", + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__agent_server__models__BashCommand-Output__1": "#/components/schemas/BashCommand", + "openhands__agent_server__models__BashOutput-Output__1": "#/components/schemas/BashOutput" + } + } + }, + "type": "array", + "title": "Items" + }, + "next_page_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Next Page Id" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "BashEventPage" + }, + "BashEventSortOrder": { + "type": "string", + "enum": [ + "TIMESTAMP", + "TIMESTAMP_DESC" + ], + "title": "BashEventSortOrder" + }, + "BashOutput": { + "properties": { + "kind": { + "type": "string", + "const": "BashOutput", + "title": "Kind", + "default": "BashOutput" + }, + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "title": "Timestamp" + }, + "command_id": { + "type": "string", + "format": "uuid", + "title": "Command Id" + }, + "order": { + "type": "integer", + "title": "Order", + "description": "The order for this output, sequentially starting with 0", + "default": 0 + }, + "exit_code": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Exit Code", + "description": "Exit code None implies the command is still running." + }, + "stdout": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Stdout", + "description": "The standard output from the command" + }, + "stderr": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Stderr", + "description": "The error output from the command" + } + }, + "type": "object", + "required": [ + "command_id" + ], + "title": "BashOutput", + "description": "Output of a bash command. A single command may have multiple pieces of output\ndepending on how large the output is." + }, + "Body_upload_file_api_file_upload__path__post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_file_api_file_upload__path__post" + }, + "BrowserClickAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserClickAction", + "title": "Kind", + "default": "BrowserClickAction" + }, + "index": { + "type": "integer", + "minimum": 0.0, + "title": "Index", + "description": "The index of the element to click (from browser_get_state)" + }, + "new_tab": { + "type": "boolean", + "title": "New Tab", + "description": "Whether to open any resulting navigation in a new tab. Default: False", + "default": false + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "index" + ], + "title": "BrowserClickAction", + "description": "Schema for clicking elements." + }, + "BrowserCloseTabAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserCloseTabAction", + "title": "Kind", + "default": "BrowserCloseTabAction" + }, + "tab_id": { + "type": "string", + "title": "Tab Id", + "description": "4 Character Tab ID of the tab to close (from browser_list_tabs)" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "tab_id" + ], + "title": "BrowserCloseTabAction", + "description": "Schema for closing browser tabs." + }, + "BrowserGetContentAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserGetContentAction", + "title": "Kind", + "default": "BrowserGetContentAction" + }, + "extract_links": { + "type": "boolean", + "title": "Extract Links", + "description": "Whether to include links in the content (default: False)", + "default": false + }, + "start_from_char": { + "type": "integer", + "minimum": 0.0, + "title": "Start From Char", + "description": "Character index to start from in the page content (default: 0)", + "default": 0 + } + }, + "additionalProperties": false, + "type": "object", + "title": "BrowserGetContentAction", + "description": "Schema for getting page content in markdown." + }, + "BrowserGetStateAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserGetStateAction", + "title": "Kind", + "default": "BrowserGetStateAction" + }, + "include_screenshot": { + "type": "boolean", + "title": "Include Screenshot", + "description": "Whether to include a screenshot of the current page. Default: False", + "default": false + } + }, + "additionalProperties": false, + "type": "object", + "title": "BrowserGetStateAction", + "description": "Schema for getting browser state." + }, + "BrowserGoBackAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserGoBackAction", + "title": "Kind", + "default": "BrowserGoBackAction" + } + }, + "additionalProperties": false, + "type": "object", + "title": "BrowserGoBackAction", + "description": "Schema for going back in browser history." + }, + "BrowserListTabsAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserListTabsAction", + "title": "Kind", + "default": "BrowserListTabsAction" + } + }, + "additionalProperties": false, + "type": "object", + "title": "BrowserListTabsAction", + "description": "Schema for listing browser tabs." + }, + "BrowserNavigateAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserNavigateAction", + "title": "Kind", + "default": "BrowserNavigateAction" + }, + "url": { + "type": "string", + "title": "Url", + "description": "The URL to navigate to" + }, + "new_tab": { + "type": "boolean", + "title": "New Tab", + "description": "Whether to open in a new tab. Default: False", + "default": false + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "url" + ], + "title": "BrowserNavigateAction", + "description": "Schema for browser navigation." + }, + "BrowserObservation": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserObservation", + "title": "Kind", + "default": "BrowserObservation" + }, + "output": { + "type": "string", + "title": "Output", + "description": "The output message from the browser operation" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error", + "description": "Error message if any" + }, + "screenshot_data": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Screenshot Data", + "description": "Base64 screenshot data if available" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "output" + ], + "title": "BrowserObservation", + "description": "Base observation for browser operations." + }, + "BrowserScrollAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserScrollAction", + "title": "Kind", + "default": "BrowserScrollAction" + }, + "direction": { + "type": "string", + "enum": [ + "up", + "down" + ], + "title": "Direction", + "description": "Direction to scroll. Options: 'up', 'down'. Default: 'down'", + "default": "down" + } + }, + "additionalProperties": false, + "type": "object", + "title": "BrowserScrollAction", + "description": "Schema for scrolling the page." + }, + "BrowserSwitchTabAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserSwitchTabAction", + "title": "Kind", + "default": "BrowserSwitchTabAction" + }, + "tab_id": { + "type": "string", + "title": "Tab Id", + "description": "4 Character Tab ID of the tab to switch to (from browser_list_tabs)" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "tab_id" + ], + "title": "BrowserSwitchTabAction", + "description": "Schema for switching browser tabs." + }, + "BrowserTypeAction": { + "properties": { + "kind": { + "type": "string", + "const": "BrowserTypeAction", + "title": "Kind", + "default": "BrowserTypeAction" + }, + "index": { + "type": "integer", + "minimum": 0.0, + "title": "Index", + "description": "The index of the input element (from browser_get_state)" + }, + "text": { + "type": "string", + "title": "Text", + "description": "The text to type" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "index", + "text" + ], + "title": "BrowserTypeAction", + "description": "Schema for typing text into elements." + }, + "ChatCompletionCachedContent": { + "properties": { + "type": { + "type": "string", + "const": "ephemeral", + "title": "Type" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "type" + ], + "title": "ChatCompletionCachedContent" + }, + "ChatCompletionToolParam": { + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "const": "function" + }, + { + "type": "string" + } + ], + "title": "Type" + }, + "function": { + "$ref": "#/components/schemas/ChatCompletionToolParamFunctionChunk" + }, + "cache_control": { + "$ref": "#/components/schemas/ChatCompletionCachedContent" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "type", + "function" + ], + "title": "ChatCompletionToolParam" + }, + "ChatCompletionToolParamFunctionChunk": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + }, + "parameters": { + "additionalProperties": true, + "type": "object", + "title": "Parameters" + }, + "strict": { + "type": "boolean", + "title": "Strict" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "name" + ], + "title": "ChatCompletionToolParamFunctionChunk" + }, + "CmdOutputMetadata": { + "properties": { + "exit_code": { + "type": "integer", + "title": "Exit Code", + "description": "The exit code of the last executed command.", + "default": -1 + }, + "pid": { + "type": "integer", + "title": "Pid", + "description": "The process ID of the last executed command.", + "default": -1 + }, + "username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Username", + "description": "The username of the current user." + }, + "hostname": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Hostname", + "description": "The hostname of the machine." + }, + "working_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Working Dir", + "description": "The current working directory." + }, + "py_interpreter_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Py Interpreter Path", + "description": "The path to the current Python interpreter, if any." + }, + "prefix": { + "type": "string", + "title": "Prefix", + "description": "Prefix to add to command output", + "default": "" + }, + "suffix": { + "type": "string", + "title": "Suffix", + "description": "Suffix to add to command output", + "default": "" + } + }, + "type": "object", + "title": "CmdOutputMetadata", + "description": "Additional metadata captured from PS1" + }, + "Condensation": { + "properties": { + "kind": { + "type": "string", + "const": "Condensation", + "title": "Kind", + "default": "Condensation" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "environment" + }, + "forgotten_event_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Forgotten Event Ids", + "description": "The IDs of the events that are being forgotten (removed from the `View` given to the LLM)." + }, + "summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Summary", + "description": "An optional summary of the events being forgotten." + }, + "summary_offset": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Summary Offset", + "description": "An optional offset to the start of the resulting view indicating where the summary should be inserted." + } + }, + "additionalProperties": false, + "type": "object", + "title": "Condensation", + "description": "This action indicates a condensation of the conversation history is happening." + }, + "CondensationRequest": { + "properties": { + "kind": { + "type": "string", + "const": "CondensationRequest", + "title": "Kind", + "default": "CondensationRequest" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "environment" + } + }, + "additionalProperties": false, + "type": "object", + "title": "CondensationRequest", + "description": "This action is used to request a condensation of the conversation history.\n\nAttributes:\n action (str): The action type, namely ActionType.CONDENSATION_REQUEST." + }, + "CondensationSummaryEvent": { + "properties": { + "kind": { + "type": "string", + "const": "CondensationSummaryEvent", + "title": "Kind", + "default": "CondensationSummaryEvent" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "environment" + }, + "summary": { + "type": "string", + "title": "Summary" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "summary" + ], + "title": "CondensationSummaryEvent", + "description": "This event represents a summary generated by a condenser." + }, + "ConfirmRisky": { + "properties": { + "kind": { + "type": "string", + "const": "ConfirmRisky", + "title": "Kind", + "default": "ConfirmRisky" + }, + "threshold": { + "$ref": "#/components/schemas/SecurityRisk", + "default": "HIGH" + }, + "confirm_unknown": { + "type": "boolean", + "title": "Confirm Unknown", + "default": true + } + }, + "type": "object", + "title": "ConfirmRisky" + }, + "ConfirmationResponseRequest": { + "properties": { + "accept": { + "type": "boolean", + "title": "Accept" + }, + "reason": { + "type": "string", + "title": "Reason", + "default": "User rejected the action." + } + }, + "type": "object", + "required": [ + "accept" + ], + "title": "ConfirmationResponseRequest", + "description": "Payload to accept or reject a pending action." + }, + "ConversationInfo": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id", + "description": "Unique conversation ID" + }, + "agent": { + "$ref": "#/components/schemas/Agent-Output", + "description": "The agent running in the conversation. This is persisted to allow resuming conversations and check agent configuration to handle e.g., tool changes, LLM changes, etc." + }, + "workspace": { + "$ref": "#/components/schemas/BaseWorkspace" + }, + "persistence_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Persistence Dir", + "description": "Directory for persisting conversation state and events. If None, conversation will not be persisted.", + "default": "workspace/conversations" + }, + "max_iterations": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Max Iterations", + "description": "Maximum number of iterations the agent can perform in a single run.", + "default": 500 + }, + "stuck_detection": { + "type": "boolean", + "title": "Stuck Detection", + "description": "Whether to enable stuck detection for the agent.", + "default": true + }, + "agent_status": { + "$ref": "#/components/schemas/AgentExecutionStatus", + "default": "idle" + }, + "confirmation_policy": { + "$ref": "#/components/schemas/ConfirmationPolicyBase" + }, + "activated_knowledge_skills": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Activated Knowledge Skills", + "description": "List of activated knowledge skills name" + }, + "stats": { + "$ref": "#/components/schemas/ConversationStats-Output", + "description": "Conversation statistics for tracking LLM metrics" + }, + "secrets_manager": { + "$ref": "#/components/schemas/SecretsManager-Output", + "description": "Manager for handling secrets and sensitive data" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title", + "description": "User-defined title for the conversation" + }, + "metrics": { + "anyOf": [ + { + "$ref": "#/components/schemas/MetricsSnapshot" + }, + { + "type": "null" + } + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "agent", + "workspace" + ], + "title": "ConversationInfo", + "description": "Information about a conversation running locally without a Runtime sandbox." + }, + "ConversationPage": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/ConversationInfo" + }, + "type": "array", + "title": "Items" + }, + "next_page_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Next Page Id" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "ConversationPage" + }, + "ConversationSortOrder": { + "type": "string", + "enum": [ + "CREATED_AT", + "UPDATED_AT", + "CREATED_AT_DESC", + "UPDATED_AT_DESC" + ], + "title": "ConversationSortOrder", + "description": "Enum for conversation sorting options." + }, + "ConversationStateUpdateEvent": { + "properties": { + "kind": { + "type": "string", + "const": "ConversationStateUpdateEvent", + "title": "Kind", + "default": "ConversationStateUpdateEvent" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "environment" + }, + "key": { + "type": "string", + "title": "Key", + "description": "Unique key for this state update event" + }, + "value": { + "title": "Value", + "description": "Serialized conversation state updates" + } + }, + "additionalProperties": false, + "type": "object", + "title": "ConversationStateUpdateEvent", + "description": "Event that contains conversation state updates.\n\nThis event is sent via websocket whenever the conversation state changes,\nallowing remote clients to stay in sync without making REST API calls.\n\nAll fields are serialized versions of the corresponding ConversationState fields\nto ensure compatibility with websocket transmission." + }, + "ConversationStats-Output": { + "properties": { + "usage_to_metrics": { + "additionalProperties": { + "$ref": "#/components/schemas/Metrics" + }, + "type": "object", + "title": "Usage To Metrics", + "description": "Active usage metrics tracked by the registry." + } + }, + "type": "object", + "title": "ConversationStats", + "description": "Track per-LLM usage metrics observed during conversations." + }, + "Cost": { + "properties": { + "model": { + "type": "string", + "title": "Model" + }, + "cost": { + "type": "number", + "minimum": 0.0, + "title": "Cost", + "description": "Cost must be non-negative" + }, + "timestamp": { + "type": "number", + "title": "Timestamp" + } + }, + "type": "object", + "required": [ + "model", + "cost" + ], + "title": "Cost" + }, + "DesktopUrlResponse": { + "properties": { + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + } + }, + "type": "object", + "required": [ + "url" + ], + "title": "DesktopUrlResponse", + "description": "Response model for Desktop URL." + }, + "EventPage": { + "properties": { + "items": { + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/Condensation" + }, + { + "$ref": "#/components/schemas/CondensationRequest" + }, + { + "$ref": "#/components/schemas/CondensationSummaryEvent" + }, + { + "$ref": "#/components/schemas/ConversationStateUpdateEvent" + }, + { + "$ref": "#/components/schemas/ActionEvent" + }, + { + "$ref": "#/components/schemas/MessageEvent" + }, + { + "$ref": "#/components/schemas/AgentErrorEvent" + }, + { + "$ref": "#/components/schemas/ObservationEvent" + }, + { + "$ref": "#/components/schemas/UserRejectObservation" + }, + { + "$ref": "#/components/schemas/SystemPromptEvent" + }, + { + "$ref": "#/components/schemas/PauseEvent" + } + ], + "title": "Event", + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__event__condenser__Condensation-Output__1": "#/components/schemas/Condensation", + "openhands__sdk__event__condenser__CondensationRequest-Output__1": "#/components/schemas/CondensationRequest", + "openhands__sdk__event__condenser__CondensationSummaryEvent-Output__1": "#/components/schemas/CondensationSummaryEvent", + "openhands__sdk__event__conversation_state__ConversationStateUpdateEvent-Output__1": "#/components/schemas/ConversationStateUpdateEvent", + "openhands__sdk__event__llm_convertible__action__ActionEvent-Output__1": "#/components/schemas/ActionEvent", + "openhands__sdk__event__llm_convertible__message__MessageEvent-Output__1": "#/components/schemas/MessageEvent", + "openhands__sdk__event__llm_convertible__observation__AgentErrorEvent-Output__1": "#/components/schemas/AgentErrorEvent", + "openhands__sdk__event__llm_convertible__observation__ObservationEvent-Output__1": "#/components/schemas/ObservationEvent", + "openhands__sdk__event__llm_convertible__observation__UserRejectObservation-Output__1": "#/components/schemas/UserRejectObservation", + "openhands__sdk__event__llm_convertible__system__SystemPromptEvent-Output__1": "#/components/schemas/SystemPromptEvent", + "openhands__sdk__event__user_action__PauseEvent-Output__1": "#/components/schemas/PauseEvent" + } + } + }, + "type": "array", + "title": "Items" + }, + "next_page_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Next Page Id" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "EventPage" + }, + "EventSortOrder": { + "type": "string", + "enum": [ + "TIMESTAMP", + "TIMESTAMP_DESC" + ], + "title": "EventSortOrder", + "description": "Enum for event sorting options." + }, + "ExecuteBashAction": { + "properties": { + "kind": { + "type": "string", + "const": "ExecuteBashAction", + "title": "Kind", + "default": "ExecuteBashAction" + }, + "command": { + "type": "string", + "title": "Command", + "description": "The bash command to execute. Can be empty string to view additional logs when previous exit code is `-1`. Can be `C-c` (Ctrl+C) to interrupt the currently running process. Note: You can only execute one bash command at a time. If you need to run multiple commands sequentially, you can use `&&` or `;` to chain them together." + }, + "is_input": { + "type": "boolean", + "title": "Is Input", + "description": "If True, the command is an input to the running process. If False, the command is a bash command to be executed in the terminal. Default is False.", + "default": false + }, + "timeout": { + "anyOf": [ + { + "type": "number", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Timeout", + "description": "Optional. Sets a maximum time limit (in seconds) for running the command. If the command takes longer than this limit, you’ll be asked whether to continue or stop it. If you don’t set a value, the command will instead pause and ask for confirmation when it produces no new output for 30 seconds. Use a higher value if the command is expected to take a long time (like installation or testing), or if it has a known fixed duration (like sleep)." + }, + "reset": { + "type": "boolean", + "title": "Reset", + "description": "If True, reset the terminal by creating a new session. Use this only when the terminal becomes unresponsive. Note that all previously set environment variables and session state will be lost after reset. Cannot be used with is_input=True.", + "default": false + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "command" + ], + "title": "ExecuteBashAction", + "description": "Schema for bash command execution." + }, + "ExecuteBashObservation": { + "properties": { + "kind": { + "type": "string", + "const": "ExecuteBashObservation", + "title": "Kind", + "default": "ExecuteBashObservation" + }, + "output": { + "type": "string", + "title": "Output", + "description": "The raw output from the tool." + }, + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Command", + "description": "The bash command that was executed. Can be empty string if the observation is from a previous command that hit soft timeout and is not yet finished." + }, + "exit_code": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Exit Code", + "description": "The exit code of the command. -1 indicates the process hit the soft timeout and is not yet finished." + }, + "error": { + "type": "boolean", + "title": "Error", + "description": "Whether there was an error during command execution.", + "default": false + }, + "timeout": { + "type": "boolean", + "title": "Timeout", + "description": "Whether the command execution timed out.", + "default": false + }, + "metadata": { + "$ref": "#/components/schemas/CmdOutputMetadata", + "description": "Additional metadata captured from PS1 after command execution." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "output" + ], + "title": "ExecuteBashObservation", + "description": "A ToolResult that can be rendered as a CLI output." + }, + "ExecuteBashRequest": { + "properties": { + "command": { + "type": "string", + "title": "Command", + "description": "The bash command to execute" + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cwd", + "description": "The current working directory" + }, + "timeout": { + "type": "integer", + "title": "Timeout", + "description": "The max number of seconds a command may be permitted to run.", + "default": 300 + } + }, + "type": "object", + "required": [ + "command" + ], + "title": "ExecuteBashRequest" + }, + "FileEditorAction": { + "properties": { + "kind": { + "type": "string", + "const": "FileEditorAction", + "title": "Kind", + "default": "FileEditorAction" + }, + "command": { + "type": "string", + "enum": [ + "view", + "create", + "str_replace", + "insert", + "undo_edit" + ], + "title": "Command", + "description": "The commands to run. Allowed options are: `view`, `create`, `str_replace`, `insert`, `undo_edit`." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Absolute path to file or directory." + }, + "file_text": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "File Text", + "description": "Required parameter of `create` command, with the content of the file to be created." + }, + "old_str": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Old Str", + "description": "Required parameter of `str_replace` command containing the string in `path` to replace." + }, + "new_str": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "New Str", + "description": "Optional parameter of `str_replace` command containing the new string (if not given, no string will be added). Required parameter of `insert` command containing the string to insert." + }, + "insert_line": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Insert Line", + "description": "Required parameter of `insert` command. The `new_str` will be inserted AFTER the line `insert_line` of `path`." + }, + "view_range": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "View Range", + "description": "Optional parameter of `view` command when `path` points to a file. If none is given, the full file is shown. If provided, the file will be shown in the indicated line number range, e.g. [11, 12] will show lines 11 and 12. Indexing at 1 to start. Setting `[start_line, -1]` shows all lines from `start_line` to the end of the file." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "command", + "path" + ], + "title": "FileEditorAction", + "description": "Schema for file editor operations." + }, + "FileEditorObservation": { + "properties": { + "kind": { + "type": "string", + "const": "FileEditorObservation", + "title": "Kind", + "default": "FileEditorObservation" + }, + "command": { + "type": "string", + "enum": [ + "view", + "create", + "str_replace", + "insert", + "undo_edit" + ], + "title": "Command", + "description": "The commands to run. Allowed options are: `view`, `create`, `str_replace`, `insert`, `undo_edit`." + }, + "output": { + "type": "string", + "title": "Output", + "description": "The output message from the tool for the LLM to see.", + "default": "" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Path", + "description": "The file path that was edited." + }, + "prev_exist": { + "type": "boolean", + "title": "Prev Exist", + "description": "Indicates if the file previously existed. If not, it was created.", + "default": true + }, + "old_content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Old Content", + "description": "The content of the file before the edit." + }, + "new_content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "New Content", + "description": "The content of the file after the edit." + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error", + "description": "Error message if any." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "command" + ], + "title": "FileEditorObservation", + "description": "A ToolResult that can be rendered as a CLI output." + }, + "FinishAction": { + "properties": { + "kind": { + "type": "string", + "const": "FinishAction", + "title": "Kind", + "default": "FinishAction" + }, + "message": { + "type": "string", + "title": "Message", + "description": "Final message to send to the user." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "message" + ], + "title": "FinishAction" + }, + "FinishObservation": { + "properties": { + "kind": { + "type": "string", + "const": "FinishObservation", + "title": "Kind", + "default": "FinishObservation" + }, + "message": { + "type": "string", + "title": "Message", + "description": "Final message sent to the user." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "message" + ], + "title": "FinishObservation" + }, + "GenerateTitleRequest": { + "properties": { + "max_length": { + "type": "integer", + "maximum": 200.0, + "minimum": 1.0, + "title": "Max Length", + "description": "Maximum length of the generated title", + "default": 50 + }, + "llm": { + "anyOf": [ + { + "$ref": "#/components/schemas/LLM" + }, + { + "type": "null" + } + ], + "description": "Optional LLM to use for title generation" + } + }, + "type": "object", + "title": "GenerateTitleRequest", + "description": "Payload to generate a title for a conversation." + }, + "GenerateTitleResponse": { + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "The generated title for the conversation" + } + }, + "type": "object", + "required": [ + "title" + ], + "title": "GenerateTitleResponse", + "description": "Response containing the generated conversation title." + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "ImageContent": { + "properties": { + "cache_prompt": { + "type": "boolean", + "title": "Cache Prompt", + "default": false + }, + "type": { + "type": "string", + "const": "image", + "title": "Type", + "default": "image" + }, + "image_urls": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Image Urls" + } + }, + "type": "object", + "required": [ + "image_urls" + ], + "title": "ImageContent" + }, + "InputMetadata": { + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name of the input parameter" + }, + "description": { + "type": "string", + "title": "Description", + "description": "Description of the input parameter" + } + }, + "type": "object", + "required": [ + "name", + "description" + ], + "title": "InputMetadata", + "description": "Metadata for task skill inputs." + }, + "KeywordTrigger": { + "properties": { + "type": { + "type": "string", + "const": "keyword", + "title": "Type", + "default": "keyword" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Keywords" + } + }, + "type": "object", + "required": [ + "keywords" + ], + "title": "KeywordTrigger", + "description": "Trigger for keyword-based skills.\n\nThese skills are activated when specific keywords appear in the user's query." + }, + "LLM": { + "properties": { + "model": { + "type": "string", + "title": "Model", + "description": "Model name.", + "default": "claude-sonnet-4-20250514" + }, + "api_key": { + "anyOf": [ + { + "type": "string", + "format": "password", + "writeOnly": true + }, + { + "type": "null" + } + ], + "title": "Api Key", + "description": "API key." + }, + "base_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Url", + "description": "Custom base URL." + }, + "api_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Api Version", + "description": "API version (e.g., Azure)." + }, + "aws_access_key_id": { + "anyOf": [ + { + "type": "string", + "format": "password", + "writeOnly": true + }, + { + "type": "null" + } + ], + "title": "Aws Access Key Id" + }, + "aws_secret_access_key": { + "anyOf": [ + { + "type": "string", + "format": "password", + "writeOnly": true + }, + { + "type": "null" + } + ], + "title": "Aws Secret Access Key" + }, + "aws_region_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Aws Region Name" + }, + "openrouter_site_url": { + "type": "string", + "title": "Openrouter Site Url", + "default": "https://docs.all-hands.dev/" + }, + "openrouter_app_name": { + "type": "string", + "title": "Openrouter App Name", + "default": "OpenHands" + }, + "num_retries": { + "type": "integer", + "minimum": 0.0, + "title": "Num Retries", + "default": 5 + }, + "retry_multiplier": { + "type": "number", + "minimum": 0.0, + "title": "Retry Multiplier", + "default": 8.0 + }, + "retry_min_wait": { + "type": "integer", + "minimum": 0.0, + "title": "Retry Min Wait", + "default": 8 + }, + "retry_max_wait": { + "type": "integer", + "minimum": 0.0, + "title": "Retry Max Wait", + "default": 64 + }, + "timeout": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Timeout", + "description": "HTTP timeout (s)." + }, + "max_message_chars": { + "type": "integer", + "minimum": 1.0, + "title": "Max Message Chars", + "description": "Approx max chars in each event/content sent to the LLM.", + "default": 30000 + }, + "temperature": { + "anyOf": [ + { + "type": "number", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Temperature", + "default": 0.0 + }, + "top_p": { + "anyOf": [ + { + "type": "number", + "maximum": 1.0, + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Top P", + "default": 1.0 + }, + "top_k": { + "anyOf": [ + { + "type": "number", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Top K" + }, + "custom_llm_provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Custom Llm Provider" + }, + "max_input_tokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Max Input Tokens", + "description": "The maximum number of input tokens. Note that this is currently unused, and the value at runtime is actually the total tokens in OpenAI (e.g. 128,000 tokens for GPT-4)." + }, + "max_output_tokens": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Max Output Tokens", + "description": "The maximum number of output tokens. This is sent to the LLM." + }, + "input_cost_per_token": { + "anyOf": [ + { + "type": "number", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Input Cost Per Token", + "description": "The cost per input token. This will available in logs for user." + }, + "output_cost_per_token": { + "anyOf": [ + { + "type": "number", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Output Cost Per Token", + "description": "The cost per output token. This will available in logs for user." + }, + "ollama_base_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ollama Base Url" + }, + "drop_params": { + "type": "boolean", + "title": "Drop Params", + "default": true + }, + "modify_params": { + "type": "boolean", + "title": "Modify Params", + "description": "Modify params allows litellm to do transformations like adding a default message, when a message is empty.", + "default": true + }, + "disable_vision": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Disable Vision", + "description": "If model is vision capable, this option allows to disable image processing (useful for cost reduction)." + }, + "disable_stop_word": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Disable Stop Word", + "description": "Disable using of stop word.", + "default": false + }, + "caching_prompt": { + "type": "boolean", + "title": "Caching Prompt", + "description": "Enable caching of prompts.", + "default": true + }, + "log_completions": { + "type": "boolean", + "title": "Log Completions", + "description": "Enable logging of completions.", + "default": false + }, + "log_completions_folder": { + "type": "string", + "title": "Log Completions Folder", + "description": "The folder to log LLM completions to. Required if log_completions is True.", + "default": "logs/completions" + }, + "custom_tokenizer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Custom Tokenizer", + "description": "A custom tokenizer to use for token counting." + }, + "native_tool_calling": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Native Tool Calling", + "description": "Whether to use native tool calling if supported by the model. Can be True, False, or not set." + }, + "reasoning_effort": { + "anyOf": [ + { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "none" + ] + }, + { + "type": "null" + } + ], + "title": "Reasoning Effort", + "description": "The effort to put into reasoning. This is a string that can be one of 'low', 'medium', 'high', or 'none'. Can apply to all reasoning models." + }, + "enable_encrypted_reasoning": { + "type": "boolean", + "title": "Enable Encrypted Reasoning", + "description": "If True, ask for ['reasoning.encrypted_content'] in Responses API include.", + "default": false + }, + "extended_thinking_budget": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Extended Thinking Budget", + "description": "The budget tokens for extended thinking, supported by Anthropic models.", + "default": 200000 + }, + "seed": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Seed", + "description": "The seed to use for random number generation." + }, + "safety_settings": { + "anyOf": [ + { + "items": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Safety Settings", + "description": "Safety settings for models that support them (like Mistral AI and Gemini)" + }, + "usage_id": { + "type": "string", + "title": "Usage Id", + "description": "Unique usage identifier for the LLM. Used for registry lookups, telemetry, and spend tracking.", + "default": "default" + }, + "metadata": { + "additionalProperties": true, + "type": "object", + "title": "Metadata", + "description": "Additional metadata for the LLM instance. Example structure: {'trace_version': '1.0.0', 'tags': ['model:gpt-4', 'agent:my-agent'], 'session_id': 'session-123', 'trace_user_id': 'user-456'}" + }, + "OVERRIDE_ON_SERIALIZE": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Override On Serialize", + "default": [ + "api_key", + "aws_access_key_id", + "aws_secret_access_key" + ] + } + }, + "additionalProperties": false, + "type": "object", + "title": "LLM", + "description": "Refactored LLM: simple `completion()`, centralized Telemetry, tiny helpers." + }, + "LLMSummarizingCondenser": { + "properties": { + "kind": { + "type": "string", + "const": "LLMSummarizingCondenser", + "title": "Kind", + "default": "LLMSummarizingCondenser" + }, + "llm": { + "$ref": "#/components/schemas/LLM" + }, + "max_size": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Max Size", + "default": 120 + }, + "keep_first": { + "type": "integer", + "minimum": 0.0, + "title": "Keep First", + "default": 4 + } + }, + "type": "object", + "required": [ + "llm" + ], + "title": "LLMSummarizingCondenser" + }, + "LocalWorkspace": { + "properties": { + "kind": { + "type": "string", + "const": "LocalWorkspace", + "title": "Kind", + "default": "LocalWorkspace" + }, + "working_dir": { + "type": "string", + "title": "Working Dir", + "description": "The working directory for agent operations and tool execution." + } + }, + "type": "object", + "required": [ + "working_dir" + ], + "title": "LocalWorkspace", + "description": "Mixin providing local workspace operations." + }, + "LookupSecret": { + "properties": { + "kind": { + "type": "string", + "const": "LookupSecret", + "title": "Kind", + "default": "LookupSecret" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Optional description for this secret" + }, + "url": { + "type": "string", + "title": "Url" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Headers" + } + }, + "type": "object", + "required": [ + "url" + ], + "title": "LookupSecret", + "description": "A secret looked up from some external url" + }, + "MCPToolAction": { + "properties": { + "kind": { + "type": "string", + "const": "MCPToolAction", + "title": "Kind", + "default": "MCPToolAction" + }, + "data": { + "additionalProperties": true, + "type": "object", + "title": "Data", + "description": "Dynamic data fields from the tool call" + } + }, + "additionalProperties": false, + "type": "object", + "title": "MCPToolAction", + "description": "Schema for MCP input action.\n\nIt is just a thin wrapper around raw JSON and does\nnot do any validation.\n\nValidation will be performed by MCPTool.__call__\nby constructing dynamically created Pydantic model\nfrom the MCP tool input schema." + }, + "MCPToolObservation": { + "properties": { + "kind": { + "type": "string", + "const": "MCPToolObservation", + "title": "Kind", + "default": "MCPToolObservation" + }, + "content": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/TextContent" + }, + { + "$ref": "#/components/schemas/ImageContent" + } + ] + }, + "type": "array", + "title": "Content", + "description": "Content returned from the MCP tool converted to LLM Ready TextContent or ImageContent" + }, + "is_error": { + "type": "boolean", + "title": "Is Error", + "description": "Whether the call resulted in an error", + "default": false + }, + "tool_name": { + "type": "string", + "title": "Tool Name", + "description": "Name of the tool that was called" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "tool_name" + ], + "title": "MCPToolObservation", + "description": "Observation from MCP tool execution." + }, + "Message": { + "properties": { + "role": { + "type": "string", + "enum": [ + "user", + "system", + "assistant", + "tool" + ], + "title": "Role" + }, + "content": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/TextContent" + }, + { + "$ref": "#/components/schemas/ImageContent" + } + ] + }, + "type": "array", + "title": "Content" + }, + "cache_enabled": { + "type": "boolean", + "title": "Cache Enabled", + "default": false + }, + "vision_enabled": { + "type": "boolean", + "title": "Vision Enabled", + "default": false + }, + "function_calling_enabled": { + "type": "boolean", + "title": "Function Calling Enabled", + "default": false + }, + "tool_calls": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/MessageToolCall" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Tool Calls" + }, + "tool_call_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Tool Call Id" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "force_string_serializer": { + "type": "boolean", + "title": "Force String Serializer", + "default": false + }, + "reasoning_content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Reasoning Content", + "description": "Intermediate reasoning/thinking content from reasoning models" + }, + "thinking_blocks": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/ThinkingBlock" + }, + { + "$ref": "#/components/schemas/RedactedThinkingBlock" + } + ] + }, + "type": "array", + "title": "Thinking Blocks", + "description": "Raw Anthropic thinking blocks for extended thinking feature" + }, + "responses_reasoning_item": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReasoningItemModel" + }, + { + "type": "null" + } + ], + "description": "OpenAI Responses reasoning item from model output" + } + }, + "type": "object", + "required": [ + "role" + ], + "title": "Message" + }, + "MessageEvent": { + "properties": { + "kind": { + "type": "string", + "const": "MessageEvent", + "title": "Kind", + "default": "MessageEvent" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source" + }, + "llm_message": { + "$ref": "#/components/schemas/Message", + "description": "The exact LLM message for this message event" + }, + "activated_skills": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Activated Skills", + "description": "List of activated skill name" + }, + "extended_content": { + "items": { + "$ref": "#/components/schemas/TextContent" + }, + "type": "array", + "title": "Extended Content", + "description": "List of content added by agent context" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "source", + "llm_message" + ], + "title": "MessageEvent", + "description": "Message from either agent or user.\n\nThis is originally the \"MessageAction\", but it suppose not to be tool call." + }, + "MessageToolCall": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Canonical tool call id" + }, + "name": { + "type": "string", + "title": "Name", + "description": "Tool/function name" + }, + "arguments": { + "type": "string", + "title": "Arguments", + "description": "JSON string of arguments" + }, + "origin": { + "type": "string", + "enum": [ + "completion", + "responses" + ], + "title": "Origin", + "description": "Originating API family" + } + }, + "type": "object", + "required": [ + "id", + "name", + "arguments", + "origin" + ], + "title": "MessageToolCall", + "description": "Transport-agnostic tool call representation.\n\nOne canonical id is used for linking across actions/observations and\nfor Responses function_call_output call_id." + }, + "Metrics": { + "properties": { + "model_name": { + "type": "string", + "title": "Model Name", + "description": "Name of the model", + "default": "default" + }, + "accumulated_cost": { + "type": "number", + "minimum": 0.0, + "title": "Accumulated Cost", + "description": "Total accumulated cost, must be non-negative", + "default": 0.0 + }, + "max_budget_per_task": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Max Budget Per Task", + "description": "Maximum budget per task" + }, + "accumulated_token_usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/TokenUsage" + }, + { + "type": "null" + } + ], + "description": "Accumulated token usage across all calls" + }, + "costs": { + "items": { + "$ref": "#/components/schemas/Cost" + }, + "type": "array", + "title": "Costs", + "description": "List of individual costs" + }, + "response_latencies": { + "items": { + "$ref": "#/components/schemas/ResponseLatency" + }, + "type": "array", + "title": "Response Latencies", + "description": "List of response latencies" + }, + "token_usages": { + "items": { + "$ref": "#/components/schemas/TokenUsage" + }, + "type": "array", + "title": "Token Usages", + "description": "List of token usage records" + } + }, + "type": "object", + "title": "Metrics", + "description": "Metrics class can record various metrics during running and evaluation.\nWe track:\n - accumulated_cost and costs\n - max_budget_per_task (budget limit)\n - A list of ResponseLatency\n - A list of TokenUsage (one per call)." + }, + "MetricsSnapshot": { + "properties": { + "model_name": { + "type": "string", + "title": "Model Name", + "description": "Name of the model", + "default": "default" + }, + "accumulated_cost": { + "type": "number", + "minimum": 0.0, + "title": "Accumulated Cost", + "description": "Total accumulated cost, must be non-negative", + "default": 0.0 + }, + "max_budget_per_task": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Max Budget Per Task", + "description": "Maximum budget per task" + }, + "accumulated_token_usage": { + "anyOf": [ + { + "$ref": "#/components/schemas/TokenUsage" + }, + { + "type": "null" + } + ], + "description": "Accumulated token usage across all calls" + } + }, + "type": "object", + "title": "MetricsSnapshot", + "description": "A snapshot of metrics at a point in time.\n\nDoes not include lists of individual costs, latencies, or token usages." + }, + "NeverConfirm": { + "properties": { + "kind": { + "type": "string", + "const": "NeverConfirm", + "title": "Kind", + "default": "NeverConfirm" + } + }, + "type": "object", + "title": "NeverConfirm" + }, + "NoOpCondenser": { + "properties": { + "kind": { + "type": "string", + "const": "NoOpCondenser", + "title": "Kind", + "default": "NoOpCondenser" + } + }, + "type": "object", + "title": "NoOpCondenser", + "description": "Simple condenser that returns a view un-manipulated.\n\nPrimarily intended for testing purposes." + }, + "ObservationEvent": { + "properties": { + "kind": { + "type": "string", + "const": "ObservationEvent", + "title": "Kind", + "default": "ObservationEvent" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "environment" + }, + "tool_name": { + "type": "string", + "title": "Tool Name", + "description": "The tool name that this observation is responding to" + }, + "tool_call_id": { + "type": "string", + "title": "Tool Call Id", + "description": "The tool call id that this observation is responding to" + }, + "observation": { + "$ref": "#/components/schemas/Observation" + }, + "action_id": { + "type": "string", + "title": "Action Id", + "description": "The action id that this observation is responding to" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "tool_name", + "tool_call_id", + "observation", + "action_id" + ], + "title": "ObservationEvent" + }, + "PauseEvent": { + "properties": { + "kind": { + "type": "string", + "const": "PauseEvent", + "title": "Kind", + "default": "PauseEvent" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "user" + } + }, + "additionalProperties": false, + "type": "object", + "title": "PauseEvent", + "description": "Event indicating that the agent execution was paused by user request." + }, + "PipelineCondenser-Output": { + "properties": { + "kind": { + "type": "string", + "const": "PipelineCondenser", + "title": "Kind", + "default": "PipelineCondenser" + }, + "condensers": { + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/LLMSummarizingCondenser" + }, + { + "$ref": "#/components/schemas/NoOpCondenser" + }, + { + "$ref": "#/components/schemas/PipelineCondenser-Output" + } + ], + "title": "CondenserBase", + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__context__condenser__llm_summarizing_condenser__LLMSummarizingCondenser-Output__1": "#/components/schemas/LLMSummarizingCondenser", + "openhands__sdk__context__condenser__no_op_condenser__NoOpCondenser-Output__1": "#/components/schemas/NoOpCondenser", + "openhands__sdk__context__condenser__pipeline_condenser__PipelineCondenser-Output__1": "#/components/schemas/PipelineCondenser-Output" + } + } + }, + "type": "array", + "title": "Condensers" + } + }, + "type": "object", + "required": [ + "condensers" + ], + "title": "PipelineCondenser", + "description": "A condenser that applies a sequence of condensers in order.\n\nAll condensers are defined primarily by their `condense` method, which takes a\n`View` and returns either a new `View` or a `Condensation` event. That means we can\nchain multiple condensers together by passing `View`s along and exiting early if any\ncondenser returns a `Condensation`.\n\nFor example:\n\n # Use the pipeline condenser to chain multiple other condensers together\n condenser = PipelineCondenser(condensers=[\n CondenserA(...),\n CondenserB(...),\n CondenserC(...),\n ])\n\n result = condenser.condense(view)\n\n # Doing the same thing without the pipeline condenser requires more boilerplate\n # for the monadic chaining\n other_result = view\n\n if isinstance(other_result, View):\n other_result = CondenserA(...).condense(other_result)\n\n if isinstance(other_result, View):\n other_result = CondenserB(...).condense(other_result)\n\n if isinstance(other_result, View):\n other_result = CondenserC(...).condense(other_result)\n\n assert result == other_result" + }, + "ReasoningItemModel": { + "properties": { + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Id" + }, + "summary": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Summary" + }, + "content": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Content" + }, + "encrypted_content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Encrypted Content" + }, + "status": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Status" + } + }, + "type": "object", + "title": "ReasoningItemModel", + "description": "OpenAI Responses reasoning item (non-stream, subset we consume).\n\nDo not log or render encrypted_content." + }, + "RedactedThinkingBlock": { + "properties": { + "type": { + "type": "string", + "const": "redacted_thinking", + "title": "Type", + "default": "redacted_thinking" + }, + "data": { + "type": "string", + "title": "Data", + "description": "The redacted thinking content" + } + }, + "type": "object", + "required": [ + "data" + ], + "title": "RedactedThinkingBlock", + "description": "Redacted thinking block for previous responses without extended thinking.\n\nThis is used as a placeholder for assistant messages that were generated\nbefore extended thinking was enabled." + }, + "RemoteWorkspace": { + "properties": { + "kind": { + "type": "string", + "const": "RemoteWorkspace", + "title": "Kind", + "default": "RemoteWorkspace" + }, + "working_dir": { + "type": "string", + "title": "Working Dir", + "description": "The working directory for agent operations and tool execution." + }, + "host": { + "type": "string", + "title": "Host", + "description": "The remote host URL for the workspace." + }, + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Api Key", + "description": "API key for authenticating with the remote host." + } + }, + "type": "object", + "required": [ + "working_dir", + "host" + ], + "title": "RemoteWorkspace", + "description": "Remote Workspace Implementation." + }, + "ResponseLatency": { + "properties": { + "model": { + "type": "string", + "title": "Model" + }, + "latency": { + "type": "number", + "minimum": 0.0, + "title": "Latency", + "description": "Latency must be non-negative" + }, + "response_id": { + "type": "string", + "title": "Response Id" + } + }, + "type": "object", + "required": [ + "model", + "latency", + "response_id" + ], + "title": "ResponseLatency", + "description": "Metric tracking the round-trip time per completion call." + }, + "SecretsManager-Output": { + "properties": { + "secret_sources": { + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/components/schemas/LookupSecret" + }, + { + "$ref": "#/components/schemas/StaticSecret" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__conversation__secret_source__LookupSecret-Output__1": "#/components/schemas/LookupSecret", + "openhands__sdk__conversation__secret_source__StaticSecret-Output__1": "#/components/schemas/StaticSecret" + } + } + }, + "type": "object", + "title": "Secret Sources" + } + }, + "type": "object", + "title": "SecretsManager", + "description": "Manages secrets and injects them into bash commands when needed.\n\nThe secrets manager stores a mapping of secret keys to SecretSources\nthat retrieve the actual secret values. When a bash command is about to be\nexecuted, it scans the command for any secret keys and injects the corresponding\nenvironment variables.\n\nSecret sources will redact / encrypt their sensitive values as appropriate when\nserializing, depending on the content of the context. If a context is present\nand contains a 'cipher' object, this is used for encryption. If it contains a\nboolean 'expose_secrets' flag set to True, secrets are dunped in plain text.\nOtherwise secrets are redacted.\n\nAdditionally, it tracks the latest exported values to enable consistent masking\neven when callable secrets fail on subsequent calls." + }, + "SecurityAnalyzerBase": { + "properties": { + "kind": { + "type": "string", + "const": "LLMSecurityAnalyzer", + "title": "Kind", + "default": "LLMSecurityAnalyzer" + } + }, + "type": "object", + "title": "LLMSecurityAnalyzer", + "description": "LLM-based security analyzer.\n\nThis analyzer respects the security_risk attribute that can be set by the LLM\nwhen generating actions, similar to OpenHands' LLMRiskAnalyzer.\n\nIt provides a lightweight security analysis approach that leverages the LLM's\nunderstanding of action context and potential risks." + }, + "SecurityRisk": { + "type": "string", + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH" + ], + "title": "SecurityRisk", + "description": "Security risk levels for actions.\n\nBased on OpenHands security risk levels but adapted for agent-sdk.\nInteger values allow for easy comparison and ordering." + }, + "SendMessageRequest": { + "properties": { + "role": { + "type": "string", + "enum": [ + "user", + "system", + "assistant", + "tool" + ], + "title": "Role", + "default": "user" + }, + "content": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/TextContent" + }, + { + "$ref": "#/components/schemas/ImageContent" + } + ] + }, + "type": "array", + "title": "Content" + }, + "run": { + "type": "boolean", + "title": "Run", + "description": "Whether the agent loop should automatically run if not running", + "default": false + } + }, + "type": "object", + "title": "SendMessageRequest", + "description": "Payload to send a message to the agent.\n\nThis is a simplified version of openhands.sdk.Message." + }, + "ServerInfo": { + "properties": { + "uptime": { + "type": "number", + "title": "Uptime" + }, + "idle_time": { + "type": "number", + "title": "Idle Time" + }, + "title": { + "type": "string", + "title": "Title", + "default": "OpenHands Agent Server" + }, + "version": { + "type": "string", + "title": "Version", + "default": "1.0.0a4" + }, + "docs": { + "type": "string", + "title": "Docs", + "default": "/docs" + }, + "redoc": { + "type": "string", + "title": "Redoc", + "default": "/redoc" + } + }, + "type": "object", + "required": [ + "uptime", + "idle_time" + ], + "title": "ServerInfo" + }, + "SetConfirmationPolicyRequest": { + "properties": { + "policy": { + "oneOf": [ + { + "$ref": "#/components/schemas/AlwaysConfirm" + }, + { + "$ref": "#/components/schemas/ConfirmRisky" + }, + { + "$ref": "#/components/schemas/NeverConfirm" + } + ], + "title": "ConfirmationPolicyBase", + "description": "The confirmation policy to set", + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__security__confirmation_policy__AlwaysConfirm-Input__1": "#/components/schemas/AlwaysConfirm", + "openhands__sdk__security__confirmation_policy__ConfirmRisky-Input__1": "#/components/schemas/ConfirmRisky", + "openhands__sdk__security__confirmation_policy__NeverConfirm-Input__1": "#/components/schemas/NeverConfirm" + } + } + } + }, + "type": "object", + "required": [ + "policy" + ], + "title": "SetConfirmationPolicyRequest", + "description": "Payload to set confirmation policy for a conversation." + }, + "Skill": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "content": { + "type": "string", + "title": "Content" + }, + "trigger": { + "anyOf": [ + { + "oneOf": [ + { + "$ref": "#/components/schemas/KeywordTrigger" + }, + { + "$ref": "#/components/schemas/TaskTrigger" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "keyword": "#/components/schemas/KeywordTrigger", + "task": "#/components/schemas/TaskTrigger" + } + } + }, + { + "type": "null" + } + ], + "title": "Trigger" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source", + "description": "The source path or identifier of the skill. When it is None, it is treated as a programmatically defined skill." + }, + "mcp_tools": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Mcp Tools", + "description": "MCP tools configuration for the skill (repo skills only). It should conform to the MCPConfig schema: https://gofastmcp.com/clients/client#configuration-format" + }, + "inputs": { + "items": { + "$ref": "#/components/schemas/InputMetadata" + }, + "type": "array", + "title": "Inputs", + "description": "Input metadata for the skill (task skills only)" + } + }, + "type": "object", + "required": [ + "name", + "content", + "trigger" + ], + "title": "Skill", + "description": "A skill provides specialized knowledge or functionality.\n\nSkills use triggers to determine when they should be activated:\n- None: Always active, for repository-specific guidelines\n- KeywordTrigger: Activated when keywords appear in user messages\n- TaskTrigger: Activated for specific tasks, may require user input" + }, + "StartConversationRequest": { + "properties": { + "agent": { + "$ref": "#/components/schemas/Agent-Output" + }, + "workspace": { + "$ref": "#/components/schemas/LocalWorkspace", + "description": "Working directory for agent operations and tool execution" + }, + "conversation_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "Conversation Id", + "description": "Optional conversation ID. If not provided, a random UUID will be generated." + }, + "confirmation_policy": { + "oneOf": [ + { + "$ref": "#/components/schemas/AlwaysConfirm" + }, + { + "$ref": "#/components/schemas/ConfirmRisky" + }, + { + "$ref": "#/components/schemas/NeverConfirm" + } + ], + "title": "ConfirmationPolicyBase", + "description": "Controls when the conversation will prompt the user before continuing. Defaults to never.", + "default": { + "kind": "NeverConfirm" + }, + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__security__confirmation_policy__AlwaysConfirm-Input__1": "#/components/schemas/AlwaysConfirm", + "openhands__sdk__security__confirmation_policy__ConfirmRisky-Input__1": "#/components/schemas/ConfirmRisky", + "openhands__sdk__security__confirmation_policy__NeverConfirm-Input__1": "#/components/schemas/NeverConfirm" + } + } + }, + "initial_message": { + "anyOf": [ + { + "$ref": "#/components/schemas/SendMessageRequest" + }, + { + "type": "null" + } + ], + "description": "Initial message to pass to the LLM" + }, + "max_iterations": { + "type": "integer", + "minimum": 1.0, + "title": "Max Iterations", + "description": "If set, the max number of iterations the agent will run before stopping. This is useful to prevent infinite loops.", + "default": 500 + }, + "stuck_detection": { + "type": "boolean", + "title": "Stuck Detection", + "description": "If true, the conversation will use stuck detection to prevent infinite loops.", + "default": true + }, + "secrets": { + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/components/schemas/LookupSecret" + }, + { + "$ref": "#/components/schemas/StaticSecret" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__conversation__secret_source__LookupSecret-Input__1": "#/components/schemas/LookupSecret", + "openhands__sdk__conversation__secret_source__StaticSecret-Input__1": "#/components/schemas/StaticSecret" + } + } + }, + "type": "object", + "title": "Secrets", + "description": "Secrets available in the conversation" + } + }, + "type": "object", + "required": [ + "agent", + "workspace" + ], + "title": "StartConversationRequest", + "description": "Payload to create a new conversation.\n\nContains an Agent configuration along with conversation-specific options." + }, + "StaticSecret": { + "properties": { + "kind": { + "type": "string", + "const": "StaticSecret", + "title": "Kind", + "default": "StaticSecret" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Optional description for this secret" + }, + "value": { + "type": "string", + "format": "password", + "title": "Value", + "writeOnly": true + } + }, + "type": "object", + "required": [ + "value" + ], + "title": "StaticSecret", + "description": "A secret stored locally" + }, + "Success": { + "properties": { + "success": { + "type": "boolean", + "title": "Success", + "default": true + } + }, + "type": "object", + "title": "Success" + }, + "SystemPromptEvent": { + "properties": { + "kind": { + "type": "string", + "const": "SystemPromptEvent", + "title": "Kind", + "default": "SystemPromptEvent" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "agent" + }, + "system_prompt": { + "$ref": "#/components/schemas/TextContent", + "description": "The system prompt text" + }, + "tools": { + "items": { + "$ref": "#/components/schemas/ChatCompletionToolParam" + }, + "type": "array", + "title": "Tools", + "description": "List of tools in OpenAI tool format" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "system_prompt", + "tools" + ], + "title": "SystemPromptEvent", + "description": "System prompt added by the agent." + }, + "TaskItem": { + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "A brief title for the task." + }, + "notes": { + "type": "string", + "title": "Notes", + "description": "Additional details or notes about the task.", + "default": "" + }, + "status": { + "type": "string", + "enum": [ + "todo", + "in_progress", + "done" + ], + "title": "Status", + "description": "The current status of the task. One of 'todo', 'in_progress', or 'done'.", + "default": "todo" + } + }, + "type": "object", + "required": [ + "title" + ], + "title": "TaskItem" + }, + "TaskTrackerAction": { + "properties": { + "kind": { + "type": "string", + "const": "TaskTrackerAction", + "title": "Kind", + "default": "TaskTrackerAction" + }, + "command": { + "type": "string", + "enum": [ + "view", + "plan" + ], + "title": "Command", + "description": "The command to execute. `view` shows the current task list. `plan` creates or updates the task list based on provided requirements and progress. Always `view` the current list before making changes.", + "default": "view" + }, + "task_list": { + "items": { + "$ref": "#/components/schemas/TaskItem" + }, + "type": "array", + "title": "Task List", + "description": "The full task list. Required parameter of `plan` command." + } + }, + "additionalProperties": false, + "type": "object", + "title": "TaskTrackerAction", + "description": "An action where the agent writes or updates a task list for task management." + }, + "TaskTrackerObservation": { + "properties": { + "kind": { + "type": "string", + "const": "TaskTrackerObservation", + "title": "Kind", + "default": "TaskTrackerObservation" + }, + "content": { + "type": "string", + "title": "Content", + "description": "The formatted task list or status message", + "default": "" + }, + "command": { + "type": "string", + "title": "Command", + "description": "The command that was executed", + "default": "" + }, + "task_list": { + "items": { + "$ref": "#/components/schemas/TaskItem" + }, + "type": "array", + "title": "Task List", + "description": "The current task list" + } + }, + "additionalProperties": false, + "type": "object", + "title": "TaskTrackerObservation", + "description": "This data class represents the result of a task tracking operation." + }, + "TaskTrigger": { + "properties": { + "type": { + "type": "string", + "const": "task", + "title": "Type", + "default": "task" + }, + "triggers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Triggers" + } + }, + "type": "object", + "required": [ + "triggers" + ], + "title": "TaskTrigger", + "description": "Trigger for task-specific skills.\n\nThese skills are activated for specific task types and can modify prompts." + }, + "TextContent": { + "properties": { + "cache_prompt": { + "type": "boolean", + "title": "Cache Prompt", + "default": false + }, + "type": { + "type": "string", + "const": "text", + "title": "Type", + "default": "text" + }, + "text": { + "type": "string", + "title": "Text" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "text" + ], + "title": "TextContent" + }, + "ThinkAction": { + "properties": { + "kind": { + "type": "string", + "const": "ThinkAction", + "title": "Kind", + "default": "ThinkAction" + }, + "thought": { + "type": "string", + "title": "Thought", + "description": "The thought to log." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "thought" + ], + "title": "ThinkAction", + "description": "Action for logging a thought without making any changes." + }, + "ThinkObservation": { + "properties": { + "kind": { + "type": "string", + "const": "ThinkObservation", + "title": "Kind", + "default": "ThinkObservation" + }, + "content": { + "type": "string", + "title": "Content", + "description": "Confirmation message.", + "default": "Your thought has been logged." + } + }, + "additionalProperties": false, + "type": "object", + "title": "ThinkObservation", + "description": "Observation returned after logging a thought." + }, + "ThinkingBlock": { + "properties": { + "type": { + "type": "string", + "const": "thinking", + "title": "Type", + "default": "thinking" + }, + "thinking": { + "type": "string", + "title": "Thinking", + "description": "The thinking content" + }, + "signature": { + "type": "string", + "title": "Signature", + "description": "Cryptographic signature for the thinking block" + } + }, + "type": "object", + "required": [ + "thinking", + "signature" + ], + "title": "ThinkingBlock", + "description": "Anthropic thinking block for extended thinking feature.\n\nThis represents the raw thinking blocks returned by Anthropic models\nwhen extended thinking is enabled. These blocks must be preserved\nand passed back to the API for tool use scenarios." + }, + "TokenUsage": { + "properties": { + "model": { + "type": "string", + "title": "Model", + "default": "" + }, + "prompt_tokens": { + "type": "integer", + "minimum": 0.0, + "title": "Prompt Tokens", + "description": "Prompt tokens must be non-negative", + "default": 0 + }, + "completion_tokens": { + "type": "integer", + "minimum": 0.0, + "title": "Completion Tokens", + "description": "Completion tokens must be non-negative", + "default": 0 + }, + "cache_read_tokens": { + "type": "integer", + "minimum": 0.0, + "title": "Cache Read Tokens", + "description": "Cache read tokens must be non-negative", + "default": 0 + }, + "cache_write_tokens": { + "type": "integer", + "minimum": 0.0, + "title": "Cache Write Tokens", + "description": "Cache write tokens must be non-negative", + "default": 0 + }, + "reasoning_tokens": { + "type": "integer", + "minimum": 0.0, + "title": "Reasoning Tokens", + "description": "Reasoning tokens must be non-negative", + "default": 0 + }, + "context_window": { + "type": "integer", + "minimum": 0.0, + "title": "Context Window", + "description": "Context window must be non-negative", + "default": 0 + }, + "per_turn_token": { + "type": "integer", + "minimum": 0.0, + "title": "Per Turn Token", + "description": "Per turn tokens must be non-negative", + "default": 0 + }, + "response_id": { + "type": "string", + "title": "Response Id", + "default": "" + } + }, + "type": "object", + "title": "TokenUsage", + "description": "Metric tracking detailed token usage per completion call." + }, + "Tool": { + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name of the tool class, e.g., 'BashTool'. Import it from an `openhands.tools.` subpackage.", + "examples": [ + "BashTool", + "FileEditorTool", + "TaskTrackerTool" + ] + }, + "params": { + "additionalProperties": true, + "type": "object", + "title": "Params", + "description": "Parameters for the tool's .create() method, e.g., {'working_dir': '/app'}", + "examples": [ + { + "working_dir": "/workspace" + } + ] + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "Tool", + "description": "Defines a tool to be initialized for the agent.\n\nThis is only used in agent-sdk for type schema for server use." + }, + "UpdateConversationRequest": { + "properties": { + "title": { + "type": "string", + "maxLength": 200, + "minLength": 1, + "title": "Title", + "description": "New conversation title" + } + }, + "type": "object", + "required": [ + "title" + ], + "title": "UpdateConversationRequest", + "description": "Payload to update conversation metadata." + }, + "UpdateSecretsRequest": { + "properties": { + "secrets": { + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/components/schemas/LookupSecret" + }, + { + "$ref": "#/components/schemas/StaticSecret" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__conversation__secret_source__LookupSecret-Input__1": "#/components/schemas/LookupSecret", + "openhands__sdk__conversation__secret_source__StaticSecret-Input__1": "#/components/schemas/StaticSecret" + } + } + }, + "type": "object", + "title": "Secrets", + "description": "Dictionary mapping secret keys to values" + } + }, + "type": "object", + "required": [ + "secrets" + ], + "title": "UpdateSecretsRequest", + "description": "Payload to update secrets in a conversation." + }, + "UserRejectObservation": { + "properties": { + "kind": { + "type": "string", + "const": "UserRejectObservation", + "title": "Kind", + "default": "UserRejectObservation" + }, + "id": { + "type": "string", + "title": "Id", + "description": "Unique event id (ULID/UUID)" + }, + "timestamp": { + "type": "string", + "title": "Timestamp", + "description": "Event timestamp" + }, + "source": { + "type": "string", + "enum": [ + "agent", + "user", + "environment" + ], + "title": "Source", + "default": "environment" + }, + "tool_name": { + "type": "string", + "title": "Tool Name", + "description": "The tool name that this observation is responding to" + }, + "tool_call_id": { + "type": "string", + "title": "Tool Call Id", + "description": "The tool call id that this observation is responding to" + }, + "rejection_reason": { + "type": "string", + "title": "Rejection Reason", + "description": "Reason for rejecting the action", + "default": "User rejected the action" + }, + "action_id": { + "type": "string", + "title": "Action Id", + "description": "The action id that this observation is responding to" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "tool_name", + "tool_call_id", + "action_id" + ], + "title": "UserRejectObservation", + "description": "Observation when user rejects an action in confirmation mode." + }, + "VSCodeUrlResponse": { + "properties": { + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + } + }, + "type": "object", + "required": [ + "url" + ], + "title": "VSCodeUrlResponse", + "description": "Response model for VSCode URL." + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "Event": { + "oneOf": [ + { + "$ref": "#/components/schemas/Condensation" + }, + { + "$ref": "#/components/schemas/CondensationRequest" + }, + { + "$ref": "#/components/schemas/CondensationSummaryEvent" + }, + { + "$ref": "#/components/schemas/ConversationStateUpdateEvent" + }, + { + "$ref": "#/components/schemas/ActionEvent" + }, + { + "$ref": "#/components/schemas/MessageEvent" + }, + { + "$ref": "#/components/schemas/AgentErrorEvent" + }, + { + "$ref": "#/components/schemas/ObservationEvent" + }, + { + "$ref": "#/components/schemas/UserRejectObservation" + }, + { + "$ref": "#/components/schemas/SystemPromptEvent" + }, + { + "$ref": "#/components/schemas/PauseEvent" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__event__condenser__Condensation-Output__1": "#/components/schemas/Condensation", + "openhands__sdk__event__condenser__CondensationRequest-Output__1": "#/components/schemas/CondensationRequest", + "openhands__sdk__event__condenser__CondensationSummaryEvent-Output__1": "#/components/schemas/CondensationSummaryEvent", + "openhands__sdk__event__conversation_state__ConversationStateUpdateEvent-Output__1": "#/components/schemas/ConversationStateUpdateEvent", + "openhands__sdk__event__llm_convertible__action__ActionEvent-Output__1": "#/components/schemas/ActionEvent", + "openhands__sdk__event__llm_convertible__message__MessageEvent-Output__1": "#/components/schemas/MessageEvent", + "openhands__sdk__event__llm_convertible__observation__AgentErrorEvent-Output__1": "#/components/schemas/AgentErrorEvent", + "openhands__sdk__event__llm_convertible__observation__ObservationEvent-Output__1": "#/components/schemas/ObservationEvent", + "openhands__sdk__event__llm_convertible__observation__UserRejectObservation-Output__1": "#/components/schemas/UserRejectObservation", + "openhands__sdk__event__llm_convertible__system__SystemPromptEvent-Output__1": "#/components/schemas/SystemPromptEvent", + "openhands__sdk__event__user_action__PauseEvent-Output__1": "#/components/schemas/PauseEvent" + } + }, + "title": "Event" + }, + "BashEventBase": { + "oneOf": [ + { + "$ref": "#/components/schemas/BashCommand" + }, + { + "$ref": "#/components/schemas/BashOutput" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__agent_server__models__BashCommand-Output__1": "#/components/schemas/BashCommand", + "openhands__agent_server__models__BashOutput-Output__1": "#/components/schemas/BashOutput" + } + }, + "title": "BashEventBase" + }, + "Action": { + "oneOf": [ + { + "$ref": "#/components/schemas/MCPToolAction" + }, + { + "$ref": "#/components/schemas/FinishAction" + }, + { + "$ref": "#/components/schemas/ThinkAction" + }, + { + "$ref": "#/components/schemas/BrowserClickAction" + }, + { + "$ref": "#/components/schemas/BrowserCloseTabAction" + }, + { + "$ref": "#/components/schemas/BrowserGetContentAction" + }, + { + "$ref": "#/components/schemas/BrowserGetStateAction" + }, + { + "$ref": "#/components/schemas/BrowserGoBackAction" + }, + { + "$ref": "#/components/schemas/BrowserListTabsAction" + }, + { + "$ref": "#/components/schemas/BrowserNavigateAction" + }, + { + "$ref": "#/components/schemas/BrowserScrollAction" + }, + { + "$ref": "#/components/schemas/BrowserSwitchTabAction" + }, + { + "$ref": "#/components/schemas/BrowserTypeAction" + }, + { + "$ref": "#/components/schemas/ExecuteBashAction" + }, + { + "$ref": "#/components/schemas/FileEditorAction" + }, + { + "$ref": "#/components/schemas/TaskTrackerAction" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__mcp__definition__MCPToolAction-Output__1": "#/components/schemas/MCPToolAction", + "openhands__sdk__tool__builtins__finish__FinishAction-Output__1": "#/components/schemas/FinishAction", + "openhands__sdk__tool__builtins__think__ThinkAction-Output__1": "#/components/schemas/ThinkAction", + "openhands__tools__browser_use__definition__BrowserClickAction-Output__1": "#/components/schemas/BrowserClickAction", + "openhands__tools__browser_use__definition__BrowserCloseTabAction-Output__1": "#/components/schemas/BrowserCloseTabAction", + "openhands__tools__browser_use__definition__BrowserGetContentAction-Output__1": "#/components/schemas/BrowserGetContentAction", + "openhands__tools__browser_use__definition__BrowserGetStateAction-Output__1": "#/components/schemas/BrowserGetStateAction", + "openhands__tools__browser_use__definition__BrowserGoBackAction-Output__1": "#/components/schemas/BrowserGoBackAction", + "openhands__tools__browser_use__definition__BrowserListTabsAction-Output__1": "#/components/schemas/BrowserListTabsAction", + "openhands__tools__browser_use__definition__BrowserNavigateAction-Output__1": "#/components/schemas/BrowserNavigateAction", + "openhands__tools__browser_use__definition__BrowserScrollAction-Output__1": "#/components/schemas/BrowserScrollAction", + "openhands__tools__browser_use__definition__BrowserSwitchTabAction-Output__1": "#/components/schemas/BrowserSwitchTabAction", + "openhands__tools__browser_use__definition__BrowserTypeAction-Output__1": "#/components/schemas/BrowserTypeAction", + "openhands__tools__execute_bash__definition__ExecuteBashAction-Output__1": "#/components/schemas/ExecuteBashAction", + "openhands__tools__file_editor__definition__FileEditorAction-Output__1": "#/components/schemas/FileEditorAction", + "openhands__tools__task_tracker__definition__TaskTrackerAction-Output__1": "#/components/schemas/TaskTrackerAction" + } + }, + "title": "Action" + }, + "CondenserBase": { + "oneOf": [ + { + "$ref": "#/components/schemas/LLMSummarizingCondenser" + }, + { + "$ref": "#/components/schemas/NoOpCondenser" + }, + { + "$ref": "#/components/schemas/PipelineCondenser-Output" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__context__condenser__llm_summarizing_condenser__LLMSummarizingCondenser-Output__1": "#/components/schemas/LLMSummarizingCondenser", + "openhands__sdk__context__condenser__no_op_condenser__NoOpCondenser-Output__1": "#/components/schemas/NoOpCondenser", + "openhands__sdk__context__condenser__pipeline_condenser__PipelineCondenser-Output__1": "#/components/schemas/PipelineCondenser-Output" + } + }, + "title": "CondenserBase" + }, + "BaseWorkspace": { + "oneOf": [ + { + "$ref": "#/components/schemas/LocalWorkspace" + }, + { + "$ref": "#/components/schemas/RemoteWorkspace" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__workspace__local__LocalWorkspace-Output__1": "#/components/schemas/LocalWorkspace", + "openhands__sdk__workspace__remote__base__RemoteWorkspace-Output__1": "#/components/schemas/RemoteWorkspace" + } + }, + "title": "BaseWorkspace" + }, + "ConfirmationPolicyBase": { + "oneOf": [ + { + "$ref": "#/components/schemas/AlwaysConfirm" + }, + { + "$ref": "#/components/schemas/ConfirmRisky" + }, + { + "$ref": "#/components/schemas/NeverConfirm" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__security__confirmation_policy__AlwaysConfirm-Output__1": "#/components/schemas/AlwaysConfirm", + "openhands__sdk__security__confirmation_policy__ConfirmRisky-Output__1": "#/components/schemas/ConfirmRisky", + "openhands__sdk__security__confirmation_policy__NeverConfirm-Output__1": "#/components/schemas/NeverConfirm" + } + }, + "title": "ConfirmationPolicyBase" + }, + "Observation": { + "oneOf": [ + { + "$ref": "#/components/schemas/MCPToolObservation" + }, + { + "$ref": "#/components/schemas/FinishObservation" + }, + { + "$ref": "#/components/schemas/ThinkObservation" + }, + { + "$ref": "#/components/schemas/BrowserObservation" + }, + { + "$ref": "#/components/schemas/ExecuteBashObservation" + }, + { + "$ref": "#/components/schemas/FileEditorObservation" + }, + { + "$ref": "#/components/schemas/TaskTrackerObservation" + } + ], + "discriminator": { + "propertyName": "kind", + "mapping": { + "openhands__sdk__mcp__definition__MCPToolObservation-Output__1": "#/components/schemas/MCPToolObservation", + "openhands__sdk__tool__builtins__finish__FinishObservation-Output__1": "#/components/schemas/FinishObservation", + "openhands__sdk__tool__builtins__think__ThinkObservation-Output__1": "#/components/schemas/ThinkObservation", + "openhands__tools__browser_use__definition__BrowserObservation-Output__1": "#/components/schemas/BrowserObservation", + "openhands__tools__execute_bash__definition__ExecuteBashObservation-Output__1": "#/components/schemas/ExecuteBashObservation", + "openhands__tools__file_editor__definition__FileEditorObservation-Output__1": "#/components/schemas/FileEditorObservation", + "openhands__tools__task_tracker__definition__TaskTrackerObservation-Output__1": "#/components/schemas/TaskTrackerObservation" + } + }, + "title": "Observation" + } + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c672927 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020", "DOM"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "resolveJsonModule": true, + "moduleResolution": "node" + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "**/*.test.ts" + ] +} \ No newline at end of file From be94da363196c59709493b90c71f9cd76c06928a Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 30 Oct 2025 01:01:52 +0000 Subject: [PATCH 2/5] Add GitHub Actions workflows and testing infrastructure - Added CI workflow with Node.js matrix testing (18.x, 20.x, 22.x) - Added release workflow for automated npm publishing - Added Dependabot configuration for dependency updates - Added Dependabot auto-merge workflow for minor/patch updates - Created Jest configuration and basic test suite - Added comprehensive npm scripts for development workflow - Updated package.json with additional dev dependencies Co-authored-by: openhands --- .github/dependabot.yml | 31 +++++++ .github/workflows/ci.yml | 89 +++++++++++++++++++++ .github/workflows/dependabot-auto-merge.yml | 37 +++++++++ .github/workflows/release.yml | 55 +++++++++++++ jest.config.js | 29 +++++++ package.json | 14 +++- src/__tests__/index.test.ts | 52 ++++++++++++ 7 files changed, 304 insertions(+), 3 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/dependabot-auto-merge.yml create mode 100644 .github/workflows/release.yml create mode 100644 jest.config.js create mode 100644 src/__tests__/index.test.ts diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..71dda01 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,31 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 10 + reviewers: + - "All-Hands-AI/core-team" + assignees: + - "All-Hands-AI/core-team" + commit-message: + prefix: "deps" + include: "scope" + groups: + typescript: + patterns: + - "typescript" + - "@typescript-eslint/*" + testing: + patterns: + - "jest" + - "@types/jest" + - "@jest/*" + linting: + patterns: + - "eslint" + - "prettier" + - "@typescript-eslint/*" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c0d54da --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,89 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run linter + run: npm run lint + + - name: Run type check + run: npm run build + + - name: Run tests + run: npm test + + - name: Check formatting + run: npm run format:check + + build: + runs-on: ubuntu-latest + needs: test + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build package + run: npm run build + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ + retention-days: 7 + + security: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run security audit + run: npm audit --audit-level=moderate + + - name: Check for vulnerabilities + run: npm audit --audit-level=high --production \ No newline at end of file diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..c7dd6de --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,37 @@ +name: Dependabot Auto-merge + +on: + pull_request: + types: [opened, synchronize] + +jobs: + auto-merge: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Run build + run: npm run build + + - name: Auto-merge minor and patch updates + uses: pascalgn/merge-action@v0.15.6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + merge_method: squash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..201713c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,55 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Build package + run: npm run build + + - name: Publish to npm + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + body: | + ## Changes + + See [CHANGELOG.md](CHANGELOG.md) for details. + + ## Installation + + ```bash + npm install @openhands/agent-server-typescript-client@${{ github.ref_name }} + ``` \ No newline at end of file diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..609ee06 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,29 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/src'], + testMatch: [ + '**/__tests__/**/*.ts', + '**/?(*.)+(spec|test).ts' + ], + transform: { + '^.+\\.ts$': 'ts-jest' + }, + collectCoverageFrom: [ + 'src/**/*.ts', + '!src/**/*.d.ts', + '!src/__tests__/**/*' + ], + coverageDirectory: 'coverage', + coverageReporters: [ + 'text', + 'lcov', + 'html' + ], + moduleFileExtensions: [ + 'ts', + 'js', + 'json' + ], + testTimeout: 10000 +}; \ No newline at end of file diff --git a/package.json b/package.json index 6189d1c..5f49a9d 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,16 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsc", - "dev": "tsc --watch", - "test": "jest", "lint": "eslint src/**/*.ts", - "format": "prettier --write src/**/*.ts" + "lint:fix": "eslint src/**/*.ts --fix", + "format": "prettier --write \"src/**/*.ts\" \"examples/**/*.ts\"", + "format:check": "prettier --check \"src/**/*.ts\" \"examples/**/*.ts\"", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage", + "dev": "tsc --watch", + "clean": "rm -rf dist", + "prepublishOnly": "npm run clean && npm run build && npm test" }, "keywords": [ "openhands", @@ -25,6 +31,7 @@ "ws": "^8.14.2" }, "devDependencies": { + "@types/jest": "^29.0.0", "@types/node": "^20.0.0", "@types/uuid": "^9.0.6", "@types/ws": "^8.5.8", @@ -33,6 +40,7 @@ "eslint": "^8.0.0", "jest": "^29.0.0", "prettier": "^3.0.0", + "ts-jest": "^29.0.0", "typescript": "^5.0.0" }, "files": [ diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts new file mode 100644 index 0000000..40635ac --- /dev/null +++ b/src/__tests__/index.test.ts @@ -0,0 +1,52 @@ +import { RemoteConversation, RemoteWorkspace } from '../index'; + +describe('OpenHands Agent Server TypeScript Client', () => { + describe('Exports', () => { + it('should export RemoteConversation', () => { + expect(RemoteConversation).toBeDefined(); + expect(typeof RemoteConversation).toBe('function'); + }); + + it('should export RemoteWorkspace', () => { + expect(RemoteWorkspace).toBeDefined(); + expect(typeof RemoteWorkspace).toBe('function'); + }); + }); + + describe('RemoteConversation', () => { + it('should create instance with config', () => { + const config = { + baseUrl: 'http://localhost:8000', + apiKey: 'test-key' + }; + + const conversation = new RemoteConversation(config); + expect(conversation).toBeInstanceOf(RemoteConversation); + }); + + it('should have workspace property', () => { + const config = { + baseUrl: 'http://localhost:8000', + apiKey: 'test-key' + }; + + const conversation = new RemoteConversation(config); + expect(conversation.workspace).toBeDefined(); + expect(conversation.workspace).toBeInstanceOf(RemoteWorkspace); + }); + }); + + describe('RemoteWorkspace', () => { + it('should create instance with http client', () => { + const mockHttpClient = { + get: jest.fn(), + post: jest.fn(), + put: jest.fn(), + delete: jest.fn() + }; + + const workspace = new RemoteWorkspace(mockHttpClient as any); + expect(workspace).toBeInstanceOf(RemoteWorkspace); + }); + }); +}); \ No newline at end of file From b342ce65437acb3e341b220adcdec52563372c9a Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 30 Oct 2025 01:14:27 +0000 Subject: [PATCH 3/5] Fix GitHub Actions CI failures - Remove .js extensions from TypeScript imports for Jest compatibility - Fix test cases to use correct interfaces and handle workspace initialization - Update package-lock.json with latest dependencies - Ensure all tests pass and code formatting is correct Co-authored-by: openhands --- examples/basic-usage.ts | 8 +- logs.zip | 0 package-lock.json | 166 ++++++++++++++++++++++++ src/__tests__/index.test.ts | 39 +++--- src/client/http-client.ts | 41 ++++-- src/conversation/remote-conversation.ts | 48 +++---- src/conversation/remote-state.ts | 30 +++-- src/events/remote-events-list.ts | 18 +-- src/events/websocket-client.ts | 29 ++--- src/index.ts | 64 ++++----- src/models/conversation.ts | 16 +-- src/models/workspace.ts | 2 +- src/types/base.ts | 6 +- src/workspace/remote-workspace.ts | 59 +++++---- 14 files changed, 349 insertions(+), 177 deletions(-) create mode 100644 logs.zip diff --git a/examples/basic-usage.ts b/examples/basic-usage.ts index 4cde6af..b6f264f 100644 --- a/examples/basic-usage.ts +++ b/examples/basic-usage.ts @@ -49,7 +49,7 @@ async function main() { // Wait for the agent to finish (in a real application, you'd handle this differently) while (status === AgentExecutionStatus.RUNNING) { - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); status = await conversation.state.getAgentStatus(); console.log(`Current status: ${status}`); } @@ -72,7 +72,6 @@ async function main() { // Clean up await conversation.close(); console.log('Conversation closed'); - } catch (error) { console.error('Error:', error); } @@ -90,14 +89,13 @@ async function loadExistingConversation() { ); console.log(`Loaded conversation: ${conversation.id}`); - + // Get current status const status = await conversation.state.getAgentStatus(); console.log(`Status: ${status}`); // Clean up await conversation.close(); - } catch (error) { console.error('Error loading conversation:', error); } @@ -106,4 +104,4 @@ async function loadExistingConversation() { // Run the example if (require.main === module) { main().catch(console.error); -} \ No newline at end of file +} diff --git a/logs.zip b/logs.zip new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json index d35404b..8e6885f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "ws": "^8.14.2" }, "devDependencies": { + "@types/jest": "^29.0.0", "@types/node": "^20.0.0", "@types/uuid": "^9.0.6", "@types/ws": "^8.5.8", @@ -21,6 +22,7 @@ "eslint": "^8.0.0", "jest": "^29.0.0", "prettier": "^3.0.0", + "ts-jest": "^29.0.0", "typescript": "^5.0.0" } }, @@ -1295,6 +1297,17 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1891,6 +1904,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -2819,6 +2845,28 @@ "dev": true, "license": "MIT" }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3836,6 +3884,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3869,6 +3924,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -3936,6 +3998,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3950,6 +4022,13 @@ "dev": true, "license": "MIT" }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4791,6 +4870,72 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-jest": { + "version": "29.4.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz", + "integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4841,6 +4986,20 @@ "node": ">=14.17" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -4953,6 +5112,13 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index 40635ac..91d0a86 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -16,37 +16,40 @@ describe('OpenHands Agent Server TypeScript Client', () => { describe('RemoteConversation', () => { it('should create instance with config', () => { const config = { - baseUrl: 'http://localhost:8000', - apiKey: 'test-key' + host: 'http://localhost:8000', + apiKey: 'test-key', }; - + const conversation = new RemoteConversation(config); expect(conversation).toBeInstanceOf(RemoteConversation); }); - it('should have workspace property', () => { + it('should throw error when accessing workspace before initialization', () => { const config = { - baseUrl: 'http://localhost:8000', - apiKey: 'test-key' + host: 'http://localhost:8000', + apiKey: 'test-key', }; - + const conversation = new RemoteConversation(config); - expect(conversation.workspace).toBeDefined(); - expect(conversation.workspace).toBeInstanceOf(RemoteWorkspace); + expect(() => conversation.workspace).toThrow( + 'Workspace not initialized. Create or load a conversation first.' + ); }); }); describe('RemoteWorkspace', () => { - it('should create instance with http client', () => { - const mockHttpClient = { - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn() + it('should create instance with options', () => { + const options = { + host: 'http://localhost:8000', + workingDir: '/tmp', + apiKey: 'test-key', }; - - const workspace = new RemoteWorkspace(mockHttpClient as any); + + const workspace = new RemoteWorkspace(options); expect(workspace).toBeInstanceOf(RemoteWorkspace); + expect(workspace.host).toBe('http://localhost:8000'); + expect(workspace.workingDir).toBe('/tmp'); + expect(workspace.apiKey).toBe('test-key'); }); }); -}); \ No newline at end of file +}); diff --git a/src/client/http-client.ts b/src/client/http-client.ts index d6c3de5..e81c631 100644 --- a/src/client/http-client.ts +++ b/src/client/http-client.ts @@ -50,7 +50,7 @@ export class HttpClient { async request(options: RequestOptions): Promise> { const url = new URL(options.url, this.baseUrl); - + // Add query parameters if (options.params) { Object.entries(options.params).forEach(([key, value]) => { @@ -89,11 +89,12 @@ export class HttpClient { try { const response = await fetch(url.toString(), requestInit); - + // Check if status code is acceptable - const isAcceptable = options.acceptableStatusCodes?.has(response.status) || - (!options.acceptableStatusCodes && response.ok); - + const isAcceptable = + options.acceptableStatusCodes?.has(response.status) || + (!options.acceptableStatusCodes && response.ok); + if (!isAcceptable) { let errorContent: any; try { @@ -106,7 +107,7 @@ export class HttpClient { } catch { errorContent = null; } - + throw new HttpError( response.status, response.statusText, @@ -140,35 +141,49 @@ export class HttpClient { if (error instanceof HttpError) { throw error; } - + if (error instanceof Error) { if (error.name === 'AbortError') { throw new Error(`Request timeout after ${options.timeout || this.timeout}ms`); } throw new Error(`Request failed: ${error.message}`); } - + throw new Error('Unknown request error'); } } - async get(url: string, options?: Omit): Promise> { + async get( + url: string, + options?: Omit + ): Promise> { return this.request({ method: 'GET', url, ...options }); } - async post(url: string, data?: any, options?: Omit): Promise> { + async post( + url: string, + data?: any, + options?: Omit + ): Promise> { return this.request({ method: 'POST', url, data, ...options }); } - async put(url: string, data?: any, options?: Omit): Promise> { + async put( + url: string, + data?: any, + options?: Omit + ): Promise> { return this.request({ method: 'PUT', url, data, ...options }); } - async delete(url: string, options?: Omit): Promise> { + async delete( + url: string, + options?: Omit + ): Promise> { return this.request({ method: 'DELETE', url, ...options }); } close(): void { // No cleanup needed for fetch-based client } -} \ No newline at end of file +} diff --git a/src/conversation/remote-conversation.ts b/src/conversation/remote-conversation.ts index bbdf237..e568867 100644 --- a/src/conversation/remote-conversation.ts +++ b/src/conversation/remote-conversation.ts @@ -3,28 +3,28 @@ */ // import { v4 as uuidv4 } from 'uuid'; // Unused for now -import { HttpClient } from '../client/http-client.js'; -import { WebSocketCallbackClient } from '../events/websocket-client.js'; -import { RemoteState } from './remote-state.js'; -import { RemoteWorkspace } from '../workspace/remote-workspace.js'; -import { - ConversationID, - Message, - ConversationCallbackType, +import { HttpClient } from '../client/http-client'; +import { WebSocketCallbackClient } from '../events/websocket-client'; +import { RemoteState } from './remote-state'; +import { RemoteWorkspace } from '../workspace/remote-workspace'; +import { + ConversationID, + Message, + ConversationCallbackType, ConfirmationPolicyBase, ConversationStats, AgentBase, - SecretValue -} from '../types/base.js'; -import { + SecretValue, +} from '../types/base'; +import { ConversationInfo, SendMessageRequest, ConfirmationResponseRequest, CreateConversationRequest, GenerateTitleRequest, GenerateTitleResponse, - UpdateSecretsRequest -} from '../models/conversation.js'; + UpdateSecretsRequest, +} from '../models/conversation'; export interface RemoteConversationOptions { host: string; @@ -48,7 +48,7 @@ export class RemoteConversation { this.apiKey = options.apiKey; this._conversationId = options.conversationId; this.callback = options.callback; - + this.client = new HttpClient({ baseUrl: this.host, apiKey: this.apiKey, @@ -81,13 +81,15 @@ export class RemoteConversation { } async conversationStats(): Promise { - const response = await this.client.get(`/api/conversations/${this.id}/stats`); + const response = await this.client.get( + `/api/conversations/${this.id}/stats` + ); return response.data; } async sendMessage(message: string | Message): Promise { let messageContent: SendMessageRequest; - + if (typeof message === 'string') { messageContent = { role: 'user', @@ -127,9 +129,9 @@ export class RemoteConversation { if (llm) { request.llm = llm; } - + const response = await this.client.post( - `/api/conversations/${this.id}/generate_title`, + `/api/conversations/${this.id}/generate_title`, request ); return response.data.title; @@ -141,7 +143,7 @@ export class RemoteConversation { for (const [key, value] of Object.entries(secrets)) { secretStrings[key] = typeof value === 'function' ? value() : value; } - + const request: UpdateSecretsRequest = { secrets: secretStrings }; await this.client.post(`/api/conversations/${this.id}/update_secrets`, request); } @@ -154,7 +156,7 @@ export class RemoteConversation { // Create combined callback that handles both user callback and state updates const combinedCallback: ConversationCallbackType = (event) => { // Add event to the events list - this.state.events.addEvent(event).catch(error => { + this.state.events.addEvent(event).catch((error) => { console.error('Error adding event to events list:', error); }); @@ -248,7 +250,9 @@ export class RemoteConversation { }); // Verify conversation exists and get workspace info - const response = await conversation.client.get(`/api/conversations/${conversationId}`); + const response = await conversation.client.get( + `/api/conversations/${conversationId}` + ); const conversationInfo = response.data; // Initialize workspace @@ -268,4 +272,4 @@ export class RemoteConversation { this._workspace.close(); } } -} \ No newline at end of file +} diff --git a/src/conversation/remote-state.ts b/src/conversation/remote-state.ts index efb922a..95e338d 100644 --- a/src/conversation/remote-state.ts +++ b/src/conversation/remote-state.ts @@ -2,18 +2,18 @@ * Remote conversation state management */ -import { HttpClient } from '../client/http-client.js'; -import { RemoteEventsList } from '../events/remote-events-list.js'; -import { - ConversationID, - Event, - AgentExecutionStatus, - ConfirmationPolicyBase, +import { HttpClient } from '../client/http-client'; +import { RemoteEventsList } from '../events/remote-events-list'; +import { + ConversationID, + Event, + AgentExecutionStatus, + ConfirmationPolicyBase, // ConversationStats, // Unused for now AgentBase, - ConversationCallbackType -} from '../types/base.js'; -import { ConversationInfo } from '../models/conversation.js'; + ConversationCallbackType, +} from '../types/base'; +import { ConversationInfo } from '../models/conversation'; const FULL_STATE_KEY = '__full_state__'; @@ -44,7 +44,9 @@ export class RemoteState { } // Fallback to REST API if no cached state - const response = await this.client.get(`/api/conversations/${this.conversationId}`); + const response = await this.client.get( + `/api/conversations/${this.conversationId}` + ); const state = response.data; this.cachedState = state; return state; @@ -73,7 +75,7 @@ export class RemoteState { createStateUpdateCallback(): ConversationCallbackType { return (event: Event) => { if (event.kind === 'ConversationStateUpdateEvent') { - this.updateStateFromEvent(event as ConversationStateUpdateEvent).catch(error => { + this.updateStateFromEvent(event as ConversationStateUpdateEvent).catch((error) => { console.error('Error updating state from event:', error); }); } @@ -100,7 +102,7 @@ export class RemoteState { async setAgentStatus(value: AgentExecutionStatus): Promise { throw new Error( `Setting agent_status on RemoteState has no effect. ` + - `Remote agent status is managed server-side. Attempted to set: ${value}` + `Remote agent status is managed server-side. Attempted to set: ${value}` ); } @@ -186,4 +188,4 @@ class AsyncLock { } }); } -} \ No newline at end of file +} diff --git a/src/events/remote-events-list.ts b/src/events/remote-events-list.ts index 2785b88..0cf314c 100644 --- a/src/events/remote-events-list.ts +++ b/src/events/remote-events-list.ts @@ -2,10 +2,10 @@ * Remote events list implementation with caching and synchronization */ -import { HttpClient } from '../client/http-client.js'; -import { Event, ConversationCallbackType } from '../types/base.js'; -// import { EventSortOrder } from '../types/base.js'; // Unused for now -import { EventPage } from '../types/base.js'; +import { HttpClient } from '../client/http-client'; +import { Event, ConversationCallbackType } from '../types/base'; +// import { EventSortOrder } from '../types/base'; // Unused for now +import { EventPage } from '../types/base'; export class RemoteEventsList { private client: HttpClient; @@ -23,7 +23,7 @@ export class RemoteEventsList { private async doFullSync(): Promise { console.debug(`Performing full sync for conversation ${this.conversationId}`); - + const events: Event[] = []; let pageId: string | undefined; @@ -38,7 +38,7 @@ export class RemoteEventsList { `/api/conversations/${this.conversationId}/events/search`, { params } ); - + const data = response.data; events.push(...data.items); @@ -51,7 +51,7 @@ export class RemoteEventsList { await this.lock.acquire(async () => { this.cachedEvents = events; this.cachedEventIds.clear(); - events.forEach(e => this.cachedEventIds.add(e.id)); + events.forEach((e) => this.cachedEventIds.add(e.id)); }); console.debug(`Full sync completed, ${events.length} events cached`); @@ -75,7 +75,7 @@ export class RemoteEventsList { createDefaultCallback(): ConversationCallbackType { return (event: Event) => { - this.addEvent(event).catch(error => { + this.addEvent(event).catch((error) => { console.error('Error adding event to cache:', error); }); }; @@ -136,4 +136,4 @@ class AsyncLock { } }); } -} \ No newline at end of file +} diff --git a/src/events/websocket-client.ts b/src/events/websocket-client.ts index d83fee5..80f1f5d 100644 --- a/src/events/websocket-client.ts +++ b/src/events/websocket-client.ts @@ -3,7 +3,7 @@ */ import WebSocket from 'ws'; -import { Event, ConversationCallbackType } from '../types/base.js'; +import { Event, ConversationCallbackType } from '../types/base'; export interface WebSocketClientOptions { host: string; @@ -35,19 +35,19 @@ export class WebSocketCallbackClient { if (this.ws) { return; } - + this.shouldReconnect = true; this.connect(); } stop(): void { this.shouldReconnect = false; - + if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = undefined; } - + if (this.ws) { this.ws.close(); this.ws = undefined; @@ -60,17 +60,17 @@ export class WebSocketCallbackClient { const url = new URL(this.host); const wsScheme = url.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${wsScheme}//${url.host}${url.pathname.replace(/\/$/, '')}/sockets/events/${this.conversationId}`; - + // Add API key as query parameter if provided const finalUrl = this.apiKey ? `${wsUrl}?session_api_key=${this.apiKey}` : wsUrl; - + this.ws = new WebSocket(finalUrl); - + this.ws.on('open', () => { console.debug(`WebSocket connected to ${finalUrl}`); this.currentDelay = this.reconnectDelay; // Reset delay on successful connection }); - + this.ws.on('message', (data: WebSocket.Data) => { try { const message = data.toString(); @@ -80,23 +80,22 @@ export class WebSocketCallbackClient { console.error('Error processing WebSocket message:', error); } }); - + this.ws.on('close', (code: number, reason: Buffer) => { console.debug(`WebSocket closed: ${code} ${reason.toString()}`); this.ws = undefined; - + if (this.shouldReconnect) { this.scheduleReconnect(); } }); - + this.ws.on('error', (error: Error) => { console.debug('WebSocket error:', error); if (this.shouldReconnect) { this.scheduleReconnect(); } }); - } catch (error) { console.error('Failed to create WebSocket connection:', error); if (this.shouldReconnect) { @@ -109,9 +108,9 @@ export class WebSocketCallbackClient { if (this.reconnectTimer) { return; } - + console.debug(`Scheduling WebSocket reconnect in ${this.currentDelay}ms`); - + this.reconnectTimer = setTimeout(() => { this.reconnectTimer = undefined; if (this.shouldReconnect) { @@ -121,4 +120,4 @@ export class WebSocketCallbackClient { } }, this.currentDelay); } -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index c316eee..8565d0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,21 +1,21 @@ /** * OpenHands Agent Server TypeScript Client - * + * * A TypeScript client library for the OpenHands Agent Server API that mirrors * the structure and functionality of the Python SDK. */ // Main conversation and workspace classes -export { RemoteConversation } from './conversation/remote-conversation.js'; -export { RemoteWorkspace } from './workspace/remote-workspace.js'; -export { RemoteState } from './conversation/remote-state.js'; -export { RemoteEventsList } from './events/remote-events-list.js'; +export { RemoteConversation } from './conversation/remote-conversation'; +export { RemoteWorkspace } from './workspace/remote-workspace'; +export { RemoteState } from './conversation/remote-state'; +export { RemoteEventsList } from './events/remote-events-list'; // WebSocket client for real-time events -export { WebSocketCallbackClient } from './events/websocket-client.js'; +export { WebSocketCallbackClient } from './events/websocket-client'; // HTTP client -export { HttpClient, HttpError } from './client/http-client.js'; +export { HttpClient, HttpError } from './client/http-client'; // Types and interfaces export type { @@ -36,20 +36,12 @@ export type { ConfirmationPolicyBase, NeverConfirm, AlwaysConfirm, -} from './types/base.js'; +} from './types/base'; -export { - EventSortOrder, - AgentExecutionStatus, -} from './types/base.js'; +export { EventSortOrder, AgentExecutionStatus } from './types/base'; // Workspace models -export type { - CommandResult, - FileOperationResult, - GitChange, - GitDiff, -} from './models/workspace.js'; +export type { CommandResult, FileOperationResult, GitChange, GitDiff } from './models/workspace'; // Conversation models export type { @@ -60,35 +52,25 @@ export type { GenerateTitleRequest, GenerateTitleResponse, UpdateSecretsRequest, -} from './models/conversation.js'; +} from './models/conversation'; // Client options -export type { - HttpClientOptions, - RequestOptions, - HttpResponse, -} from './client/http-client.js'; +export type { HttpClientOptions, RequestOptions, HttpResponse } from './client/http-client'; -export type { - WebSocketClientOptions, -} from './events/websocket-client.js'; +export type { WebSocketClientOptions } from './events/websocket-client'; -export type { - RemoteWorkspaceOptions, -} from './workspace/remote-workspace.js'; +export type { RemoteWorkspaceOptions } from './workspace/remote-workspace'; -export type { - RemoteConversationOptions, -} from './conversation/remote-conversation.js'; +export type { RemoteConversationOptions } from './conversation/remote-conversation'; // Re-import for default export -import { RemoteConversation } from './conversation/remote-conversation.js'; -import { RemoteWorkspace } from './workspace/remote-workspace.js'; -import { RemoteState } from './conversation/remote-state.js'; -import { RemoteEventsList } from './events/remote-events-list.js'; -import { WebSocketCallbackClient } from './events/websocket-client.js'; -import { HttpClient, HttpError } from './client/http-client.js'; -import { EventSortOrder, AgentExecutionStatus } from './types/base.js'; +import { RemoteConversation } from './conversation/remote-conversation'; +import { RemoteWorkspace } from './workspace/remote-workspace'; +import { RemoteState } from './conversation/remote-state'; +import { RemoteEventsList } from './events/remote-events-list'; +import { WebSocketCallbackClient } from './events/websocket-client'; +import { HttpClient, HttpError } from './client/http-client'; +import { EventSortOrder, AgentExecutionStatus } from './types/base'; // Default export for convenience export default { @@ -101,4 +83,4 @@ export default { HttpError, EventSortOrder, AgentExecutionStatus, -}; \ No newline at end of file +}; diff --git a/src/models/conversation.ts b/src/models/conversation.ts index e4c555b..fa3570f 100644 --- a/src/models/conversation.ts +++ b/src/models/conversation.ts @@ -2,14 +2,14 @@ * Conversation-related models and interfaces */ -import { - ConversationID, - // Event, // Unused for now - AgentExecutionStatus, - ConfirmationPolicyBase, +import { + ConversationID, + // Event, // Unused for now + AgentExecutionStatus, + ConfirmationPolicyBase, ConversationStats, - AgentBase -} from '../types/base.js'; + AgentBase, +} from '../types/base'; export interface ConversationInfo { id: ConversationID; @@ -57,4 +57,4 @@ export interface GenerateTitleResponse { export interface UpdateSecretsRequest { secrets: Record; -} \ No newline at end of file +} diff --git a/src/models/workspace.ts b/src/models/workspace.ts index c1e8b26..6259882 100644 --- a/src/models/workspace.ts +++ b/src/models/workspace.ts @@ -28,4 +28,4 @@ export interface GitDiff { path: string; diff: string; [key: string]: any; -} \ No newline at end of file +} diff --git a/src/types/base.ts b/src/types/base.ts index 9358e96..9aaa1ef 100644 --- a/src/types/base.ts +++ b/src/types/base.ts @@ -63,7 +63,7 @@ export interface EventPage { export enum EventSortOrder { TIMESTAMP = 'TIMESTAMP', - REVERSE_TIMESTAMP = 'REVERSE_TIMESTAMP' + REVERSE_TIMESTAMP = 'REVERSE_TIMESTAMP', } export enum AgentExecutionStatus { @@ -71,7 +71,7 @@ export enum AgentExecutionStatus { RUNNING = 'running', PAUSED = 'paused', FINISHED = 'finished', - ERROR = 'error' + ERROR = 'error', } export interface ConversationStats { @@ -97,4 +97,4 @@ export interface AlwaysConfirm extends ConfirmationPolicyBase { export type ConversationCallbackType = (event: Event) => void; -export type SecretValue = string | (() => string); \ No newline at end of file +export type SecretValue = string | (() => string); diff --git a/src/workspace/remote-workspace.ts b/src/workspace/remote-workspace.ts index 65961cc..cdf252b 100644 --- a/src/workspace/remote-workspace.ts +++ b/src/workspace/remote-workspace.ts @@ -2,8 +2,8 @@ * Remote workspace implementation for executing commands and file operations */ -import { HttpClient } from '../client/http-client.js'; -import { CommandResult, FileOperationResult, GitChange, GitDiff } from '../models/workspace.js'; +import { HttpClient } from '../client/http-client'; +import { CommandResult, FileOperationResult, GitChange, GitDiff } from '../models/workspace'; export interface RemoteWorkspaceOptions { host: string; @@ -21,7 +21,7 @@ export class RemoteWorkspace { this.host = options.host.replace(/\/$/, ''); this.workingDir = options.workingDir; this.apiKey = options.apiKey; - + this.client = new HttpClient({ baseUrl: this.host, apiKey: this.apiKey, @@ -42,20 +42,18 @@ export class RemoteWorkspace { command, timeout: Math.floor(timeout), }; - + if (cwd) { payload.cwd = cwd; } - const startResponse = await this.client.post( - '/api/bash/start_bash_command', - payload, - { timeout: (timeout + 5) * 1000 } - ); - + const startResponse = await this.client.post('/api/bash/start_bash_command', payload, { + timeout: (timeout + 5) * 1000, + }); + const bashCommand = startResponse.data; const commandId = bashCommand.id; - + console.debug(`Started command with ID: ${commandId}`); // Step 2: Poll for output until command completes @@ -74,7 +72,7 @@ export class RemoteWorkspace { }, timeout: timeout * 1000, }); - + const searchResult = searchResponse.data; // Filter for BashOutput events for this command @@ -98,7 +96,7 @@ export class RemoteWorkspace { } // Wait a bit before polling again - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); } // If we timed out waiting for completion @@ -119,7 +117,6 @@ export class RemoteWorkspace { stderr, timeout_occurred: exitCode === -1 && stderr.includes('timed out'), }; - } catch (error) { console.error(`Remote command execution failed: ${error}`); return { @@ -140,10 +137,10 @@ export class RemoteWorkspace { // For Node.js environments, we can read the file const fs = await import('fs'); const path = await import('path'); - + const fileContent = await fs.promises.readFile(sourcePath); const fileName = path.basename(sourcePath); - + // Create FormData for file upload const formData = new FormData(); const blob = new Blob([fileContent]); @@ -166,7 +163,6 @@ export class RemoteWorkspace { file_size: resultData.file_size, error: resultData.error, }; - } catch (error) { console.error(`Remote file upload failed: ${error}`); return { @@ -182,22 +178,26 @@ export class RemoteWorkspace { console.debug(`Remote file download: ${sourcePath} -> ${destinationPath}`); try { - const response = await this.client.get(`/api/file/download/${encodeURIComponent(sourcePath)}`, { - timeout: 60000, - }); + const response = await this.client.get( + `/api/file/download/${encodeURIComponent(sourcePath)}`, + { + timeout: 60000, + } + ); // For Node.js environments, write the file const fs = await import('fs'); const path = await import('path'); - + // Ensure destination directory exists const destDir = path.dirname(destinationPath); await fs.promises.mkdir(destDir, { recursive: true }); - + // Write the file content - const content = typeof response.data === 'string' ? response.data : JSON.stringify(response.data); + const content = + typeof response.data === 'string' ? response.data : JSON.stringify(response.data); await fs.promises.writeFile(destinationPath, content); - + const stats = await fs.promises.stat(destinationPath); return { @@ -206,7 +206,6 @@ export class RemoteWorkspace { destination_path: destinationPath, file_size: stats.size, }; - } catch (error) { console.error(`Remote file download failed: ${error}`); return { @@ -225,7 +224,9 @@ export class RemoteWorkspace { }); return response.data; } catch (error) { - throw new Error(`Failed to get git changes: ${error instanceof Error ? error.message : String(error)}`); + throw new Error( + `Failed to get git changes: ${error instanceof Error ? error.message : String(error)}` + ); } } @@ -236,11 +237,13 @@ export class RemoteWorkspace { }); return response.data; } catch (error) { - throw new Error(`Failed to get git diff: ${error instanceof Error ? error.message : String(error)}`); + throw new Error( + `Failed to get git diff: ${error instanceof Error ? error.message : String(error)}` + ); } } close(): void { this.client.close(); } -} \ No newline at end of file +} From 115cea9cad8500e717f56f74dd787d2efe306a76 Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 30 Oct 2025 01:14:46 +0000 Subject: [PATCH 4/5] Remove accidentally committed logs.zip file Co-authored-by: openhands --- logs.zip | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 logs.zip diff --git a/logs.zip b/logs.zip deleted file mode 100644 index e69de29..0000000 From 1e6778e0c6fd35ec584a8bbb429c6f873956297c Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 30 Oct 2025 01:23:21 +0000 Subject: [PATCH 5/5] Add repository documentation explaining purpose and source materials - Created .openhands/microagents/repo.md with comprehensive overview - Documents relationship to OpenAPI spec and Python SDK - Explains architectural alignment and development workflow - Provides context for usage and related repositories Co-authored-by: openhands --- .openhands/microagents/repo.md | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .openhands/microagents/repo.md diff --git a/.openhands/microagents/repo.md b/.openhands/microagents/repo.md new file mode 100644 index 0000000..5975358 --- /dev/null +++ b/.openhands/microagents/repo.md @@ -0,0 +1,83 @@ +# Agent Server TypeScript Client + +## General Purpose + +This repository contains a TypeScript client library for the OpenHands Agent Server. It provides a complete, type-safe interface for interacting with the OpenHands Agent Server API, enabling developers to build applications that can create and manage AI agent conversations, workspaces, and real-time event streams. + +The client is designed to mirror the structure and functionality of the Python SDK (`software-agent-sdk`) while providing idiomatic TypeScript/JavaScript APIs with full type safety and modern development tooling. + +## Key Features + +- **Complete API Coverage**: Implements all endpoints from the OpenHands Agent Server OpenAPI specification +- **Type Safety**: Full TypeScript support with comprehensive interfaces and type definitions +- **Real-time Events**: WebSocket client for streaming conversation events and agent status updates +- **Workspace Management**: File operations, uploads, downloads, and workspace state management +- **Conversation Lifecycle**: Create, start, stop, and manage AI agent conversations +- **Error Handling**: Robust error handling with custom exception classes and retry logic +- **Modern Tooling**: ESLint, Prettier, Jest testing framework, and GitHub Actions CI/CD + +## Source Material + +This TypeScript client is based on the following source materials: + +### 1. OpenAPI Specification +- **Source**: [OpenHands Docs - Agent SDK OpenAPI](https://github.com/OpenHands/docs/blob/main/openapi/agent-sdk.json) +- **Purpose**: Defines the complete REST API specification for the OpenHands Agent Server +- **Usage**: Used to generate TypeScript interfaces, API client methods, and ensure complete endpoint coverage + +### 2. Python SDK Reference Implementation +- **Source**: [OpenHands Software Agent SDK](https://github.com/OpenHands/software-agent-sdk) +- **Key Components**: + - `RemoteConversation` class - Main conversation management + - `RemoteWorkspace` class - Workspace file operations + - `RemoteState` class - Agent state and configuration management +- **Purpose**: Provides the architectural blueprint and API design patterns +- **Usage**: Ensures consistent class names, method signatures, and behavior across language implementations + +### 3. Agent Server Implementation +- **Source**: Located within the `software-agent-sdk` repository as `agent-server` +- **Purpose**: The actual server implementation that this client communicates with +- **Usage**: Reference for understanding expected request/response formats and WebSocket event structures + +## Architecture Alignment + +The TypeScript client maintains architectural consistency with the Python SDK: + +```typescript +// TypeScript Client (this repo) +const conversation = new RemoteConversation(config); +await conversation.start(); +await conversation.workspace.write_file('/path/file.txt', 'content'); +const state = conversation.state; +``` + +```python +# Python SDK (reference implementation) +conversation = RemoteConversation(config) +await conversation.start() +await conversation.workspace.write_file('/path/file.txt', 'content') +state = conversation.state +``` + +## Development Workflow + +1. **API Changes**: When the OpenAPI specification is updated, corresponding TypeScript interfaces and client methods should be updated +2. **Feature Parity**: New features added to the Python SDK should be implemented in this TypeScript client +3. **Testing**: All functionality should be tested against a running OpenHands Agent Server instance +4. **Documentation**: API changes should be reflected in the README.md and example code + +## Related Repositories + +- **[OpenHands/OpenHands](https://github.com/OpenHands/OpenHands)**: Core OpenHands application +- **[OpenHands/software-agent-sdk](https://github.com/OpenHands/software-agent-sdk)**: Python SDK and Agent Server +- **[OpenHands/docs](https://github.com/OpenHands/docs)**: Documentation and OpenAPI specifications + +## Usage Context + +This client is intended for developers who want to: +- Build web applications that interact with OpenHands agents +- Create Node.js services that manage agent conversations +- Integrate OpenHands capabilities into existing TypeScript/JavaScript applications +- Develop custom frontends for the OpenHands Agent Server + +The client abstracts away the complexity of HTTP requests, WebSocket management, and API authentication, providing a clean, type-safe interface for all OpenHands Agent Server functionality. \ No newline at end of file