diff --git a/.cursor/commands/qa.md b/.cursor/commands/qa.md index b1d65e1..63d7d04 100644 --- a/.cursor/commands/qa.md +++ b/.cursor/commands/qa.md @@ -63,6 +63,7 @@ Here are all valid language + template combinations: > **Note:** The `yutori-computer-use` template supports two modes: `computer_use` (default, full VM screenshots) and `playwright` (viewport-only screenshots via CDP). Both modes should be tested. | python | sample-app | py-sample-app | python-basic | No | - | +| python | gemini-computer-use | py-gemini-cua | python-gemini-cua | Yes | GOOGLE_API_KEY | | python | captcha-solver | py-captcha-solver | python-captcha-solver | No | - | | python | browser-use | py-browser-use | python-bu | Yes | OPENAI_API_KEY | | python | anthropic-computer-use | py-anthropic-cua | python-anthropic-cua | Yes | ANTHROPIC_API_KEY | @@ -99,6 +100,7 @@ Run each of these (they are non-interactive when all flags are provided): ../bin/kernel create -n py-openai-cua -l python -t openai-computer-use ../bin/kernel create -n py-openagi-cua -l python -t openagi-computer-use ../bin/kernel create -n py-claude-agent-sdk -l python -t claude-agent-sdk +../bin/kernel create -n py-gemini-cua -l python -t gemini-computer-use ../bin/kernel create -n py-yutori-cua -l python -t yutori-computer-use ``` @@ -241,6 +243,15 @@ echo "ANTHROPIC_API_KEY=" > .env cd .. ``` +**py-gemini-cua** (needs GOOGLE_API_KEY): + +```bash +cd py-gemini-cua +echo "GOOGLE_API_KEY=" > .env +../bin/kernel deploy main.py --env-file .env +cd .. +``` + **py-yutori-cua** (needs YUTORI_API_KEY): ```bash @@ -262,7 +273,7 @@ kernel invoke ts-stagehand teamsize-task --payload '{"company": "Kernel"}' kernel invoke ts-anthropic-cua cua-task --payload '{"query": "Go to http://magnitasks.com, Click the Tasks option in the left-side bar, and move the 5 items in the To Do and In Progress items to the Done section of the Kanban board. You are done successfully when the items are moved.", "record_replay": true}' kernel invoke ts-magnitude mag-url-extract --payload '{"url": "https://en.wikipedia.org/wiki/Special:Random"}' kernel invoke ts-openai-cua cua-task --payload '{"task": "Go to https://news.ycombinator.com and get the top 5 articles"}' -kernel invoke ts-gemini-cua gemini-cua-task --payload '{"startingUrl": "https://www.magnitasks.com/", "instruction": "Click the Tasks option in the left-side bar, and move the 5 items in the To Do and In Progress items to the Done section of the Kanban board? You are done successfully when the items are moved."}' +kernel invoke ts-gemini-cua cua-task --payload '{"query": "Go to http://magnitasks.com, Click the Tasks option in the left-side bar, and move the 5 items in the To Do and In Progress items to the Done section of the Kanban board. You are done successfully when the items are moved.", "record_replay": true}' kernel invoke ts-claude-agent-sdk agent-task --payload '{"task": "Go to https://news.ycombinator.com and get the top 3 stories"}' kernel invoke ts-yutori-cua cua-task --payload '{"query": "Go to http://magnitasks.com, Click the Tasks option in the left-side bar, and drag the 5 items in the To Do and In Progress columns to the Done section of the Kanban board. You are done successfully when the items are dragged to Done. Do not click into the items.", "record_replay": true, "mode": "computer_use"}' kernel invoke ts-yutori-cua cua-task --payload '{"query": "Go to http://magnitasks.com, Click the Tasks option in the left-side bar, and drag the 5 items in the To Do and In Progress columns to the Done section of the Kanban board. You are done successfully when the items are dragged to Done. Do not click into the items.", "record_replay": true, "mode": "playwright"}' @@ -275,13 +286,14 @@ kernel invoke python-anthropic-cua cua-task --payload '{"query": "Go to http://m kernel invoke python-openai-cua cua-task --payload '{"task": "Go to https://news.ycombinator.com and get the top 5 articles"}' kernel invoke python-openagi-cua openagi-default-task -p '{"instruction": "Navigate to https://agiopen.org and click the What is Computer Use? button"}' kernel invoke py-claude-agent-sdk agent-task --payload '{"task": "Go to https://news.ycombinator.com and get the top 3 stories"}' +kernel invoke python-gemini-cua cua-task --payload '{"query": "Go to http://magnitasks.com, Click the Tasks option in the left-side bar, and move the 5 items in the To Do and In Progress items to the Done section of the Kanban board. You are done successfully when the items are moved.", "record_replay": true}' kernel invoke python-yutori-cua cua-task --payload '{"query": "Go to http://magnitasks.com, Click the Tasks option in the left-side bar, and drag the 5 items in the To Do and In Progress columns to the Done section of the Kanban board. You are done successfully when the items are dragged to Done. Do not click into the items.", "record_replay": true, "mode": "computer_use"}' kernel invoke python-yutori-cua cua-task --payload '{"query": "Go to http://magnitasks.com, Click the Tasks option in the left-side bar, and drag the 5 items in the To Do and In Progress columns to the Done section of the Kanban board. You are done successfully when the items are dragged to Done. Do not click into the items.", "record_replay": true, "mode": "playwright"}' ``` ## Step 7: Automated Runtime Testing (Optional) -**STOP and ask the human:** "Would you like me to automatically invoke all 19 test cases and report back on their runtime status?" +**STOP and ask the human:** "Would you like me to automatically invoke all 21 test cases and report back on their runtime status?" If the human agrees, invoke each template use the Kernel CLI and collect results. Present findings in this format: @@ -310,6 +322,7 @@ If the human agrees, invoke each template use the Kernel CLI and collect results | py-openai-cua | python-openai-cua | | | | py-openagi-cua | python-openagi-cua | | | | py-claude-agent-sdk | py-claude-agent-sdk | | | +| py-gemini-cua | python-gemini-cua | | | | py-yutori-cua | python-yutori-cua | | mode: computer_use | | py-yutori-cua | python-yutori-cua | | mode: playwright | @@ -324,9 +337,9 @@ Notes should include brief error messages for failures or confirmation of succes - [ ] Built CLI with `make build` - [ ] Created QA directory - [ ] Got KERNEL_API_KEY from human -- [ ] Created all 17 template variations +- [ ] Created all 18 template variations - [ ] Got required API keys from human (OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY, OAGI_API_KEY, YUTORI_API_KEY) -- [ ] Deployed all 17 apps +- [ ] Deployed all 18 apps - [ ] Provided invoke commands to human for manual testing - [ ] (Optional) Ran automated runtime testing and reviewed results diff --git a/cmd/create_test.go b/cmd/create_test.go index e37b861..f164966 100644 --- a/cmd/create_test.go +++ b/cmd/create_test.go @@ -439,12 +439,6 @@ func TestCreateCommand_InvalidLanguageTemplateCombinations(t *testing.T) { template: create.TemplateMagnitude, errContains: "template not found: python/magnitude", }, - { - name: "gemini-computer-use not available for python", - language: create.LanguagePython, - template: create.TemplateGeminiComputerUse, - errContains: "template not found: python/gemini-computer-use", - }, { name: "invalid language", language: "ruby", @@ -558,7 +552,6 @@ func TestCreateCommand_TemplateNotAvailableForLanguage(t *testing.T) { create.TemplateBrowserUse: {create.LanguageTypeScript}, create.TemplateStagehand: {create.LanguagePython}, create.TemplateMagnitude: {create.LanguagePython}, - create.TemplateGeminiComputerUse: {create.LanguagePython}, } for template, unavailableLanguages := range unavailableCombinations { diff --git a/pkg/create/templates.go b/pkg/create/templates.go index 957d45f..c9b07b7 100644 --- a/pkg/create/templates.go +++ b/pkg/create/templates.go @@ -63,7 +63,7 @@ var Templates = map[string]TemplateInfo{ TemplateGeminiComputerUse: { Name: "Gemini Computer Use", Description: "Implements a Gemini computer use agent", - Languages: []string{LanguageTypeScript}, + Languages: []string{LanguageTypeScript, LanguagePython}, }, TemplateBrowserUse: { Name: "Browser Use", @@ -201,7 +201,7 @@ var Commands = map[string]map[string]DeployConfig{ TemplateGeminiComputerUse: { EntryPoint: "index.ts", NeedsEnvFile: true, - InvokeCommand: "kernel invoke ts-gemini-cua gemini-cua-task", + InvokeCommand: `kernel invoke ts-gemini-cua cua-task --payload '{"query": "Navigate to http://magnitasks.com and click on Tasks in the sidebar"}'`, }, TemplateClaudeAgentSDK: { EntryPoint: "index.ts", @@ -250,6 +250,11 @@ var Commands = map[string]map[string]DeployConfig{ NeedsEnvFile: true, InvokeCommand: `kernel invoke py-claude-agent-sdk agent-task --payload '{"task": "Go to https://news.ycombinator.com and get the top 3 stories"}'`, }, + TemplateGeminiComputerUse: { + EntryPoint: "main.py", + NeedsEnvFile: true, + InvokeCommand: `kernel invoke python-gemini-cua cua-task --payload '{"query": "Navigate to http://magnitasks.com and click on Tasks in the sidebar"}'`, + }, TemplateYutoriComputerUse: { EntryPoint: "main.py", NeedsEnvFile: true, diff --git a/pkg/templates/python/gemini-computer-use/README.md b/pkg/templates/python/gemini-computer-use/README.md new file mode 100644 index 0000000..6344a2f --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/README.md @@ -0,0 +1,59 @@ +# Kernel Python Sample App - Gemini Computer Use + +This is a Kernel application that implements a prompt loop using Google's Gemini Computer Use model with Kernel's Computer Controls API. + +## Setup + +1. Get your API keys: + - **Kernel**: [dashboard.onkernel.com](https://dashboard.onkernel.com) + - **Google AI**: [aistudio.google.com/api-keys](https://aistudio.google.com/api-keys) + +2. Deploy the app: +```bash +kernel login +cp .env.example .env # Add your GOOGLE_API_KEY +kernel deploy main.py --env-file .env +``` + +## Usage + +```bash +kernel invoke python-gemini-cua cua-task --payload '{"query": "Navigate to https://example.com and describe the page"}' +``` + +## Recording Replays + +> **Note:** Replay recording is only available to Kernel users on paid plans. + +Add `"record_replay": true` to your payload to capture a video of the browser session: + +```bash +kernel invoke python-gemini-cua cua-task --payload '{"query": "Navigate to https://example.com", "record_replay": true}' +``` + +When enabled, the response will include a `replay_url` field with a link to view the recorded session. + +## Gemini Computer Use Actions + +The Gemini model can execute the following browser actions: + +| Action | Description | +|--------|-------------| +| `open_web_browser` | Returns a screenshot (browser is already running) | +| `click_at` | Click at coordinates (x, y) | +| `hover_at` | Move mouse to coordinates (x, y) | +| `type_text_at` | Click and type text at coordinates | +| `scroll_document` | Scroll the page (up/down/left/right) | +| `scroll_at` | Scroll at specific coordinates | +| `search` | Focus the browser URL bar | +| `navigate` | Navigate to a URL | +| `go_back` | Go back in browser history | +| `go_forward` | Go forward in browser history | +| `key_combination` | Press key combination (e.g., "ctrl+c") | +| `drag_and_drop` | Drag from one point to another | +| `wait_5_seconds` | Wait for 5 seconds | + +## Resources + +- [Google Gemini Computer Use Documentation](https://ai.google.dev/gemini-api/docs/computer-use) +- [Kernel Computer Controls](https://www.kernel.sh/docs/browsers/computer-controls) diff --git a/pkg/templates/python/gemini-computer-use/_gitignore b/pkg/templates/python/gemini-computer-use/_gitignore new file mode 100644 index 0000000..deac005 --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/_gitignore @@ -0,0 +1,6 @@ +.venv/ +__pycache__/ +*.pyc +.env +.env.local +uv.lock diff --git a/pkg/templates/python/gemini-computer-use/loop.py b/pkg/templates/python/gemini-computer-use/loop.py new file mode 100644 index 0000000..ada2130 --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/loop.py @@ -0,0 +1,300 @@ +""" +Gemini Computer Use sampling loop. +Based on Google's computer-use-preview reference implementation. +""" + +from datetime import datetime +from typing import Any, Dict, List + +from google import genai +from google.genai import types +from google.genai.types import ( + Content, + FunctionResponse, + GenerateContentConfig, + Part, +) +from kernel import Kernel + +from tools import ComputerTool, PREDEFINED_COMPUTER_USE_FUNCTIONS + + +# System prompt for browser-based computer use +def get_system_prompt() -> str: + """Generate system prompt with current date.""" + current_date = datetime.now().strftime("%A, %B %d, %Y") + return f"""You are a helpful assistant that can use a web browser. +You are operating a Chrome browser through computer use tools. +The browser is already open and ready for use. + +When you need to navigate to a page, use the navigate action with a full URL. +When you need to interact with elements, use click_at, type_text_at, etc. +After each action, carefully evaluate the screenshot to determine your next step. + +Current date: {current_date}.""" + +# Maximum number of recent turns to keep screenshots for (to manage context) +MAX_RECENT_TURN_WITH_SCREENSHOTS = 3 + + +async def sampling_loop( + *, + model: str, + query: str, + api_key: str, + kernel: Kernel, + session_id: str, + max_iterations: int = 50, + system_prompt_suffix: str = "", +) -> Dict[str, Any]: + """ + Run the Gemini computer use sampling loop. + + Args: + model: The Gemini model to use + query: The user's query/task + api_key: Google API key + kernel: Kernel client instance + session_id: Browser session ID + max_iterations: Maximum number of loop iterations + system_prompt_suffix: Additional system prompt text + + Returns: + Dict with 'final_response', 'iterations', and 'error' + """ + # Initialize the Gemini client + client = genai.Client(api_key=api_key) + + computer_tool = ComputerTool(kernel, session_id) + + # Initialize conversation with user query + contents: List[Content] = [ + Content( + role="user", + parts=[Part(text=query)], + ) + ] + + base_prompt = get_system_prompt() + system_prompt = ( + f"{base_prompt}\n\n{system_prompt_suffix}" + if system_prompt_suffix + else base_prompt + ) + + # Generate content config + generate_content_config = GenerateContentConfig( + temperature=1, + top_p=0.95, + top_k=40, + max_output_tokens=8192, + system_instruction=system_prompt, + tools=[ + types.Tool( + computer_use=types.ComputerUse( + environment=types.Environment.ENVIRONMENT_BROWSER, + ), + ), + ], + thinking_config=types.ThinkingConfig(include_thoughts=True), + ) + + iteration = 0 + final_response = "" + error = None + + while iteration < max_iterations: + iteration += 1 + print(f"\n=== Iteration {iteration} ===") + + try: + # Generate response from Gemini + response = client.models.generate_content( + model=model, + contents=contents, + config=generate_content_config, + ) + + if not response.candidates: + print("No candidates in response") + break + + candidate = response.candidates[0] + if not candidate.content: + print("No content in candidate") + break + + # Add assistant response to conversation + contents.append(candidate.content) + + # Extract text and function calls + reasoning = _extract_text(candidate.content) + function_calls = _extract_function_calls(candidate.content) + + # Log the response + print(f"Reasoning: {reasoning or '(none)'}") + print(f"Function calls: {len(function_calls)}") + for fc in function_calls: + print(f" - {fc.name}: {fc.args}") + + # Check finish reason + finish_reason = candidate.finish_reason + if ( + finish_reason == types.FinishReason.MALFORMED_FUNCTION_CALL + and not function_calls + ): + print("Malformed function call, retrying...") + continue + + # If no function calls, the model is done + if not function_calls: + print("Agent loop complete") + final_response = reasoning or "" + break + + # Execute function calls and collect results + function_responses: List[Part] = [] + for fc in function_calls: + args = dict(fc.args) if fc.args else {} + + # Handle safety decisions if present + if ( + "safety_decision" in args + and args["safety_decision"].get("decision") == "require_confirmation" + ): + print( + f"Safety confirmation required: {args['safety_decision'].get('explanation')}" + ) + print("Auto-acknowledging safety check") + + # Execute the action + print(f"Executing action: {fc.name}") + result = await computer_tool.execute_action(fc.name, args) + + if result.error: + print(f"Action error: {result.error}") + function_responses.append( + Part( + function_response=FunctionResponse( + name=fc.name, + response={"error": result.error}, + ) + ) + ) + else: + # Build response with screenshot - always include URL (required by Computer Use API) + response_data: Dict[str, Any] = { + "url": result.url or "about:blank", + } + + # Include screenshot for predefined functions + parts = None + if result.base64_image and _is_predefined_function(fc.name): + parts = [ + types.FunctionResponsePart( + inline_data=types.FunctionResponseBlob( + mime_type="image/png", + data=result.base64_image, + ) + ) + ] + + function_responses.append( + Part( + function_response=FunctionResponse( + name=fc.name, + response=response_data, + parts=parts, + ) + ) + ) + + # Add function responses to conversation + contents.append( + Content( + role="user", + parts=function_responses, + ) + ) + + # Manage screenshot history to avoid context overflow + _prune_old_screenshots(contents) + + except Exception as e: + error = str(e) + print(f"Error in sampling loop: {error}") + break + + if iteration >= max_iterations: + print("Max iterations reached") + + return { + "final_response": final_response, + "iterations": iteration, + "error": error, + } + + +def _extract_text(content: Content) -> str: + if not content.parts: + return "" + + texts: List[str] = [] + for part in content.parts: + if hasattr(part, "text") and part.text: + texts.append(part.text) + return " ".join(texts) + + +def _extract_function_calls(content: Content) -> List[types.FunctionCall]: + if not content.parts: + return [] + + calls: List[types.FunctionCall] = [] + for part in content.parts: + if hasattr(part, "function_call") and part.function_call: + calls.append(part.function_call) + return calls + + +def _is_predefined_function(name: str) -> bool: + return name in [a.value for a in PREDEFINED_COMPUTER_USE_FUNCTIONS] + + +def _prune_old_screenshots(contents: List[Content]) -> None: + turns_with_screenshots = 0 + + # Iterate in reverse to find recent turns with screenshots + for content in reversed(contents): + if content.role != "user" or not content.parts: + continue + + # Check if this turn has screenshots from predefined functions + has_screenshot = False + for part in content.parts: + if ( + hasattr(part, "function_response") + and part.function_response + and _is_predefined_function(part.function_response.name or "") + ): + # Check if it has parts (which contain screenshots) + if ( + hasattr(part.function_response, "parts") + and part.function_response.parts + ): + has_screenshot = True + break + + if has_screenshot: + turns_with_screenshots += 1 + + # Remove screenshots from old turns + if turns_with_screenshots > MAX_RECENT_TURN_WITH_SCREENSHOTS: + for part in content.parts: + if ( + hasattr(part, "function_response") + and part.function_response + and _is_predefined_function(part.function_response.name or "") + ): + # Remove the parts array (which contains the screenshot) + part.function_response.parts = None diff --git a/pkg/templates/python/gemini-computer-use/main.py b/pkg/templates/python/gemini-computer-use/main.py new file mode 100644 index 0000000..0c7d461 --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/main.py @@ -0,0 +1,109 @@ +import os +from typing import Optional, TypedDict + +import kernel +from loop import sampling_loop +from session import KernelBrowserSession + + +class QueryInput(TypedDict): + query: str + record_replay: Optional[bool] + + +class QueryOutput(TypedDict): + result: str + replay_url: Optional[str] + error: Optional[str] + + +api_key = os.getenv("GOOGLE_API_KEY") +if not api_key: + raise ValueError( + "GOOGLE_API_KEY is not set. " + "Set it via environment variable or deploy with: kernel deploy main.py --env-file .env" + ) + +app = kernel.App("python-gemini-cua") + + +@app.action("cua-task") +async def cua_task( + ctx: kernel.KernelContext, + payload: QueryInput, +) -> QueryOutput: + """ + Process a user query using Gemini Computer Use with Kernel's browser automation. + + Args: + ctx: Kernel context containing invocation information + payload: An object containing: + - query: The task/query string to process + - record_replay: Optional boolean to enable video replay recording + + Returns: + A dictionary containing: + - result: The result of the sampling loop as a string + - replay_url: URL to view the replay (if recording was enabled) + """ + if not payload or not payload.get("query"): + raise ValueError('Query is required. Payload must include: {"query": "your task description"}') + + record_replay = payload.get("record_replay", False) + + async with KernelBrowserSession( + stealth=True, + record_replay=record_replay, + ) as session: + print("Kernel browser live view url:", session.live_view_url) + + result = await sampling_loop( + model="gemini-2.5-computer-use-preview-10-2025", + query=payload["query"], + api_key=str(api_key), + kernel=session.kernel, + session_id=session.session_id, + ) + + final_response = result.get("final_response", "") + + return { + "result": final_response, + "replay_url": session.replay_view_url, + "error": result.get("error"), + } + + +# Run locally if executed directly (not imported as a module) +# Execute via: uv run main.py +if __name__ == "__main__": + import asyncio + + async def main(): + test_query = "Navigate to https://www.google.com and describe what you see" + + print(f"Running local test with query: {test_query}") + + async with KernelBrowserSession( + stealth=True, + record_replay=False, + ) as session: + print("Kernel browser live view url:", session.live_view_url) + + try: + result = await sampling_loop( + model="gemini-2.5-computer-use-preview-10-2025", + query=test_query, + api_key=str(api_key), + kernel=session.kernel, + session_id=session.session_id, + ) + + print("Result:", result.get("final_response", "")) + if result.get("error"): + print("Error:", result.get("error")) + except Exception as e: + print(f"Local execution failed: {e}") + raise + + asyncio.run(main()) diff --git a/pkg/templates/python/gemini-computer-use/pyproject.toml b/pkg/templates/python/gemini-computer-use/pyproject.toml new file mode 100644 index 0000000..c689f30 --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "python-gemini-cua" +version = "0.1.0" +description = "Gemini Computer Use template for Kernel" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "google-genai>=1.0.0", + "kernel>=0.5.0", +] + +[tool.uv] +dev-dependencies = [] diff --git a/pkg/templates/python/gemini-computer-use/session.py b/pkg/templates/python/gemini-computer-use/session.py new file mode 100644 index 0000000..82e856d --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/session.py @@ -0,0 +1,130 @@ +""" +Kernel Browser Session Manager. + +Provides an async context manager for managing Kernel browser lifecycle +with optional video replay recording. +""" + +import asyncio +import time +from dataclasses import dataclass, field +from typing import Optional + +from kernel import Kernel +from tools import DEFAULT_SCREEN_SIZE + + +@dataclass +class KernelBrowserSession: + stealth: bool = True + timeout_seconds: int = 300 + + # Replay recording options + record_replay: bool = False + replay_grace_period: float = 5.0 # Seconds to wait before stopping replay + + # Set after browser creation + session_id: Optional[str] = field(default=None, init=False) + live_view_url: Optional[str] = field(default=None, init=False) + replay_id: Optional[str] = field(default=None, init=False) + replay_view_url: Optional[str] = field(default=None, init=False) + _kernel: Optional[Kernel] = field(default=None, init=False) + + async def __aenter__(self) -> "KernelBrowserSession": + self._kernel = Kernel() + + # Create browser with specified settings + browser = self._kernel.browsers.create( + stealth=self.stealth, + timeout_seconds=self.timeout_seconds, + viewport={ + "width": DEFAULT_SCREEN_SIZE.width, + "height": DEFAULT_SCREEN_SIZE.height, + }, + ) + + self.session_id = browser.session_id + self.live_view_url = browser.browser_live_view_url + + print(f"Kernel browser created: {self.session_id}") + print(f"Live view URL: {self.live_view_url}") + + # Start replay recording if enabled + if self.record_replay: + try: + await self._start_replay() + except Exception as e: + print(f"Warning: Failed to start replay recording: {e}") + print("Continuing without replay recording.") + + return self + + async def _start_replay(self) -> None: + if not self._kernel or not self.session_id: + return + + print("Starting replay recording...") + replay = self._kernel.browsers.replays.start(self.session_id) + self.replay_id = replay.replay_id + print(f"Replay recording started: {self.replay_id}") + + async def _stop_and_get_replay_url(self) -> None: + if not self._kernel or not self.session_id or not self.replay_id: + return + + print("Stopping replay recording...") + self._kernel.browsers.replays.stop( + replay_id=self.replay_id, + id=self.session_id, + ) + print("Replay recording stopped. Processing video...") + + # Wait a moment for processing + await asyncio.sleep(2) + + # Poll for replay to be ready (with timeout) + max_wait = 60 # seconds + start_time = time.time() + replay_ready = False + + while time.time() - start_time < max_wait: + try: + replays = self._kernel.browsers.replays.list(self.session_id) + for replay in replays: + if replay.replay_id == self.replay_id: + self.replay_view_url = replay.replay_view_url + replay_ready = True + break + if replay_ready: + break + except Exception: + pass + await asyncio.sleep(1) + + if not replay_ready: + print("Warning: Replay may still be processing") + elif self.replay_view_url: + print(f"Replay view URL: {self.replay_view_url}") + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + if self._kernel and self.session_id: + try: + # Stop replay if recording was enabled + if self.record_replay and self.replay_id: + # Wait grace period before stopping to capture final state + if self.replay_grace_period > 0: + print(f"Waiting {self.replay_grace_period}s grace period...") + await asyncio.sleep(self.replay_grace_period) + await self._stop_and_get_replay_url() + finally: + print(f"Destroying browser session: {self.session_id}") + self._kernel.browsers.delete_by_id(self.session_id) + print("Browser session destroyed.") + + self._kernel = None + + @property + def kernel(self) -> Kernel: + if self._kernel is None: + raise RuntimeError("Session not initialized. Use async with context.") + return self._kernel diff --git a/pkg/templates/python/gemini-computer-use/tools/__init__.py b/pkg/templates/python/gemini-computer-use/tools/__init__.py new file mode 100644 index 0000000..54a6769 --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/tools/__init__.py @@ -0,0 +1,23 @@ +"""Gemini Computer Use tools package.""" + +from .computer import ComputerTool +from .types import ( + GeminiAction, + GeminiFunctionArgs, + PREDEFINED_COMPUTER_USE_FUNCTIONS, + ToolResult, + ScreenSize, + DEFAULT_SCREEN_SIZE, + COORDINATE_SCALE, +) + +__all__ = [ + "ComputerTool", + "GeminiAction", + "GeminiFunctionArgs", + "PREDEFINED_COMPUTER_USE_FUNCTIONS", + "ToolResult", + "ScreenSize", + "DEFAULT_SCREEN_SIZE", + "COORDINATE_SCALE", +] diff --git a/pkg/templates/python/gemini-computer-use/tools/computer.py b/pkg/templates/python/gemini-computer-use/tools/computer.py new file mode 100644 index 0000000..de2297d --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/tools/computer.py @@ -0,0 +1,275 @@ +""" +Gemini Computer Tool - Maps Gemini actions to Kernel Computer Controls API. +Based on Google's computer-use-preview reference implementation. +""" + +import asyncio +import base64 + +from kernel import Kernel + +from .types import ( + GeminiAction, + GeminiFunctionArgs, + PREDEFINED_COMPUTER_USE_FUNCTIONS, + DEFAULT_SCREEN_SIZE, + COORDINATE_SCALE, + ToolResult, + ScreenSize, +) + + +TYPING_DELAY_MS = 12 +SCREENSHOT_DELAY_SECS = 0.5 + + +class ComputerTool: + def __init__( + self, + kernel: Kernel, + session_id: str, + screen_size: ScreenSize = DEFAULT_SCREEN_SIZE, + ): + self.kernel = kernel + self.session_id = session_id + self.screen_size = screen_size + + def denormalize_x(self, x: int) -> int: + return int((x / COORDINATE_SCALE) * self.screen_size.width) + + def denormalize_y(self, y: int) -> int: + return int((y / COORDINATE_SCALE) * self.screen_size.height) + + async def screenshot(self) -> ToolResult: + try: + await asyncio.sleep(SCREENSHOT_DELAY_SECS) + response = self.kernel.browsers.computer.capture_screenshot(self.session_id) + screenshot_bytes = response.read() + + # Get current URL + url = "" + try: + state = self.kernel.browsers.computer.get_state(self.session_id) + url = state.url or "" + except Exception: + pass + + return ToolResult( + base64_image=base64.b64encode(screenshot_bytes).decode("utf-8"), + url=url, + ) + except Exception as e: + return ToolResult(error=f"Failed to take screenshot: {e}") + + async def execute_action( + self, action_name: str, args: GeminiFunctionArgs + ) -> ToolResult: + # Check if this is a known computer use function + if action_name not in [a.value for a in PREDEFINED_COMPUTER_USE_FUNCTIONS]: + return ToolResult(error=f"Unknown action: {action_name}") + + try: + if action_name == GeminiAction.OPEN_WEB_BROWSER: + # Browser is already open in Kernel, just return screenshot + pass + + elif action_name == GeminiAction.CLICK_AT: + if "x" not in args or "y" not in args: + return ToolResult(error="click_at requires x and y coordinates") + x = self.denormalize_x(args["x"]) + y = self.denormalize_y(args["y"]) + self.kernel.browsers.computer.click_mouse( + self.session_id, + x=x, + y=y, + button="left", + click_type="click", + num_clicks=1, + ) + + elif action_name == GeminiAction.HOVER_AT: + if "x" not in args or "y" not in args: + return ToolResult(error="hover_at requires x and y coordinates") + x = self.denormalize_x(args["x"]) + y = self.denormalize_y(args["y"]) + self.kernel.browsers.computer.move_mouse( + self.session_id, x=x, y=y + ) + + elif action_name == GeminiAction.TYPE_TEXT_AT: + if "x" not in args or "y" not in args: + return ToolResult(error="type_text_at requires x and y coordinates") + if "text" not in args: + return ToolResult(error="type_text_at requires text") + + x = self.denormalize_x(args["x"]) + y = self.denormalize_y(args["y"]) + + # Click at the location first + self.kernel.browsers.computer.click_mouse( + self.session_id, + x=x, + y=y, + button="left", + click_type="click", + num_clicks=1, + ) + + # Clear existing text if requested (default: true) + if args.get("clear_before_typing", True): + self.kernel.browsers.computer.press_key( + self.session_id, keys=["ctrl+a"] + ) + await asyncio.sleep(0.05) + + # Type the text + self.kernel.browsers.computer.type_text( + self.session_id, + text=args["text"], + delay=TYPING_DELAY_MS, + ) + + # Press enter if requested + if args.get("press_enter", False): + await asyncio.sleep(0.1) + self.kernel.browsers.computer.press_key( + self.session_id, keys=["Return"] + ) + + elif action_name == GeminiAction.SCROLL_DOCUMENT: + if "direction" not in args: + return ToolResult(error="scroll_document requires direction") + # Scroll at center of viewport + center_x = self.screen_size.width // 2 + center_y = self.screen_size.height // 2 + scroll_delta = 500 + + delta_x, delta_y = 0, 0 + direction = args["direction"] + if direction == "down": + delta_y = scroll_delta + elif direction == "up": + delta_y = -scroll_delta + elif direction == "right": + delta_x = scroll_delta + elif direction == "left": + delta_x = -scroll_delta + + self.kernel.browsers.computer.scroll( + self.session_id, + x=center_x, + y=center_y, + delta_x=delta_x, + delta_y=delta_y, + ) + + elif action_name == GeminiAction.SCROLL_AT: + if "x" not in args or "y" not in args: + return ToolResult(error="scroll_at requires x and y coordinates") + if "direction" not in args: + return ToolResult(error="scroll_at requires direction") + + x = self.denormalize_x(args["x"]) + y = self.denormalize_y(args["y"]) + + # Denormalize magnitude if provided + magnitude = args.get("magnitude", 800) + direction = args["direction"] + if direction in ("up", "down"): + magnitude = self.denormalize_y(magnitude) + else: + magnitude = self.denormalize_x(magnitude) + + delta_x, delta_y = 0, 0 + if direction == "down": + delta_y = magnitude + elif direction == "up": + delta_y = -magnitude + elif direction == "right": + delta_x = magnitude + elif direction == "left": + delta_x = -magnitude + + self.kernel.browsers.computer.scroll( + self.session_id, + x=x, + y=y, + delta_x=delta_x, + delta_y=delta_y, + ) + + elif action_name == GeminiAction.WAIT_5_SECONDS: + await asyncio.sleep(5) + + elif action_name == GeminiAction.GO_BACK: + self.kernel.browsers.computer.press_key( + self.session_id, keys=["alt+Left"] + ) + await asyncio.sleep(1) + + elif action_name == GeminiAction.GO_FORWARD: + self.kernel.browsers.computer.press_key( + self.session_id, keys=["alt+Right"] + ) + await asyncio.sleep(1) + + elif action_name == GeminiAction.SEARCH: + # Focus URL bar (Ctrl+L) - equivalent to clicking search + self.kernel.browsers.computer.press_key( + self.session_id, keys=["ctrl+l"] + ) + + elif action_name == GeminiAction.NAVIGATE: + if "url" not in args: + return ToolResult(error="navigate requires url") + # Focus URL bar and type the URL + self.kernel.browsers.computer.press_key( + self.session_id, keys=["ctrl+l"] + ) + await asyncio.sleep(0.1) + self.kernel.browsers.computer.type_text( + self.session_id, + text=args["url"], + delay=TYPING_DELAY_MS, + ) + await asyncio.sleep(0.1) + self.kernel.browsers.computer.press_key( + self.session_id, keys=["Return"] + ) + await asyncio.sleep(1.5) # Wait for navigation + + elif action_name == GeminiAction.KEY_COMBINATION: + if "keys" not in args: + return ToolResult(error="key_combination requires keys") + # Gemini sends keys as "key1+key2+key3" + self.kernel.browsers.computer.press_key( + self.session_id, keys=[args["keys"]] + ) + + elif action_name == GeminiAction.DRAG_AND_DROP: + required = ["x", "y", "destination_x", "destination_y"] + if not all(k in args for k in required): + return ToolResult( + error="drag_and_drop requires x, y, destination_x, and destination_y" + ) + + start_x = self.denormalize_x(args["x"]) + start_y = self.denormalize_y(args["y"]) + end_x = self.denormalize_x(args["destination_x"]) + end_y = self.denormalize_y(args["destination_y"]) + + self.kernel.browsers.computer.drag_mouse( + self.session_id, + path=[[start_x, start_y], [end_x, end_y]], + button="left", + ) + + else: + return ToolResult(error=f"Unhandled action: {action_name}") + + # Wait a moment for the action to complete, then take a screenshot + await asyncio.sleep(SCREENSHOT_DELAY_SECS) + return await self.screenshot() + + except Exception as e: + return ToolResult(error=f"Action failed: {e}") diff --git a/pkg/templates/python/gemini-computer-use/tools/types.py b/pkg/templates/python/gemini-computer-use/tools/types.py new file mode 100644 index 0000000..8fdd67f --- /dev/null +++ b/pkg/templates/python/gemini-computer-use/tools/types.py @@ -0,0 +1,85 @@ +""" +Type definitions for Gemini Computer Use actions. +Based on Google's computer-use-preview reference implementation. +""" + +from dataclasses import dataclass +from enum import StrEnum +from typing import Literal, Optional, TypedDict + + +class GeminiAction(StrEnum): + OPEN_WEB_BROWSER = "open_web_browser" + CLICK_AT = "click_at" + HOVER_AT = "hover_at" + TYPE_TEXT_AT = "type_text_at" + SCROLL_DOCUMENT = "scroll_document" + SCROLL_AT = "scroll_at" + WAIT_5_SECONDS = "wait_5_seconds" + GO_BACK = "go_back" + GO_FORWARD = "go_forward" + SEARCH = "search" + NAVIGATE = "navigate" + KEY_COMBINATION = "key_combination" + DRAG_AND_DROP = "drag_and_drop" + + +# Derive from enum to prevent drift when adding new actions +PREDEFINED_COMPUTER_USE_FUNCTIONS = list(GeminiAction) + + +# Scroll direction type +ScrollDirection = Literal["up", "down", "left", "right"] + + +class SafetyDecision(TypedDict, total=False): + decision: str + explanation: str + + +class GeminiFunctionArgs(TypedDict, total=False): + # click_at, hover_at, scroll_at + x: int + y: int + + # type_text_at + text: str + press_enter: bool + clear_before_typing: bool + + # scroll_document, scroll_at + direction: ScrollDirection + magnitude: int + + # navigate + url: str + + # key_combination + keys: str + + # drag_and_drop + destination_x: int + destination_y: int + + # Safety decision (may be included in any function call) + safety_decision: SafetyDecision + + +@dataclass +class ToolResult: + base64_image: Optional[str] = None + url: Optional[str] = None + error: Optional[str] = None + + +@dataclass +class ScreenSize: + width: int + height: int + + +# Default screen size (matching Kernel browser viewport) +DEFAULT_SCREEN_SIZE = ScreenSize(width=1200, height=800) + +# Gemini uses normalized coordinates (0-1000) +COORDINATE_SCALE = 1000 diff --git a/pkg/templates/typescript/gemini-computer-use/README.md b/pkg/templates/typescript/gemini-computer-use/README.md index 0775ccd..3fb7c32 100644 --- a/pkg/templates/typescript/gemini-computer-use/README.md +++ b/pkg/templates/typescript/gemini-computer-use/README.md @@ -1,68 +1,59 @@ # Kernel TypeScript Sample App - Gemini Computer Use -A Kernel application that demonstrates Computer Use Agent (CUA) capabilities using Google's Gemini 2.5 model with Stagehand for browser automation. - -## What It Does - -This app uses [Gemini 2.5's computer use model](https://blog.google/technology/google-deepmind/gemini-computer-use-model/) capabilities to autonomously navigate websites and complete tasks. The agent can interact with web pages just like a human would - clicking, typing, scrolling, and extracting information. +This is a Kernel application that implements a prompt loop using Google's Gemini Computer Use model with Kernel's Computer Controls API. ## Setup -1. **Add your API keys as environment variables:** - - `KERNEL_API_KEY` - Get from [Kernel dashboard](https://dashboard.onkernel.com/sign-in) - - `GOOGLE_API_KEY` - Get from [Google AI Studio](https://aistudio.google.com/apikey) - -## Running Locally - -Execute the script directly with tsx: +1. Get your API keys: + - **Kernel**: [dashboard.onkernel.com](https://dashboard.onkernel.com) + - **Google AI**: [aistudio.google.com/api-keys](https://aistudio.google.com/api-keys) +2. Deploy the app: ```bash -npx tsx index.ts +kernel login +cp .env.example .env # Add your GOOGLE_API_KEY +kernel deploy index.ts --env-file .env ``` -This runs the agent without a Kernel invocation context and provides the browser live view URL for debugging. +## Usage -## Deploying to Kernel +```bash +kernel invoke ts-gemini-cua cua-task --payload '{"query": "Navigate to https://example.com and describe the page"}' +``` -1. **Copy the example env file, add your API keys, and deploy:** - ```bash - cp .example.env .env - kernel deploy index.ts --env-file .env - ``` +## Recording Replays -2. **Invoke the action:** - ```bash - kernel invoke ts-gemini-cua gemini-cua-task - ``` +> **Note:** Replay recording is only available to Kernel users on paid plans. -The action creates a Kernel-managed browser and associates it with the invocation for tracking and monitoring. +Add `"record_replay": true` to your payload to capture a video of the browser session: -## Alternative Model Providers +```bash +kernel invoke ts-gemini-cua cua-task --payload '{"query": "Navigate to https://example.com", "record_replay": true}' +``` -Stagehand's CUA agent supports multiple model providers. You can switch from Gemini to OpenAI or Anthropic by changing the model configuration in `index.ts` and redeploying your Kernel app: +When enabled, the response will include a `replay_url` field with a link to view the recorded session. -**OpenAI Computer Use:** -```typescript -model: { - modelName: "openai/computer-use-preview", - apiKey: process.env.OPENAI_API_KEY -} -``` +## Gemini Computer Use Actions -**Anthropic Claude Sonnet:** -```typescript -model: { - modelName: "anthropic/claude-sonnet-4-20250514", - apiKey: process.env.ANTHROPIC_API_KEY -} -``` +The Gemini model can execute the following browser actions: -When using alternative providers, make sure to: -1. Add the corresponding API key to your environment variables -2. Update the deploy command to include the new API key (e.g., `--env OPENAI_API_KEY=XXX`) +| Action | Description | +|--------|-------------| +| `open_web_browser` | Returns a screenshot (browser is already running) | +| `click_at` | Click at coordinates (x, y) | +| `hover_at` | Move mouse to coordinates (x, y) | +| `type_text_at` | Click and type text at coordinates | +| `scroll_document` | Scroll the page (up/down/left/right) | +| `scroll_at` | Scroll at specific coordinates | +| `search` | Focus the browser URL bar | +| `navigate` | Navigate to a URL | +| `go_back` | Go back in browser history | +| `go_forward` | Go forward in browser history | +| `key_combination` | Press key combination (e.g., "ctrl+c") | +| `drag_and_drop` | Drag from one point to another | +| `wait_5_seconds` | Wait for 5 seconds | -## Documentation +## Resources -- [Kernel Documentation](https://www.kernel.sh/docs/quickstart) -- [Kernel Stagehand Guide](https://www.kernel.sh/docs/integrations/stagehand) -- [Gemini 2.5 Computer Use](https://blog.google/technology/google-deepmind/gemini-computer-use-model/) +- [Google Gemini Computer Use Documentation](https://ai.google.dev/gemini-api/docs/computer-use) +- [Kernel Computer Controls](https://www.kernel.sh/docs/browsers/computer-controls) diff --git a/pkg/templates/typescript/gemini-computer-use/index.ts b/pkg/templates/typescript/gemini-computer-use/index.ts index a2e0bdf..1b27b42 100644 --- a/pkg/templates/typescript/gemini-computer-use/index.ts +++ b/pkg/templates/typescript/gemini-computer-use/index.ts @@ -1,123 +1,106 @@ -import { Stagehand } from "@browserbasehq/stagehand"; import { Kernel, type KernelContext } from '@onkernel/sdk'; +import { samplingLoop } from './loop'; +import { KernelBrowserSession } from './session'; -const kernel = new Kernel({ - apiKey: process.env.KERNEL_API_KEY -}); +const kernel = new Kernel(); const app = kernel.app('ts-gemini-cua'); -interface CuaTaskInput { - startingUrl?: string; - instruction?: string; +interface QueryInput { + query: string; + record_replay?: boolean; } -interface SearchQueryOutput { - success: boolean; +interface QueryOutput { result: string; + replay_url?: string; error?: string; } -// API Key for LLM provider -// - GOOGLE_API_KEY: Required for Gemini 2.5 Computer Use Agent +// API Key for Gemini +// - GOOGLE_API_KEY: Required for Gemini Computer Use model // Set via environment variables or `kernel deploy --env-file .env` // See https://www.kernel.sh/docs/launch/deploy#environment-variables const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY; if (!GOOGLE_API_KEY) { - throw new Error('GOOGLE_API_KEY is not set'); + throw new Error( + 'GOOGLE_API_KEY is not set. ' + + 'Set it via environment variable or deploy with: kernel deploy index.ts --env-file .env' + ); } -async function runStagehandTask( - invocationId?: string, - startingUrl: string = "https://www.magnitasks.com/", - instruction: string = "Click the Tasks option in the left-side bar, and move the 5 items in the 'To Do' and 'In Progress' items to the 'Done' section of the Kanban board? You are done successfully when the items are moved." -): Promise { - // Executes a Computer Use Agent (CUA) task using Gemini 2.5 and Stagehand - - const browserOptions = { - stealth: true, - viewport: { - width: 1440, - height: 900, - refresh_rate: 25 - }, - ...(invocationId && { invocation_id: invocationId }) - }; - - const kernelBrowser = await kernel.browsers.create(browserOptions); - - console.log("Kernel browser live view url: ", kernelBrowser.browser_live_view_url); - - const stagehand = new Stagehand({ - env: "LOCAL", - verbose: 1, - domSettleTimeout: 30_000, - localBrowserLaunchOptions: { - cdpUrl: kernelBrowser.cdp_ws_url +app.action( + 'cua-task', + async (ctx: KernelContext, payload?: QueryInput): Promise => { + if (!payload?.query) { + throw new Error('Query is required. Payload must include: { "query": "your task description" }'); } - }); - await stagehand.init(); - - ///////////////////////////////////// - // Your Stagehand implementation here - ///////////////////////////////////// - try { - const page = stagehand.context.pages()[0]; - - const agent = stagehand.agent({ - cua: true, - model: { - modelName: "google/gemini-2.5-computer-use-preview-10-2025", - apiKey: GOOGLE_API_KEY, - }, - systemPrompt: `You are a helpful assistant that can use a web browser. - You are currently on the following page: ${page.url()}. - Do not ask follow up questions, the user will trust your judgement.`, - }); - // Navigate to the starting website - await page.goto(startingUrl); - - // Execute the instruction - const result = await agent.execute({ - instruction, - maxSteps: 20, + // Create browser session with optional replay recording + const session = new KernelBrowserSession(kernel, { + stealth: true, + recordReplay: payload.record_replay ?? false, }); - console.log("result: ", result); - - return { success: true, result: result.message }; - } catch (error) { - console.error(error); - const errorMessage = error instanceof Error ? error.message : String(error); - return { success: false, result: "", error: errorMessage }; - } finally { - console.log("Deleting browser and closing stagehand..."); - await stagehand.close(); - await kernel.browsers.deleteByID(kernelBrowser.session_id); - } -} + await session.start(); + console.log('Kernel browser live view url:', session.liveViewUrl); -// Register Kernel action handler for remote invocation -// Invoked via: kernel invoke ts-gemini-cua gemini-cua-task -app.action( - 'gemini-cua-task', - async (ctx: KernelContext, payload?: CuaTaskInput): Promise => { - return runStagehandTask( - ctx.invocation_id, - payload?.startingUrl, - payload?.instruction - ); + try { + // Run the Gemini sampling loop + const result = await samplingLoop({ + model: 'gemini-2.5-computer-use-preview-10-2025', + query: payload.query, + apiKey: GOOGLE_API_KEY, + kernel, + sessionId: session.sessionId, + }); + + // Stop session and get replay URL if recording was enabled + const sessionInfo = await session.stop(); + + return { + result: result.finalResponse, + replay_url: sessionInfo.replayViewUrl, + error: result.error, + }; + } catch (error) { + console.error('Error in sampling loop:', error); + await session.stop(); + throw error; + } }, ); // Run locally if executed directly (not imported as a module) // Execute via: npx tsx index.ts if (import.meta.url === `file://${process.argv[1]}`) { - runStagehandTask().then(result => { - console.log('Local execution result:', result); - process.exit(result.success ? 0 : 1); + const testQuery = "Navigate to https://www.google.com and describe what you see"; + + console.log('Running local test with query:', testQuery); + + const session = new KernelBrowserSession(kernel, { + stealth: true, + recordReplay: false, + }); + + session.start().then(async () => { + try { + const result = await samplingLoop({ + model: 'gemini-2.5-computer-use-preview-10-2025', + query: testQuery, + apiKey: GOOGLE_API_KEY, + kernel, + sessionId: session.sessionId, + }); + console.log('Result:', result.finalResponse); + if (result.error) { + console.error('Error:', result.error); + } + } finally { + await session.stop(); + } + process.exit(0); }).catch(error => { console.error('Local execution failed:', error); process.exit(1); diff --git a/pkg/templates/typescript/gemini-computer-use/loop.ts b/pkg/templates/typescript/gemini-computer-use/loop.ts new file mode 100644 index 0000000..284147b --- /dev/null +++ b/pkg/templates/typescript/gemini-computer-use/loop.ts @@ -0,0 +1,298 @@ +/** + * Gemini Computer Use sampling loop. + * Based on Google's computer-use-preview reference implementation. + */ + +import { + GoogleGenAI, + type Content, + type FunctionCall, + type Part, +} from '@google/genai'; +import type { Kernel } from '@onkernel/sdk'; +import { ComputerTool } from './tools/computer'; +import { PREDEFINED_COMPUTER_USE_FUNCTIONS, type GeminiFunctionArgs } from './tools/types/gemini'; + +// System prompt for browser-based computer use +function getSystemPrompt(): string { + const currentDate = new Date().toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + }); + + return `You are a helpful assistant that can use a web browser. +You are operating a Chrome browser through computer use tools. +The browser is already open and ready for use. + +When you need to navigate to a page, use the navigate action with a full URL. +When you need to interact with elements, use click_at, type_text_at, etc. +After each action, carefully evaluate the screenshot to determine your next step. + +Current date: ${currentDate}.`; +} + +// Maximum number of recent turns to keep screenshots for (to manage context) +const MAX_RECENT_TURN_WITH_SCREENSHOTS = 3; + +interface SamplingLoopOptions { + model: string; + query: string; + apiKey: string; + kernel: Kernel; + sessionId: string; + maxIterations?: number; + systemPromptSuffix?: string; +} + +interface SamplingLoopResult { + finalResponse: string; + iterations: number; + error?: string; +} + +/** + * Run the Gemini computer use sampling loop. + */ +export async function samplingLoop({ + model, + query, + apiKey, + kernel, + sessionId, + maxIterations = 50, + systemPromptSuffix = '', +}: SamplingLoopOptions): Promise { + const ai = new GoogleGenAI({ apiKey }); + + const computerTool = new ComputerTool(kernel, sessionId); + + // Initialize conversation with user query + const contents: Content[] = [ + { + role: 'user', + parts: [{ text: query }], + }, + ]; + + const basePrompt = getSystemPrompt(); + const systemPrompt = systemPromptSuffix + ? `${basePrompt}\n\n${systemPromptSuffix}` + : basePrompt; + + let iteration = 0; + let finalResponse = ''; + let error: string | undefined; + + while (iteration < maxIterations) { + iteration++; + console.log(`\n=== Iteration ${iteration} ===`); + + try { + // Generate response from Gemini + const response = await ai.models.generateContent({ + model, + contents, + config: { + temperature: 1, + topP: 0.95, + topK: 40, + maxOutputTokens: 8192, + systemInstruction: systemPrompt, + tools: [ + { + computerUse: { + environment: 'ENVIRONMENT_BROWSER', + }, + }, + ], + thinkingConfig: { + includeThoughts: true, + }, + }, + }); + + if (!response.candidates || response.candidates.length === 0) { + console.log('No candidates in response'); + break; + } + + const candidate = response.candidates[0]; + if (!candidate.content) { + console.log('No content in candidate'); + break; + } + + // Add assistant response to conversation + contents.push(candidate.content); + + // Extract text and function calls + const reasoning = extractText(candidate.content); + const functionCalls = extractFunctionCalls(candidate.content); + + // Log the response + console.log('Reasoning:', reasoning || '(none)'); + console.log('Function calls:', functionCalls.length); + for (const fc of functionCalls) { + console.log(` - ${fc.name}:`, fc.args); + } + + // Check finish reason + const finishReason = candidate.finishReason; + if (finishReason === 'MALFORMED_FUNCTION_CALL' && !functionCalls.length) { + console.log('Malformed function call, retrying...'); + continue; + } + + // If no function calls, the model is done + if (functionCalls.length === 0) { + console.log('Agent loop complete'); + finalResponse = reasoning || ''; + break; + } + + // Execute function calls and collect results + const functionResponses: Part[] = []; + for (const fc of functionCalls) { + const args = fc.args as GeminiFunctionArgs || {}; + + // Handle safety decisions if present + if (args.safety_decision?.decision === 'require_confirmation') { + console.log('Safety confirmation required:', args.safety_decision.explanation); + // Auto-acknowledge for automated execution + console.log('Auto-acknowledging safety check'); + } + + // Execute the action + console.log(`Executing action: ${fc.name}`); + const result = await computerTool.executeAction(fc.name, args); + + if (result.error) { + console.log(`Action error: ${result.error}`); + functionResponses.push({ + functionResponse: { + name: fc.name, + response: { error: result.error }, + }, + }); + } else { + // Build response with screenshot - always include URL (required by Computer Use API) + const responseData: Record = { + url: result.url || 'about:blank', + }; + + functionResponses.push({ + functionResponse: { + name: fc.name, + response: responseData, + // Include screenshot as inline data + ...(result.base64Image && isPredefinedFunction(fc.name) ? { + parts: [{ + inlineData: { + mimeType: 'image/png', + data: result.base64Image, + }, + }], + } : {}), + }, + }); + } + } + + // Add function responses to conversation + contents.push({ + role: 'user', + parts: functionResponses, + }); + + // Manage screenshot history to avoid context overflow + pruneOldScreenshots(contents); + + } catch (err) { + error = err instanceof Error ? err.message : String(err); + console.error('Error in sampling loop:', error); + break; + } + } + + if (iteration >= maxIterations) { + console.log('Max iterations reached'); + } + + return { + finalResponse, + iterations: iteration, + error, + }; +} + +function extractText(content: Content): string { + if (!content.parts) return ''; + + const texts: string[] = []; + for (const part of content.parts) { + if ('text' in part && part.text) { + texts.push(part.text); + } + } + return texts.join(' '); +} + +function extractFunctionCalls(content: Content): FunctionCall[] { + if (!content.parts) return []; + + const calls: FunctionCall[] = []; + for (const part of content.parts) { + if ('functionCall' in part && part.functionCall) { + calls.push(part.functionCall); + } + } + return calls; +} + +function isPredefinedFunction(name: string): boolean { + return PREDEFINED_COMPUTER_USE_FUNCTIONS.includes(name as typeof PREDEFINED_COMPUTER_USE_FUNCTIONS[number]); +} + +function pruneOldScreenshots(contents: Content[]): void { + let turnsWithScreenshots = 0; + + // Iterate in reverse to find recent turns with screenshots + for (let i = contents.length - 1; i >= 0; i--) { + const content = contents[i]; + if (content.role !== 'user' || !content.parts) continue; + + // Check if this turn has screenshots from predefined functions + let hasScreenshot = false; + for (const part of content.parts) { + if ('functionResponse' in part && + part.functionResponse && + isPredefinedFunction(part.functionResponse.name || '')) { + // Check if it has inline data (screenshot) + const fr = part.functionResponse as { parts?: Array<{ inlineData?: unknown }> }; + if (fr.parts?.some(p => p.inlineData)) { + hasScreenshot = true; + break; + } + } + } + + if (hasScreenshot) { + turnsWithScreenshots++; + + // Remove screenshots from old turns + if (turnsWithScreenshots > MAX_RECENT_TURN_WITH_SCREENSHOTS) { + for (const part of content.parts) { + if ('functionResponse' in part && + part.functionResponse && + isPredefinedFunction(part.functionResponse.name || '')) { + // Remove the parts array (which contains the screenshot) + const fr = part.functionResponse as { parts?: unknown }; + delete fr.parts; + } + } + } + } + } +} diff --git a/pkg/templates/typescript/gemini-computer-use/package.json b/pkg/templates/typescript/gemini-computer-use/package.json index dd74931..8ef6ebf 100644 --- a/pkg/templates/typescript/gemini-computer-use/package.json +++ b/pkg/templates/typescript/gemini-computer-use/package.json @@ -4,9 +4,8 @@ "type": "module", "private": true, "dependencies": { - "@browserbasehq/stagehand": "^3.0.6", - "@onkernel/sdk": "^0.23.0", - "zod": "^4.2.0" + "@google/genai": "^1.0.0", + "@onkernel/sdk": "^0.23.0" }, "devDependencies": { "@types/node": "^22.15.17", diff --git a/pkg/templates/typescript/gemini-computer-use/pnpm-lock.yaml b/pkg/templates/typescript/gemini-computer-use/pnpm-lock.yaml deleted file mode 100644 index b1e3100..0000000 --- a/pkg/templates/typescript/gemini-computer-use/pnpm-lock.yaml +++ /dev/null @@ -1,2971 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@browserbasehq/stagehand': - specifier: ^3.0.6 - version: 3.0.6(@cfworker/json-schema@4.1.1)(@opentelemetry/api@1.9.0)(deepmerge@4.3.1)(dotenv@16.6.1)(hono@4.11.1)(zod@4.2.0) - '@onkernel/sdk': - specifier: ^0.23.0 - version: 0.23.0 - zod: - specifier: ^4.2.0 - version: 4.2.0 - devDependencies: - '@types/node': - specifier: ^22.15.17 - version: 22.19.3 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - -packages: - - '@ai-sdk/anthropic@2.0.56': - resolution: {integrity: sha512-XHJKu0Yvfu9SPzRfsAFESa+9T7f2YJY6TxykKMfRsAwpeWAiX/Gbx5J5uM15AzYC3Rw8tVP3oH+j7jEivENirQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/azure@2.0.88': - resolution: {integrity: sha512-OMAXXZV7GiFz8qpCpzhaesTfiuiXU92WZWdvtr+K8rjfTNGm9sJWUuSLZ29z5aAeLUSRlwDMUlK4lYr8/1IewQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/cerebras@1.0.33': - resolution: {integrity: sha512-2gSSS/7kunIwMdC4td5oWsUAzoLw84ccGpz6wQbxVnrb1iWnrEnKa5tRBduaP6IXpzLWsu8wME3+dQhZy+gT7w==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/deepseek@1.0.32': - resolution: {integrity: sha512-DDNZSZn6OuExVBJBAWdk3VeyQPH+pYwSykixePhzll9EnT3aakapMYr5gjw3wMl+eZ0tLplythHL1TfIehUZ0g==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/gateway@2.0.21': - resolution: {integrity: sha512-BwV7DU/lAm3Xn6iyyvZdWgVxgLu3SNXzl5y57gMvkW4nGhAOV5269IrJzQwGt03bb107sa6H6uJwWxc77zXoGA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/google-vertex@3.0.91': - resolution: {integrity: sha512-SonFMMdSIlos0fjBFBff7rcZQx+q3WP4CpXdz7+YEIEWItnR/k9f5MqRCXMZilfyzcpz5wFxa7Sqlnapv3oqsA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/google@2.0.46': - resolution: {integrity: sha512-8PK6u4sGE/kXebd7ZkTp+0aya4kNqzoqpS5m7cHY2NfTK6fhPc6GNvE+MZIZIoHQTp5ed86wGBdeBPpFaaUtyg==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/groq@2.0.33': - resolution: {integrity: sha512-FWGl7xNr88NBveao3y9EcVWYUt9ABPrwLFY7pIutSNgaTf32vgvyhREobaMrLU4Scr5G/2tlNqOPZ5wkYMaZig==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/mistral@2.0.26': - resolution: {integrity: sha512-jxDB++4WI1wEx5ONNBI+VbkmYJOYIuS8UQY13/83UGRaiW7oB/WHiH4ETe6KzbKpQPB3XruwTJQjUMsMfKyTXA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/openai-compatible@1.0.29': - resolution: {integrity: sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/openai@2.0.86': - resolution: {integrity: sha512-obsLIOyA93lbQiSt1rvBItoVQp1U2RDPs0bNG0JYhm6Gku8Dg/0Cm8e4NUWT5p5PN10/doKSb3SMSKCixwIAKA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/perplexity@2.0.22': - resolution: {integrity: sha512-zwzcnk08R2J3mZcQPn4Ifl4wYGrvANR7jsBB0hCTUSbb+Rx3ybpikSWiGuXQXxdiRc1I5MWXgj70m+bZaLPvHw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider-utils@3.0.19': - resolution: {integrity: sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider@2.0.0': - resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} - engines: {node: '>=18'} - - '@ai-sdk/togetherai@1.0.30': - resolution: {integrity: sha512-9bxQbIXnWSN4bNismrza3NvIo+ui/Y3pj3UN6e9vCszCWFCN45RgISi4oDe10RqmzaJ/X8cfO/Tem+K8MT3wGQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/xai@2.0.41': - resolution: {integrity: sha512-mztE/1svgeBscJBEcy/AqVZSxTXasnN22o58Vsf7rX7f1X9gqadVoDHH4a/caiklADg/HNu2h1u+vTgVYL8XbA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@anthropic-ai/sdk@0.39.0': - resolution: {integrity: sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg==} - - '@browserbasehq/sdk@2.6.0': - resolution: {integrity: sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==} - - '@browserbasehq/stagehand@3.0.6': - resolution: {integrity: sha512-WN/GuJMHXyXNsQTErmeTEstxUe0gZf9DLXlPkfJtVUuqErIJgZPiBstlTrdgx7I8n1ZE+q8gaEU/beJsW30+bg==} - peerDependencies: - deepmerge: ^4.3.1 - dotenv: ^16.4.5 - zod: ^3.25.76 || ^4.1.8 - - '@cfworker/json-schema@4.1.1': - resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} - - '@google/genai@1.33.0': - resolution: {integrity: sha512-ThUjFZ1N0DU88peFjnQkb8K198EWaW2RmmnDShFQ+O+xkIH9itjpRe358x3L/b4X/A7dimkvq63oz49Vbh7Cog==} - engines: {node: '>=20.0.0'} - peerDependencies: - '@modelcontextprotocol/sdk': ^1.24.0 - peerDependenciesMeta: - '@modelcontextprotocol/sdk': - optional: true - - '@hono/node-server@1.19.7': - resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@langchain/core@0.3.79': - resolution: {integrity: sha512-ZLAs5YMM5N2UXN3kExMglltJrKKoW7hs3KMZFlXUnD7a5DFKBYxPFMeXA4rT+uvTxuJRZPCYX0JKI5BhyAWx4A==} - engines: {node: '>=18'} - - '@langchain/openai@0.4.9': - resolution: {integrity: sha512-NAsaionRHNdqaMjVLPkFCyjUDze+OqRHghA1Cn4fPoAafz+FXcl9c7LlEl9Xo0FH6/8yiCl7Rw2t780C/SBVxQ==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/core': '>=0.3.39 <0.4.0' - - '@modelcontextprotocol/sdk@1.25.0': - resolution: {integrity: sha512-z0Zhn/LmQ3yz91dEfd5QgS7DpSjA4pk+3z2++zKgn5L6iDFM9QapsVoAQSbKLvlrFsZk9+ru6yHHWNq2lCYJKQ==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true - - '@onkernel/sdk@0.23.0': - resolution: {integrity: sha512-P/ez6HU8sO2QvqWATkvC+Wdv+fgto4KfBCHLl2T6EUpoU3LhgOZ/sJP2ZRf/vh5Vh7QR2Vf05RgMaFcIGBGD9Q==} - - '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - - '@pinojs/redact@0.4.0': - resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@puppeteer/browsers@2.3.0': - resolution: {integrity: sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==} - engines: {node: '>=18'} - hasBin: true - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - - '@types/node-fetch@2.6.13': - resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} - - '@types/node@18.19.130': - resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - - '@types/node@22.19.3': - resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} - - '@types/retry@0.12.0': - resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - - '@types/uuid@10.0.0': - resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - - '@vercel/oidc@3.0.5': - resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} - engines: {node: '>= 20'} - - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - - agentkeepalive@4.6.0: - resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} - engines: {node: '>= 8.0.0'} - - ai@5.0.113: - resolution: {integrity: sha512-26vivpSO/mzZj0k1Si2IpsFspp26ttQICHRySQiMrtWcRd5mnJMX2a8sG28vmZ38C+JUn1cWmfZrsLMxkSMw9g==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - b4a@1.7.3: - resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==} - peerDependencies: - react-native-b4a: '*' - peerDependenciesMeta: - react-native-b4a: - optional: true - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - bare-events@2.8.2: - resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} - peerDependencies: - bare-abort-controller: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - - bare-fs@4.5.2: - resolution: {integrity: sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==} - engines: {bare: '>=1.16.0'} - peerDependencies: - bare-buffer: '*' - peerDependenciesMeta: - bare-buffer: - optional: true - - bare-os@3.6.2: - resolution: {integrity: sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==} - engines: {bare: '>=1.14.0'} - - bare-path@3.0.0: - resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - - bare-stream@2.7.0: - resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==} - peerDependencies: - bare-buffer: '*' - bare-events: '*' - peerDependenciesMeta: - bare-buffer: - optional: true - bare-events: - optional: true - - bare-url@2.3.2: - resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} - engines: {node: '>=10.0.0'} - - bignumber.js@9.3.1: - resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - - body-parser@2.2.1: - resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} - engines: {node: '>=18'} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - bufferutil@4.0.9: - resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} - engines: {node: '>=6.14.2'} - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chrome-launcher@1.2.1: - resolution: {integrity: sha512-qmFR5PLMzHyuNJHwOloHPAHhbaNglkfeV/xDtt5b7xiFFyU1I+AZZX0PYseMuhenJSSirgxELYIbswcoc+5H4A==} - engines: {node: '>=12.13.0'} - hasBin: true - - chromium-bidi@0.6.3: - resolution: {integrity: sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A==} - peerDependencies: - devtools-protocol: '*' - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - console-table-printer@2.15.0: - resolution: {integrity: sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==} - - content-disposition@1.0.1: - resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} - engines: {node: '>=18'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - - data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} - - dateformat@4.6.3: - resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - devtools-protocol@0.0.1312386: - resolution: {integrity: sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==} - - devtools-protocol@0.0.1464554: - resolution: {integrity: sha512-CAoP3lYfwAGQTaAXYvA6JZR0fjGUb7qec1qf4mToyoH2TZgUFeIqYcjh6f9jNuhHfuZiEdH+PONHYrLhRQX6aw==} - - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - events-universal@1.0.1: - resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} - - eventsource-parser@3.0.6: - resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} - engines: {node: '>=18.0.0'} - - eventsource@3.0.7: - resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} - engines: {node: '>=18.0.0'} - - express-rate-limit@7.5.1: - resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} - engines: {node: '>= 16'} - peerDependencies: - express: '>= 4.11' - - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - - fast-copy@4.0.1: - resolution: {integrity: sha512-+uUOQlhsaswsizHFmEFAQhB3lSiQ+lisxl50N6ZP0wywlZeWsIESxSi9ftPEps8UGfiBzyYP7x27zA674WUvXw==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - - fetch-cookie@3.2.0: - resolution: {integrity: sha512-n61pQIxP25C6DRhcJxn7BDzgHP/+S56Urowb5WFxtcRMpU6drqXD90xjyAsVQYsNSNNVbaCcYY1DuHsdkZLuiA==} - - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - - form-data-encoder@1.7.2: - resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} - - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - formdata-node@4.4.1: - resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} - engines: {node: '>= 12.20'} - - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - gaxios@7.1.3: - resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==} - engines: {node: '>=18'} - - gcp-metadata@8.1.2: - resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} - engines: {node: '>=18'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - - get-uri@6.0.5: - resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} - engines: {node: '>= 14'} - - glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - hasBin: true - - google-auth-library@10.5.0: - resolution: {integrity: sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==} - engines: {node: '>=18'} - - google-logging-utils@1.1.3: - resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} - engines: {node: '>=14'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - gtoken@8.0.0: - resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==} - engines: {node: '>=18'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - - hono@4.11.1: - resolution: {integrity: sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg==} - engines: {node: '>=16.9.0'} - - http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} - - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - - humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - - is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} - - joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} - - js-tiktoken@1.0.21: - resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==} - - json-bigint@1.0.0: - resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - - langsmith@0.3.87: - resolution: {integrity: sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q==} - peerDependencies: - '@opentelemetry/api': '*' - '@opentelemetry/exporter-trace-otlp-proto': '*' - '@opentelemetry/sdk-trace-base': '*' - openai: '*' - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@opentelemetry/exporter-trace-otlp-proto': - optional: true - '@opentelemetry/sdk-trace-base': - optional: true - openai: - optional: true - - lighthouse-logger@2.0.2: - resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - - marky@1.3.0: - resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - mustache@4.2.0: - resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} - hasBin: true - - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - - netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} - hasBin: true - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - ollama-ai-provider-v2@1.5.5: - resolution: {integrity: sha512-1YwTFdPjhPNHny/DrOHO+s8oVGGIE5Jib61/KnnjPRNWQhVVimrJJdaAX3e6nNRRDXrY5zbb9cfm2+yVvgsrqw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^4.0.16 - - on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - openai@4.104.0: - resolution: {integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==} - hasBin: true - peerDependencies: - ws: ^8.18.0 - zod: ^3.23.8 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-queue@6.6.2: - resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} - engines: {node: '>=8'} - - p-retry@4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} - - p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - - pac-proxy-agent@7.2.0: - resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} - engines: {node: '>= 14'} - - pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - patchright-core@1.57.0: - resolution: {integrity: sha512-um/9Wue7IFAa9UDLacjNgDn62ub5GJe1b1qouvYpELIF9rsFVMNhRo/rRXYajupLwp5xKJ0sSjOV6sw8/HarBQ==} - engines: {node: '>=18'} - hasBin: true - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - - pino-abstract-transport@2.0.0: - resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} - - pino-abstract-transport@3.0.0: - resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} - - pino-pretty@13.1.3: - resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} - hasBin: true - - pino-std-serializers@7.0.0: - resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - - pino@9.14.0: - resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} - hasBin: true - - pkce-challenge@5.0.1: - resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} - engines: {node: '>=16.20.0'} - - playwright-core@1.57.0: - resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} - engines: {node: '>=18'} - hasBin: true - - playwright@1.57.0: - resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} - engines: {node: '>=18'} - hasBin: true - - process-warning@5.0.0: - resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} - - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - proxy-agent@6.5.0: - resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} - engines: {node: '>= 14'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - - puppeteer-core@22.15.0: - resolution: {integrity: sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==} - engines: {node: '>=18'} - - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - - rimraf@5.0.10: - resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} - hasBin: true - - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - secure-json-parse@4.1.0: - resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} - - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} - - set-cookie-parser@2.7.2: - resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-wcswidth@1.1.2: - resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - socks-proxy-agent@8.0.5: - resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} - engines: {node: '>= 14'} - - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - - sonic-boom@4.2.0: - resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - - streamx@2.23.0: - resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - - strip-json-comments@5.0.3: - resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} - engines: {node: '>=14.16'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - tar-fs@3.1.1: - resolution: {integrity: sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==} - - tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - - text-decoder@1.2.3: - resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} - - thread-stream@3.1.0: - resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - - tldts-core@7.0.19: - resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} - - tldts@7.0.19: - resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} - hasBin: true - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - tough-cookie@6.0.0: - resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} - engines: {node: '>=16'} - - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - urlpattern-polyfill@10.0.0: - resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} - - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} - hasBin: true - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - - web-streams-polyfill@4.0.0-beta.3: - resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} - engines: {node: '>= 14'} - - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - - zod-to-json-schema@3.25.0: - resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} - peerDependencies: - zod: ^3.25 || ^4 - - zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - - zod@4.2.0: - resolution: {integrity: sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==} - -snapshots: - - '@ai-sdk/anthropic@2.0.56(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/azure@2.0.88(zod@4.2.0)': - dependencies: - '@ai-sdk/openai': 2.0.86(zod@4.2.0) - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/cerebras@1.0.33(zod@4.2.0)': - dependencies: - '@ai-sdk/openai-compatible': 1.0.29(zod@4.2.0) - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/deepseek@1.0.32(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/gateway@2.0.21(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - '@vercel/oidc': 3.0.5 - zod: 4.2.0 - - '@ai-sdk/google-vertex@3.0.91(zod@4.2.0)': - dependencies: - '@ai-sdk/anthropic': 2.0.56(zod@4.2.0) - '@ai-sdk/google': 2.0.46(zod@4.2.0) - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - google-auth-library: 10.5.0 - zod: 4.2.0 - transitivePeerDependencies: - - supports-color - optional: true - - '@ai-sdk/google@2.0.46(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/groq@2.0.33(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/mistral@2.0.26(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/openai-compatible@1.0.29(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/openai@2.0.86(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/perplexity@2.0.22(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/provider-utils@3.0.19(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.0.6 - zod: 4.2.0 - - '@ai-sdk/provider@2.0.0': - dependencies: - json-schema: 0.4.0 - - '@ai-sdk/togetherai@1.0.30(zod@4.2.0)': - dependencies: - '@ai-sdk/openai-compatible': 1.0.29(zod@4.2.0) - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@ai-sdk/xai@2.0.41(zod@4.2.0)': - dependencies: - '@ai-sdk/openai-compatible': 1.0.29(zod@4.2.0) - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - '@anthropic-ai/sdk@0.39.0': - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.13 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - '@browserbasehq/sdk@2.6.0': - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.13 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - '@browserbasehq/stagehand@3.0.6(@cfworker/json-schema@4.1.1)(@opentelemetry/api@1.9.0)(deepmerge@4.3.1)(dotenv@16.6.1)(hono@4.11.1)(zod@4.2.0)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@anthropic-ai/sdk': 0.39.0 - '@browserbasehq/sdk': 2.6.0 - '@google/genai': 1.33.0(@modelcontextprotocol/sdk@1.25.0(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@4.2.0))(bufferutil@4.0.9) - '@langchain/openai': 0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0)))(ws@8.18.3(bufferutil@4.0.9)) - '@modelcontextprotocol/sdk': 1.25.0(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@4.2.0) - ai: 5.0.113(zod@4.2.0) - deepmerge: 4.3.1 - devtools-protocol: 0.0.1464554 - dotenv: 16.6.1 - fetch-cookie: 3.2.0 - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0) - pino: 9.14.0 - pino-pretty: 13.1.3 - playwright: 1.57.0 - uuid: 11.1.0 - ws: 8.18.3(bufferutil@4.0.9) - zod: 4.2.0 - zod-to-json-schema: 3.25.0(zod@4.2.0) - optionalDependencies: - '@ai-sdk/anthropic': 2.0.56(zod@4.2.0) - '@ai-sdk/azure': 2.0.88(zod@4.2.0) - '@ai-sdk/cerebras': 1.0.33(zod@4.2.0) - '@ai-sdk/deepseek': 1.0.32(zod@4.2.0) - '@ai-sdk/google': 2.0.46(zod@4.2.0) - '@ai-sdk/google-vertex': 3.0.91(zod@4.2.0) - '@ai-sdk/groq': 2.0.33(zod@4.2.0) - '@ai-sdk/mistral': 2.0.26(zod@4.2.0) - '@ai-sdk/openai': 2.0.86(zod@4.2.0) - '@ai-sdk/perplexity': 2.0.22(zod@4.2.0) - '@ai-sdk/togetherai': 1.0.30(zod@4.2.0) - '@ai-sdk/xai': 2.0.41(zod@4.2.0) - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0)) - bufferutil: 4.0.9 - chrome-launcher: 1.2.1 - ollama-ai-provider-v2: 1.5.5(zod@4.2.0) - patchright-core: 1.57.0 - playwright-core: 1.57.0 - puppeteer-core: 22.15.0(bufferutil@4.0.9) - transitivePeerDependencies: - - '@cfworker/json-schema' - - '@opentelemetry/api' - - '@opentelemetry/exporter-trace-otlp-proto' - - '@opentelemetry/sdk-trace-base' - - bare-abort-controller - - bare-buffer - - encoding - - hono - - react-native-b4a - - supports-color - - utf-8-validate - - '@cfworker/json-schema@4.1.1': {} - - '@google/genai@1.33.0(@modelcontextprotocol/sdk@1.25.0(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@4.2.0))(bufferutil@4.0.9)': - dependencies: - google-auth-library: 10.5.0 - ws: 8.18.3(bufferutil@4.0.9) - optionalDependencies: - '@modelcontextprotocol/sdk': 1.25.0(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@4.2.0) - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@hono/node-server@1.19.7(hono@4.11.1)': - dependencies: - hono: 4.11.1 - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.2 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0))': - dependencies: - '@cfworker/json-schema': 4.1.1 - ansi-styles: 5.2.0 - camelcase: 6.3.0 - decamelize: 1.2.0 - js-tiktoken: 1.0.21 - langsmith: 0.3.87(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0)) - mustache: 4.2.0 - p-queue: 6.6.2 - p-retry: 4.6.2 - uuid: 10.0.0 - zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) - transitivePeerDependencies: - - '@opentelemetry/api' - - '@opentelemetry/exporter-trace-otlp-proto' - - '@opentelemetry/sdk-trace-base' - - openai - - '@langchain/openai@0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0)))(ws@8.18.3(bufferutil@4.0.9))': - dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0)) - js-tiktoken: 1.0.21 - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76) - zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) - transitivePeerDependencies: - - encoding - - ws - - '@modelcontextprotocol/sdk@1.25.0(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@4.2.0)': - dependencies: - '@hono/node-server': 1.19.7(hono@4.11.1) - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) - content-type: 1.0.5 - cors: 2.8.5 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.0.6 - express: 5.2.1 - express-rate-limit: 7.5.1(express@5.2.1) - jose: 6.1.3 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.1 - raw-body: 3.0.2 - zod: 4.2.0 - zod-to-json-schema: 3.25.0(zod@4.2.0) - optionalDependencies: - '@cfworker/json-schema': 4.1.1 - transitivePeerDependencies: - - hono - - supports-color - - '@onkernel/sdk@0.23.0': {} - - '@opentelemetry/api@1.9.0': {} - - '@pinojs/redact@0.4.0': {} - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@puppeteer/browsers@2.3.0': - dependencies: - debug: 4.4.3 - extract-zip: 2.0.1 - progress: 2.0.3 - proxy-agent: 6.5.0 - semver: 7.7.3 - tar-fs: 3.1.1 - unbzip2-stream: 1.4.3 - yargs: 17.7.2 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color - optional: true - - '@standard-schema/spec@1.1.0': {} - - '@tootallnate/quickjs-emscripten@0.23.0': - optional: true - - '@types/node-fetch@2.6.13': - dependencies: - '@types/node': 22.19.3 - form-data: 4.0.5 - - '@types/node@18.19.130': - dependencies: - undici-types: 5.26.5 - - '@types/node@22.19.3': - dependencies: - undici-types: 6.21.0 - - '@types/retry@0.12.0': {} - - '@types/uuid@10.0.0': {} - - '@types/yauzl@2.10.3': - dependencies: - '@types/node': 22.19.3 - optional: true - - '@vercel/oidc@3.0.5': {} - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.0.0 - - agent-base@7.1.4: {} - - agentkeepalive@4.6.0: - dependencies: - humanize-ms: 1.2.1 - - ai@5.0.113(zod@4.2.0): - dependencies: - '@ai-sdk/gateway': 2.0.21(zod@4.2.0) - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - '@opentelemetry/api': 1.9.0 - zod: 4.2.0 - - ajv-formats@3.0.1(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - ansi-styles@6.2.3: {} - - ast-types@0.13.4: - dependencies: - tslib: 2.8.1 - optional: true - - asynckit@0.4.0: {} - - atomic-sleep@1.0.0: {} - - b4a@1.7.3: - optional: true - - balanced-match@1.0.2: {} - - bare-events@2.8.2: - optional: true - - bare-fs@4.5.2: - dependencies: - bare-events: 2.8.2 - bare-path: 3.0.0 - bare-stream: 2.7.0(bare-events@2.8.2) - bare-url: 2.3.2 - fast-fifo: 1.3.2 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - optional: true - - bare-os@3.6.2: - optional: true - - bare-path@3.0.0: - dependencies: - bare-os: 3.6.2 - optional: true - - bare-stream@2.7.0(bare-events@2.8.2): - dependencies: - streamx: 2.23.0 - optionalDependencies: - bare-events: 2.8.2 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - optional: true - - bare-url@2.3.2: - dependencies: - bare-path: 3.0.0 - optional: true - - base64-js@1.5.1: {} - - basic-ftp@5.0.5: - optional: true - - bignumber.js@9.3.1: {} - - body-parser@2.2.1: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.1 - on-finished: 2.4.1 - qs: 6.14.0 - raw-body: 3.0.2 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - buffer-crc32@0.2.13: - optional: true - - buffer-equal-constant-time@1.0.1: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - optional: true - - bufferutil@4.0.9: - dependencies: - node-gyp-build: 4.8.4 - optional: true - - bytes@3.1.2: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - camelcase@6.3.0: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chrome-launcher@1.2.1: - dependencies: - '@types/node': 22.19.3 - escape-string-regexp: 4.0.0 - is-wsl: 2.2.0 - lighthouse-logger: 2.0.2 - transitivePeerDependencies: - - supports-color - optional: true - - chromium-bidi@0.6.3(devtools-protocol@0.0.1312386): - dependencies: - devtools-protocol: 0.0.1312386 - mitt: 3.0.1 - urlpattern-polyfill: 10.0.0 - zod: 3.23.8 - optional: true - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - optional: true - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - colorette@2.0.20: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - console-table-printer@2.15.0: - dependencies: - simple-wcswidth: 1.1.2 - - content-disposition@1.0.1: {} - - content-type@1.0.5: {} - - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - - cors@2.8.5: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - data-uri-to-buffer@4.0.1: {} - - data-uri-to-buffer@6.0.2: - optional: true - - dateformat@4.6.3: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decamelize@1.2.0: {} - - deepmerge@4.3.1: {} - - degenerator@5.0.1: - dependencies: - ast-types: 0.13.4 - escodegen: 2.1.0 - esprima: 4.0.1 - optional: true - - delayed-stream@1.0.0: {} - - depd@2.0.0: {} - - devtools-protocol@0.0.1312386: - optional: true - - devtools-protocol@0.0.1464554: {} - - dotenv@16.6.1: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - eastasianwidth@0.2.0: {} - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - - ee-first@1.1.1: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - encodeurl@2.0.0: {} - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - escalade@3.2.0: - optional: true - - escape-html@1.0.3: {} - - escape-string-regexp@4.0.0: - optional: true - - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - optional: true - - esprima@4.0.1: - optional: true - - estraverse@5.3.0: - optional: true - - esutils@2.0.3: - optional: true - - etag@1.8.1: {} - - event-target-shim@5.0.1: {} - - eventemitter3@4.0.7: {} - - events-universal@1.0.1: - dependencies: - bare-events: 2.8.2 - transitivePeerDependencies: - - bare-abort-controller - optional: true - - eventsource-parser@3.0.6: {} - - eventsource@3.0.7: - dependencies: - eventsource-parser: 3.0.6 - - express-rate-limit@7.5.1(express@5.2.1): - dependencies: - express: 5.2.1 - - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.2.1 - content-disposition: 1.0.1 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1 - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.14.0 - range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - extend@3.0.2: {} - - extract-zip@2.0.1: - dependencies: - debug: 4.4.3 - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - optional: true - - fast-copy@4.0.1: {} - - fast-deep-equal@3.1.3: {} - - fast-fifo@1.3.2: - optional: true - - fast-safe-stringify@2.1.1: {} - - fast-uri@3.1.0: {} - - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - optional: true - - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - - fetch-cookie@3.2.0: - dependencies: - set-cookie-parser: 2.7.2 - tough-cookie: 6.0.0 - - finalhandler@2.1.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - form-data-encoder@1.7.2: {} - - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - formdata-node@4.4.1: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 4.0.0-beta.3 - - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - - forwarded@0.2.0: {} - - fresh@2.0.0: {} - - fsevents@2.3.2: - optional: true - - function-bind@1.1.2: {} - - gaxios@7.1.3: - dependencies: - extend: 3.0.2 - https-proxy-agent: 7.0.6 - node-fetch: 3.3.2 - rimraf: 5.0.10 - transitivePeerDependencies: - - supports-color - - gcp-metadata@8.1.2: - dependencies: - gaxios: 7.1.3 - google-logging-utils: 1.1.3 - json-bigint: 1.0.0 - transitivePeerDependencies: - - supports-color - - get-caller-file@2.0.5: - optional: true - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@5.2.0: - dependencies: - pump: 3.0.3 - optional: true - - get-uri@6.0.5: - dependencies: - basic-ftp: 5.0.5 - data-uri-to-buffer: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - optional: true - - glob@10.5.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - google-auth-library@10.5.0: - dependencies: - base64-js: 1.5.1 - ecdsa-sig-formatter: 1.0.11 - gaxios: 7.1.3 - gcp-metadata: 8.1.2 - google-logging-utils: 1.1.3 - gtoken: 8.0.0 - jws: 4.0.1 - transitivePeerDependencies: - - supports-color - - google-logging-utils@1.1.3: {} - - gopd@1.2.0: {} - - gtoken@8.0.0: - dependencies: - gaxios: 7.1.3 - jws: 4.0.1 - transitivePeerDependencies: - - supports-color - - has-flag@4.0.0: {} - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - help-me@5.0.0: {} - - hono@4.11.1: {} - - http-errors@2.0.1: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.2 - toidentifier: 1.0.1 - - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - optional: true - - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - humanize-ms@1.2.1: - dependencies: - ms: 2.1.3 - - iconv-lite@0.7.1: - dependencies: - safer-buffer: 2.1.2 - - ieee754@1.2.1: - optional: true - - inherits@2.0.4: {} - - ip-address@10.1.0: - optional: true - - ipaddr.js@1.9.1: {} - - is-docker@2.2.1: - optional: true - - is-fullwidth-code-point@3.0.0: {} - - is-promise@4.0.0: {} - - is-wsl@2.2.0: - dependencies: - is-docker: 2.2.1 - optional: true - - isexe@2.0.0: {} - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jose@6.1.3: {} - - joycon@3.1.1: {} - - js-tiktoken@1.0.21: - dependencies: - base64-js: 1.5.1 - - json-bigint@1.0.0: - dependencies: - bignumber.js: 9.3.1 - - json-schema-traverse@1.0.0: {} - - json-schema-typed@8.0.2: {} - - json-schema@0.4.0: {} - - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - - langsmith@0.3.87(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0)): - dependencies: - '@types/uuid': 10.0.0 - chalk: 4.1.2 - console-table-printer: 2.15.0 - p-queue: 6.6.2 - semver: 7.7.3 - uuid: 10.0.0 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0) - - lighthouse-logger@2.0.2: - dependencies: - debug: 4.4.3 - marky: 1.3.0 - transitivePeerDependencies: - - supports-color - optional: true - - lru-cache@10.4.3: {} - - lru-cache@7.18.3: - optional: true - - marky@1.3.0: - optional: true - - math-intrinsics@1.1.0: {} - - media-typer@1.1.0: {} - - merge-descriptors@2.0.0: {} - - mime-db@1.52.0: {} - - mime-db@1.54.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mime-types@3.0.2: - dependencies: - mime-db: 1.54.0 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - minipass@7.1.2: {} - - mitt@3.0.1: - optional: true - - ms@2.1.3: {} - - mustache@4.2.0: {} - - negotiator@1.0.0: {} - - netmask@2.0.2: - optional: true - - node-domexception@1.0.0: {} - - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - - node-gyp-build@4.8.4: - optional: true - - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - ollama-ai-provider-v2@1.5.5(zod@4.2.0): - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@4.2.0) - zod: 4.2.0 - optional: true - - on-exit-leak-free@2.1.2: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76): - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.13 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - optionalDependencies: - ws: 8.18.3(bufferutil@4.0.9) - zod: 3.25.76 - transitivePeerDependencies: - - encoding - - openai@4.104.0(ws@8.18.3(bufferutil@4.0.9))(zod@4.2.0): - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.13 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - optionalDependencies: - ws: 8.18.3(bufferutil@4.0.9) - zod: 4.2.0 - transitivePeerDependencies: - - encoding - - p-finally@1.0.0: {} - - p-queue@6.6.2: - dependencies: - eventemitter3: 4.0.7 - p-timeout: 3.2.0 - - p-retry@4.6.2: - dependencies: - '@types/retry': 0.12.0 - retry: 0.13.1 - - p-timeout@3.2.0: - dependencies: - p-finally: 1.0.0 - - pac-proxy-agent@7.2.0: - dependencies: - '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.4 - debug: 4.4.3 - get-uri: 6.0.5 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - optional: true - - pac-resolver@7.0.1: - dependencies: - degenerator: 5.0.1 - netmask: 2.0.2 - optional: true - - package-json-from-dist@1.0.1: {} - - parseurl@1.3.3: {} - - patchright-core@1.57.0: - optional: true - - path-key@3.1.1: {} - - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - path-to-regexp@8.3.0: {} - - pend@1.2.0: - optional: true - - pino-abstract-transport@2.0.0: - dependencies: - split2: 4.2.0 - - pino-abstract-transport@3.0.0: - dependencies: - split2: 4.2.0 - - pino-pretty@13.1.3: - dependencies: - colorette: 2.0.20 - dateformat: 4.6.3 - fast-copy: 4.0.1 - fast-safe-stringify: 2.1.1 - help-me: 5.0.0 - joycon: 3.1.1 - minimist: 1.2.8 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pump: 3.0.3 - secure-json-parse: 4.1.0 - sonic-boom: 4.2.0 - strip-json-comments: 5.0.3 - - pino-std-serializers@7.0.0: {} - - pino@9.14.0: - dependencies: - '@pinojs/redact': 0.4.0 - atomic-sleep: 1.0.0 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 2.0.0 - pino-std-serializers: 7.0.0 - process-warning: 5.0.0 - quick-format-unescaped: 4.0.4 - real-require: 0.2.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.0 - thread-stream: 3.1.0 - - pkce-challenge@5.0.1: {} - - playwright-core@1.57.0: {} - - playwright@1.57.0: - dependencies: - playwright-core: 1.57.0 - optionalDependencies: - fsevents: 2.3.2 - - process-warning@5.0.0: {} - - progress@2.0.3: - optional: true - - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - proxy-agent@6.5.0: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - lru-cache: 7.18.3 - pac-proxy-agent: 7.2.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - optional: true - - proxy-from-env@1.1.0: - optional: true - - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - puppeteer-core@22.15.0(bufferutil@4.0.9): - dependencies: - '@puppeteer/browsers': 2.3.0 - chromium-bidi: 0.6.3(devtools-protocol@0.0.1312386) - debug: 4.4.3 - devtools-protocol: 0.0.1312386 - ws: 8.18.3(bufferutil@4.0.9) - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - bufferutil - - react-native-b4a - - supports-color - - utf-8-validate - optional: true - - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - - quick-format-unescaped@4.0.4: {} - - range-parser@1.2.1: {} - - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.1 - unpipe: 1.0.0 - - real-require@0.2.0: {} - - require-directory@2.1.1: - optional: true - - require-from-string@2.0.2: {} - - retry@0.13.1: {} - - rimraf@5.0.10: - dependencies: - glob: 10.5.0 - - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.3.0 - transitivePeerDependencies: - - supports-color - - safe-buffer@5.2.1: {} - - safe-stable-stringify@2.5.0: {} - - safer-buffer@2.1.2: {} - - secure-json-parse@4.1.0: {} - - semver@7.7.3: {} - - send@1.2.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - serve-static@2.2.1: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.1 - transitivePeerDependencies: - - supports-color - - set-cookie-parser@2.7.2: {} - - setprototypeof@1.2.0: {} - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - signal-exit@4.1.0: {} - - simple-wcswidth@1.1.2: {} - - smart-buffer@4.2.0: - optional: true - - socks-proxy-agent@8.0.5: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - socks: 2.8.7 - transitivePeerDependencies: - - supports-color - optional: true - - socks@2.8.7: - dependencies: - ip-address: 10.1.0 - smart-buffer: 4.2.0 - optional: true - - sonic-boom@4.2.0: - dependencies: - atomic-sleep: 1.0.0 - - source-map@0.6.1: - optional: true - - split2@4.2.0: {} - - statuses@2.0.2: {} - - streamx@2.23.0: - dependencies: - events-universal: 1.0.1 - fast-fifo: 1.3.2 - text-decoder: 1.2.3 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - optional: true - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.2 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - - strip-json-comments@5.0.3: {} - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - tar-fs@3.1.1: - dependencies: - pump: 3.0.3 - tar-stream: 3.1.7 - optionalDependencies: - bare-fs: 4.5.2 - bare-path: 3.0.0 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - optional: true - - tar-stream@3.1.7: - dependencies: - b4a: 1.7.3 - fast-fifo: 1.3.2 - streamx: 2.23.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - optional: true - - text-decoder@1.2.3: - dependencies: - b4a: 1.7.3 - transitivePeerDependencies: - - react-native-b4a - optional: true - - thread-stream@3.1.0: - dependencies: - real-require: 0.2.0 - - through@2.3.8: - optional: true - - tldts-core@7.0.19: {} - - tldts@7.0.19: - dependencies: - tldts-core: 7.0.19 - - toidentifier@1.0.1: {} - - tough-cookie@6.0.0: - dependencies: - tldts: 7.0.19 - - tr46@0.0.3: {} - - tslib@2.8.1: - optional: true - - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.2 - - typescript@5.9.3: {} - - unbzip2-stream@1.4.3: - dependencies: - buffer: 5.7.1 - through: 2.3.8 - optional: true - - undici-types@5.26.5: {} - - undici-types@6.21.0: {} - - unpipe@1.0.0: {} - - urlpattern-polyfill@10.0.0: - optional: true - - uuid@10.0.0: {} - - uuid@11.1.0: {} - - vary@1.1.2: {} - - web-streams-polyfill@3.3.3: {} - - web-streams-polyfill@4.0.0-beta.3: {} - - webidl-conversions@3.0.1: {} - - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.1.2 - - wrappy@1.0.2: {} - - ws@8.18.3(bufferutil@4.0.9): - optionalDependencies: - bufferutil: 4.0.9 - - y18n@5.0.8: - optional: true - - yargs-parser@21.1.1: - optional: true - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - optional: true - - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - optional: true - - zod-to-json-schema@3.25.0(zod@3.25.76): - dependencies: - zod: 3.25.76 - - zod-to-json-schema@3.25.0(zod@4.2.0): - dependencies: - zod: 4.2.0 - - zod@3.23.8: - optional: true - - zod@3.25.76: {} - - zod@4.2.0: {} diff --git a/pkg/templates/typescript/gemini-computer-use/session.ts b/pkg/templates/typescript/gemini-computer-use/session.ts new file mode 100644 index 0000000..fd68442 --- /dev/null +++ b/pkg/templates/typescript/gemini-computer-use/session.ts @@ -0,0 +1,199 @@ +/** + * Kernel Browser Session Manager. + * + * Provides a class for managing Kernel browser lifecycle + * with optional video replay recording. + */ + +import type { Kernel } from '@onkernel/sdk'; +import { DEFAULT_SCREEN_SIZE } from './tools/types/gemini'; + +export interface SessionOptions { + stealth?: boolean; + timeoutSeconds?: number; + recordReplay?: boolean; + replayGracePeriod?: number; +} + +export interface SessionInfo { + sessionId: string; + liveViewUrl: string; + replayId?: string; + replayViewUrl?: string; +} + +const DEFAULT_OPTIONS: Required = { + stealth: true, + timeoutSeconds: 300, + recordReplay: false, + replayGracePeriod: 5.0, +}; + +export class KernelBrowserSession { + private kernel: Kernel; + private options: Required; + + // Session state + private _sessionId: string | null = null; + private _liveViewUrl: string | null = null; + private _replayId: string | null = null; + private _replayViewUrl: string | null = null; + + constructor(kernel: Kernel, options: SessionOptions = {}) { + this.kernel = kernel; + this.options = { ...DEFAULT_OPTIONS, ...options }; + } + + get sessionId(): string { + if (!this._sessionId) { + throw new Error('Session not started. Call start() first.'); + } + return this._sessionId; + } + + get liveViewUrl(): string | null { + return this._liveViewUrl; + } + + get replayViewUrl(): string | null { + return this._replayViewUrl; + } + + get info(): SessionInfo { + return { + sessionId: this.sessionId, + liveViewUrl: this._liveViewUrl || '', + replayId: this._replayId || undefined, + replayViewUrl: this._replayViewUrl || undefined, + }; + } + + async start(): Promise { + // Create browser with specified settings + const browser = await this.kernel.browsers.create({ + stealth: this.options.stealth, + timeout_seconds: this.options.timeoutSeconds, + viewport: { + width: DEFAULT_SCREEN_SIZE.width, + height: DEFAULT_SCREEN_SIZE.height, + }, + }); + + this._sessionId = browser.session_id; + this._liveViewUrl = browser.browser_live_view_url; + + console.log(`Kernel browser created: ${this._sessionId}`); + console.log(`Live view URL: ${this._liveViewUrl}`); + + // Start replay recording if enabled + if (this.options.recordReplay) { + try { + await this.startReplay(); + } catch (error) { + console.warn(`Warning: Failed to start replay recording: ${error}`); + console.warn('Continuing without replay recording.'); + } + } + + return this.info; + } + + private async startReplay(): Promise { + if (!this._sessionId) { + return; + } + + console.log('Starting replay recording...'); + const replay = await this.kernel.browsers.replays.start(this._sessionId); + this._replayId = replay.replay_id; + console.log(`Replay recording started: ${this._replayId}`); + } + + private async stopReplay(): Promise { + if (!this._sessionId || !this._replayId) { + return; + } + + console.log('Stopping replay recording...'); + await this.kernel.browsers.replays.stop(this._replayId, { + id: this._sessionId, + }); + console.log('Replay recording stopped. Processing video...'); + + // Wait a moment for processing + await this.sleep(2000); + + // Poll for replay to be ready (with timeout) + const maxWait = 60000; // 60 seconds + const startTime = Date.now(); + let replayReady = false; + + while (Date.now() - startTime < maxWait) { + try { + const replays = await this.kernel.browsers.replays.list(this._sessionId); + for (const replay of replays) { + if (replay.replay_id === this._replayId) { + this._replayViewUrl = replay.replay_view_url; + replayReady = true; + break; + } + } + if (replayReady) { + break; + } + } catch { + // Ignore errors while polling + } + await this.sleep(1000); + } + + if (!replayReady) { + console.log('Warning: Replay may still be processing'); + } else if (this._replayViewUrl) { + console.log(`Replay view URL: ${this._replayViewUrl}`); + } + } + + async stop(): Promise { + // Build info object directly to avoid throwing if session wasn't started + const currentSessionId = this._sessionId; + const info: SessionInfo = { + sessionId: currentSessionId || '', + liveViewUrl: this._liveViewUrl || '', + replayId: this._replayId || undefined, + replayViewUrl: this._replayViewUrl || undefined, + }; + + if (currentSessionId) { + try { + // Stop replay if recording was enabled + if (this.options.recordReplay && this._replayId) { + // Wait grace period before stopping to capture final state + if (this.options.replayGracePeriod > 0) { + console.log(`Waiting ${this.options.replayGracePeriod}s grace period...`); + await this.sleep(this.options.replayGracePeriod * 1000); + } + await this.stopReplay(); + info.replayViewUrl = this._replayViewUrl || undefined; + } + } finally { + // Always clean up the browser session, even if replay stopping fails + console.log(`Destroying browser session: ${currentSessionId}`); + await this.kernel.browsers.deleteByID(currentSessionId); + console.log('Browser session destroyed.'); + } + } + + // Reset state + this._sessionId = null; + this._liveViewUrl = null; + this._replayId = null; + this._replayViewUrl = null; + + return info; + } + + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} diff --git a/pkg/templates/typescript/gemini-computer-use/tools/computer.ts b/pkg/templates/typescript/gemini-computer-use/tools/computer.ts new file mode 100644 index 0000000..82e98b6 --- /dev/null +++ b/pkg/templates/typescript/gemini-computer-use/tools/computer.ts @@ -0,0 +1,304 @@ +/** + * Gemini Computer Tool - Maps Gemini actions to Kernel Computer Controls API. + * Based on Google's computer-use-preview reference implementation. + */ + +import { Buffer } from 'buffer'; +import type { Kernel } from '@onkernel/sdk'; +import { + GeminiAction, + PREDEFINED_COMPUTER_USE_FUNCTIONS, + DEFAULT_SCREEN_SIZE, + COORDINATE_SCALE, + type GeminiFunctionArgs, + type ToolResult, + type ScreenSize, +} from './types/gemini'; + +const TYPING_DELAY_MS = 12; +const SCREENSHOT_DELAY_MS = 500; + +/** + * Computer tool that maps Gemini actions to Kernel's Computer Controls API. + */ +export class ComputerTool { + private kernel: Kernel; + private sessionId: string; + private screenSize: ScreenSize; + + constructor(kernel: Kernel, sessionId: string, screenSize: ScreenSize = DEFAULT_SCREEN_SIZE) { + this.kernel = kernel; + this.sessionId = sessionId; + this.screenSize = screenSize; + } + + private denormalizeX(x: number): number { + return Math.round((x / COORDINATE_SCALE) * this.screenSize.width); + } + + private denormalizeY(y: number): number { + return Math.round((y / COORDINATE_SCALE) * this.screenSize.height); + } + + async screenshot(): Promise { + try { + await this.sleep(SCREENSHOT_DELAY_MS); + const response = await this.kernel.browsers.computer.captureScreenshot(this.sessionId); + const blob = await response.blob(); + const arrayBuffer = await blob.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + + // Get current URL + let url = ''; + try { + const state = await this.kernel.browsers.computer.getState(this.sessionId); + url = state.url || ''; + } catch { + // Ignore URL fetch errors + } + + return { + base64Image: buffer.toString('base64'), + url, + }; + } catch (error) { + return { + error: `Failed to take screenshot: ${error}`, + }; + } + } + + async executeAction(actionName: string, args: GeminiFunctionArgs): Promise { + // Check if this is a known computer use function + if (!PREDEFINED_COMPUTER_USE_FUNCTIONS.includes(actionName as GeminiAction)) { + return { error: `Unknown action: ${actionName}` }; + } + + try { + switch (actionName) { + case GeminiAction.OPEN_WEB_BROWSER: + // Browser is already open in Kernel, just return screenshot + break; + + case GeminiAction.CLICK_AT: { + if (args.x === undefined || args.y === undefined) { + return { error: 'click_at requires x and y coordinates' }; + } + const x = this.denormalizeX(args.x); + const y = this.denormalizeY(args.y); + await this.kernel.browsers.computer.clickMouse(this.sessionId, { + x, + y, + button: 'left', + click_type: 'click', + num_clicks: 1, + }); + break; + } + + case GeminiAction.HOVER_AT: { + if (args.x === undefined || args.y === undefined) { + return { error: 'hover_at requires x and y coordinates' }; + } + const x = this.denormalizeX(args.x); + const y = this.denormalizeY(args.y); + await this.kernel.browsers.computer.moveMouse(this.sessionId, { x, y }); + break; + } + + case GeminiAction.TYPE_TEXT_AT: { + if (args.x === undefined || args.y === undefined) { + return { error: 'type_text_at requires x and y coordinates' }; + } + if (!args.text) { + return { error: 'type_text_at requires text' }; + } + + const x = this.denormalizeX(args.x); + const y = this.denormalizeY(args.y); + + // Click at the location first + await this.kernel.browsers.computer.clickMouse(this.sessionId, { + x, + y, + button: 'left', + click_type: 'click', + num_clicks: 1, + }); + + // Clear existing text if requested (default: true) + if (args.clear_before_typing !== false) { + await this.kernel.browsers.computer.pressKey(this.sessionId, { + keys: ['ctrl+a'], + }); + await this.sleep(50); + } + + // Type the text + await this.kernel.browsers.computer.typeText(this.sessionId, { + text: args.text, + delay: TYPING_DELAY_MS, + }); + + // Press enter if requested + if (args.press_enter) { + await this.sleep(100); + await this.kernel.browsers.computer.pressKey(this.sessionId, { + keys: ['Return'], + }); + } + break; + } + + case GeminiAction.SCROLL_DOCUMENT: { + if (!args.direction) { + return { error: 'scroll_document requires direction' }; + } + // Scroll at center of viewport + const centerX = Math.round(this.screenSize.width / 2); + const centerY = Math.round(this.screenSize.height / 2); + const scrollDelta = 500; + + let deltaX = 0; + let deltaY = 0; + if (args.direction === 'down') deltaY = scrollDelta; + else if (args.direction === 'up') deltaY = -scrollDelta; + else if (args.direction === 'right') deltaX = scrollDelta; + else if (args.direction === 'left') deltaX = -scrollDelta; + + await this.kernel.browsers.computer.scroll(this.sessionId, { + x: centerX, + y: centerY, + delta_x: deltaX, + delta_y: deltaY, + }); + break; + } + + case GeminiAction.SCROLL_AT: { + if (args.x === undefined || args.y === undefined) { + return { error: 'scroll_at requires x and y coordinates' }; + } + if (!args.direction) { + return { error: 'scroll_at requires direction' }; + } + + const x = this.denormalizeX(args.x); + const y = this.denormalizeY(args.y); + + // Denormalize magnitude if provided + let magnitude = args.magnitude ?? 800; + if (args.direction === 'up' || args.direction === 'down') { + magnitude = this.denormalizeY(magnitude); + } else { + magnitude = this.denormalizeX(magnitude); + } + + let deltaX = 0; + let deltaY = 0; + if (args.direction === 'down') deltaY = magnitude; + else if (args.direction === 'up') deltaY = -magnitude; + else if (args.direction === 'right') deltaX = magnitude; + else if (args.direction === 'left') deltaX = -magnitude; + + await this.kernel.browsers.computer.scroll(this.sessionId, { + x, + y, + delta_x: deltaX, + delta_y: deltaY, + }); + break; + } + + case GeminiAction.WAIT_5_SECONDS: + await this.sleep(5000); + break; + + case GeminiAction.GO_BACK: + await this.kernel.browsers.computer.pressKey(this.sessionId, { + keys: ['alt+Left'], + }); + await this.sleep(1000); + break; + + case GeminiAction.GO_FORWARD: + await this.kernel.browsers.computer.pressKey(this.sessionId, { + keys: ['alt+Right'], + }); + await this.sleep(1000); + break; + + case GeminiAction.SEARCH: + // Focus URL bar (Ctrl+L) - equivalent to clicking search + await this.kernel.browsers.computer.pressKey(this.sessionId, { + keys: ['ctrl+l'], + }); + break; + + case GeminiAction.NAVIGATE: { + if (!args.url) { + return { error: 'navigate requires url' }; + } + // Focus URL bar and type the URL + await this.kernel.browsers.computer.pressKey(this.sessionId, { + keys: ['ctrl+l'], + }); + await this.sleep(100); + await this.kernel.browsers.computer.typeText(this.sessionId, { + text: args.url, + delay: TYPING_DELAY_MS, + }); + await this.sleep(100); + await this.kernel.browsers.computer.pressKey(this.sessionId, { + keys: ['Return'], + }); + await this.sleep(1500); // Wait for navigation + break; + } + + case GeminiAction.KEY_COMBINATION: { + if (!args.keys) { + return { error: 'key_combination requires keys' }; + } + // Gemini sends keys as "key1+key2+key3" + await this.kernel.browsers.computer.pressKey(this.sessionId, { + keys: [args.keys], + }); + break; + } + + case GeminiAction.DRAG_AND_DROP: { + if (args.x === undefined || args.y === undefined || + args.destination_x === undefined || args.destination_y === undefined) { + return { error: 'drag_and_drop requires x, y, destination_x, and destination_y' }; + } + + const startX = this.denormalizeX(args.x); + const startY = this.denormalizeY(args.y); + const endX = this.denormalizeX(args.destination_x); + const endY = this.denormalizeY(args.destination_y); + + await this.kernel.browsers.computer.dragMouse(this.sessionId, { + path: [[startX, startY], [endX, endY]], + button: 'left', + }); + break; + } + + default: + return { error: `Unhandled action: ${actionName}` }; + } + + // Wait a moment for the action to complete, then take a screenshot + await this.sleep(SCREENSHOT_DELAY_MS); + return await this.screenshot(); + + } catch (error) { + return { error: `Action failed: ${error}` }; + } + } + + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} diff --git a/pkg/templates/typescript/gemini-computer-use/tools/types/gemini.ts b/pkg/templates/typescript/gemini-computer-use/tools/types/gemini.ts new file mode 100644 index 0000000..56764b5 --- /dev/null +++ b/pkg/templates/typescript/gemini-computer-use/tools/types/gemini.ts @@ -0,0 +1,78 @@ +/** + * Type definitions for Gemini Computer Use actions. + * Based on Google's computer-use-preview reference implementation. + */ + +export enum GeminiAction { + OPEN_WEB_BROWSER = 'open_web_browser', + CLICK_AT = 'click_at', + HOVER_AT = 'hover_at', + TYPE_TEXT_AT = 'type_text_at', + SCROLL_DOCUMENT = 'scroll_document', + SCROLL_AT = 'scroll_at', + WAIT_5_SECONDS = 'wait_5_seconds', + GO_BACK = 'go_back', + GO_FORWARD = 'go_forward', + SEARCH = 'search', + NAVIGATE = 'navigate', + KEY_COMBINATION = 'key_combination', + DRAG_AND_DROP = 'drag_and_drop', +} + +// Derive from enum to prevent drift when adding new actions +export const PREDEFINED_COMPUTER_USE_FUNCTIONS = Object.values(GeminiAction); + +export type ScrollDirection = 'up' | 'down' | 'left' | 'right'; + +export interface GeminiFunctionArgs { + // click_at, hover_at, scroll_at + x?: number; + y?: number; + + // type_text_at + text?: string; + press_enter?: boolean; + clear_before_typing?: boolean; + + // scroll_document, scroll_at + direction?: ScrollDirection; + magnitude?: number; + + // navigate + url?: string; + + // key_combination + keys?: string; + + // drag_and_drop + destination_x?: number; + destination_y?: number; + + // Safety decision (may be included in any function call) + safety_decision?: { + decision: string; + explanation: string; + }; +} + +export interface ToolResult { + /** Base64-encoded screenshot image */ + base64Image?: string; + /** Current URL of the browser */ + url?: string; + /** Error message if the action failed */ + error?: string; +} + +export interface ScreenSize { + width: number; + height: number; +} + +export const DEFAULT_SCREEN_SIZE: ScreenSize = { + width: 1200, + height: 800, +}; + +// Gemini uses normalized coordinates (0-1000) +export const COORDINATE_SCALE = 1000;