diff --git a/.changeset/lovely-friends-study.md b/.changeset/lovely-friends-study.md new file mode 100644 index 00000000..3202773f --- /dev/null +++ b/.changeset/lovely-friends-study.md @@ -0,0 +1,7 @@ +--- +'@cloudflare/sandbox': patch +--- + +Add OpenAI Agents adapters + +Add OpenAI Agents adapters (`Shell` and `Editor`) that integrate Cloudflare Sandbox with the OpenAI Agents SDK. These adapters enable AI agents to execute shell commands and perform file operations (create, update, delete) inside sandboxed environments. Both adapters automatically collect and timestamp results from operations, making it easy to track command execution and file modifications during agent sessions. The adapters are exported from `@cloudflare/sandbox/openai` and implement the OpenAI Agents `Shell` and `Editor` interfaces. diff --git a/docs/OPENAI_AGENTS.md b/docs/OPENAI_AGENTS.md new file mode 100644 index 00000000..187ad277 --- /dev/null +++ b/docs/OPENAI_AGENTS.md @@ -0,0 +1,323 @@ +# OpenAI Agents Adapter + +The Cloudflare Sandbox SDK provides adapters that integrate with the [OpenAI Agents SDK](https://github.com/openai/agents) to enable AI agents to execute shell commands and perform file operations inside sandboxed environments. + +## Overview + +The OpenAI Agents adapter consists of two main components: + +- **`Shell`**: Implements the OpenAI Agents `Shell` interface, allowing agents to execute shell commands in the sandbox +- **`Editor`**: Implements the OpenAI Agents `Editor` interface, enabling agents to create, update, and delete files using patch operations + +Both adapters automatically collect results from operations, making it easy to track what commands were executed and what files were modified during an agent session. + +## Installation + +The adapters are part of the `@cloudflare/sandbox` package: + +```typescript +import { getSandbox } from '@cloudflare/sandbox'; +import { Shell, Editor } from '@cloudflare/sandbox/openai'; +import { Agent, applyPatchTool, run, shellTool } from '@openai/agents'; +``` + +## Basic Usage + +### Setting Up an Agent + +```typescript +import { getSandbox } from '@cloudflare/sandbox'; +import { Shell, Editor } from '@cloudflare/sandbox/openai'; +import { Agent, applyPatchTool, run, shellTool } from '@openai/agents'; + +export default { + async fetch(request: Request, env: Env): Promise { + // Get a sandbox instance + const sandbox = getSandbox(env.Sandbox, 'workspace-session'); + + // Create shell adapter (executes commands in /workspace by default) + const shell = new Shell(sandbox); + + // Create editor adapter (operates on /workspace by default) + const editor = new Editor(sandbox, '/workspace'); + + // Create an agent with both tools + const agent = new Agent({ + name: 'Sandbox Assistant', + model: 'gpt-4', + instructions: + 'You can execute shell commands and edit files in the workspace.', + tools: [ + shellTool({ shell, needsApproval: false }), + applyPatchTool({ editor, needsApproval: false }) + ] + }); + + // Run the agent with user input + const { input } = await request.json(); + const result = await run(agent, input); + + // Access collected results + const commandResults = shell.results; + const fileOperations = editor.results; + + return new Response( + JSON.stringify({ + naturalResponse: result.finalOutput, + commandResults, + fileOperations + }), + { + headers: { 'Content-Type': 'application/json' } + } + ); + } +}; +``` + +## Shell Adapter + +The `Shell` class adapts Cloudflare Sandbox `exec` calls to the OpenAI Agents `Shell` contract. + +### Features + +- Executes commands sequentially in the sandbox +- Preserves working directory (`/workspace` by default) +- Handles timeouts and errors gracefully +- Collects results with timestamps for each command +- Separates stdout and stderr output + +### Command Results + +Each executed command is automatically collected in `shell.results`: + +```typescript +interface CommandResult { + command: string; // The command that was executed + stdout: string; // Standard output + stderr: string; // Standard error + exitCode: number | null; // Exit code (null for timeouts) + timestamp: number; // Unix timestamp in milliseconds +} +``` + +### Example: Inspecting Workspace + +```typescript +const shell = new Shell(sandbox); + +// Agent can execute commands like: +// - ls -la +// - cat package.json +// - git status +// - npm install + +// After agent execution, access results: +shell.results.forEach((result) => { + console.log(`Command: ${result.command}`); + console.log(`Exit code: ${result.exitCode}`); + console.log(`Output: ${result.stdout}`); +}); +``` + +### Error Handling + +The Shell adapter handles various error scenarios: + +- **Command failures**: Non-zero exit codes are captured in `exitCode` +- **Timeouts**: Commands that exceed the timeout return `exitCode: null` and `outcome.type: 'timeout'` +- **Network errors**: HTTP/network errors are caught and logged + +## Editor Adapter + +The `Editor` class implements file operations using the OpenAI Agents patch-based editing system. + +### Features + +- Creates files with initial content using diffs +- Updates existing files by applying diffs +- Deletes files +- Automatically creates parent directories when needed +- Validates paths to prevent operations outside the workspace +- Collects results with timestamps for each operation + +### File Operation Results + +Each file operation is automatically collected in `editor.results`: + +```typescript +interface FileOperationResult { + operation: 'create' | 'update' | 'delete'; + path: string; // Relative path from workspace root + status: 'completed' | 'failed'; + output: string; // Human-readable status message + error?: string; // Error message if status is 'failed' + timestamp: number; // Unix timestamp in milliseconds +} +``` + +### Path Resolution + +The Editor enforces security by: + +- Resolving relative paths within the workspace root (`/workspace` by default) +- Preventing path traversal attacks (e.g., `../../../etc/passwd`) +- Normalizing path separators and removing redundant segments +- Throwing errors for operations outside the workspace + +### Example: Creating and Editing Files + +```typescript +const editor = new Editor(sandbox, '/workspace'); + +// Agent can use apply_patch tool to: +// - Create new files with content +// - Update existing files with diffs +// - Delete files + +// After agent execution, access results: +editor.results.forEach((result) => { + console.log(`${result.operation}: ${result.path}`); + console.log(`Status: ${result.status}`); + if (result.error) { + console.log(`Error: ${result.error}`); + } +}); +``` + +### Custom Workspace Root + +You can specify a custom workspace root: + +```typescript +// Use a different root directory +const editor = new Editor(sandbox, '/custom/workspace'); +``` + +## Complete Example + +Here's a complete example showing how to integrate the adapters in a Cloudflare Worker: + +```typescript +import { getSandbox } from '@cloudflare/sandbox'; +import { Shell, Editor } from '@cloudflare/sandbox/openai'; +import { Agent, applyPatchTool, run, shellTool } from '@openai/agents'; + +async function handleRunRequest(request: Request, env: Env): Promise { + try { + const { input } = await request.json(); + + if (!input || typeof input !== 'string') { + return new Response( + JSON.stringify({ error: 'Missing or invalid input field' }), + { status: 400, headers: { 'Content-Type': 'application/json' } } + ); + } + + // Get sandbox instance (reused for both shell and editor) + const sandbox = getSandbox(env.Sandbox, 'workspace-session'); + + // Create adapters + const shell = new Shell(sandbox); + const editor = new Editor(sandbox, '/workspace'); + + // Create agent with tools + const agent = new Agent({ + name: 'Sandbox Studio', + model: 'gpt-4', + instructions: ` + You can execute shell commands and edit files in the workspace. + Use shell commands to inspect the repository and the apply_patch tool + to create, update, or delete files. Keep responses concise and include + command output when helpful. + `, + tools: [ + shellTool({ shell, needsApproval: false }), + applyPatchTool({ editor, needsApproval: false }) + ] + }); + + // Run the agent + const result = await run(agent, input); + + // Format response with sorted results + const response = { + naturalResponse: result.finalOutput || null, + commandResults: shell.results.sort((a, b) => a.timestamp - b.timestamp), + fileOperations: editor.results.sort((a, b) => a.timestamp - b.timestamp) + }; + + return new Response(JSON.stringify(response), { + headers: { 'Content-Type': 'application/json' } + }); + } catch (error) { + return new Response( + JSON.stringify({ + error: error instanceof Error ? error.message : 'Internal server error', + naturalResponse: 'An error occurred while processing your request.', + commandResults: [], + fileOperations: [] + }), + { + status: 500, + headers: { 'Content-Type': 'application/json' } + } + ); + } +} + +export default { + async fetch(request: Request, env: Env): Promise { + const url = new URL(request.url); + + if (url.pathname === '/run' && request.method === 'POST') { + return handleRunRequest(request, env); + } + + return new Response('Not found', { status: 404 }); + } +}; +``` + +## Result Tracking + +Both adapters automatically track all operations with timestamps. This makes it easy to: + +- **Audit operations**: See exactly what commands were run and files were modified +- **Debug issues**: Identify which operation failed and when +- **Build UIs**: Display a timeline of agent actions +- **Logging**: Export operation history for analysis + +### Combining Results + +You can combine and sort results from both adapters: + +```typescript +const allResults = [ + ...shell.results.map((r) => ({ type: 'command' as const, ...r })), + ...editor.results.map((r) => ({ type: 'file' as const, ...r })) +].sort((a, b) => a.timestamp - b.timestamp); + +// allResults is now a chronological list of all operations +``` + +## Best Practices + +1. **Reuse sandbox instances**: Create one sandbox instance and share it between Shell and Editor +2. **Set appropriate timeouts**: Configure command timeouts based on expected operation duration +3. **Handle errors gracefully**: Check `status` fields in results and handle `failed` operations +4. **Validate paths**: The Editor already validates paths, but be aware of workspace boundaries +5. **Monitor resource usage**: Large command outputs or file operations may impact performance + +## Limitations + +- **Working directory**: Shell operations always execute in `/workspace` (or the configured root) +- **Path restrictions**: File operations are restricted to the workspace root +- **Sequential execution**: Commands execute sequentially, not in parallel +- **Timeout handling**: Timeouts stop further command execution in a batch + +## See Also + +- [OpenAI Agents SDK Documentation](https://github.com/openai/openai-agents-js/) +- [Session Execution Architecture](./SESSION_EXECUTION.md) - Understanding how commands execute in sandboxes +- [Example Implementation](../examples/openai-agents/src/index.ts) - Full working example diff --git a/examples/openai-agents/Dockerfile b/examples/openai-agents/Dockerfile new file mode 100644 index 00000000..84025409 --- /dev/null +++ b/examples/openai-agents/Dockerfile @@ -0,0 +1,13 @@ +# This image is unique to this repo, and you'll never need it. +# Whenever you're integrating with sandbox SDK in your own project, +# you should use the official image instead: +# FROM docker.io/cloudflare/sandbox:0.5.0 +# FROM cloudflare/sandbox-test:0.5.0 + +# On a mac, you might need to actively pick up the +# arm64 build of the image. +FROM --platform=linux/arm64 cloudflare/sandbox-test:0.5.0 + +# Required during local development to access exposed ports +EXPOSE 8080 +EXPOSE 3000 diff --git a/examples/openai-agents/README.md b/examples/openai-agents/README.md new file mode 100644 index 00000000..531bca28 --- /dev/null +++ b/examples/openai-agents/README.md @@ -0,0 +1,42 @@ +# OpenAI Agents with Cloudflare Sandbox + +A conversational AI assistant that executes shell commands and edits files in a Cloudflare Sandbox. + +## Setup + +Create a `.env` file with your OpenAI API key: + +``` +OPENAI_API_KEY=your-api-key-here +``` + +Then start the development server: + +```bash +npm start +``` + +## Usage + +Enter natural language commands in the chat interface. The assistant can: + +- Execute shell commands +- Create, edit, and delete files + +All conversations are saved in your browser's localStorage. + +## Deploy + +```bash +npm run deploy +``` + +## Security Warning + +**This example auto-approves all AI operations without human review.** The AI can: + +- Execute ANY shell command +- Create, modify, or delete ANY file in /workspace +- No safety limits beyond the container itself + +**Do not use in production without proper approval flows and rate limiting.** diff --git a/examples/openai-agents/env.d.ts b/examples/openai-agents/env.d.ts new file mode 100644 index 00000000..7e155674 --- /dev/null +++ b/examples/openai-agents/env.d.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +// Generated by Wrangler by running `wrangler types env.d.ts --include-runtime false` (hash: 1f6b96358a96f1fdba78ce47af501d9f) +declare namespace Cloudflare { + interface GlobalProps { + mainModule: typeof import('./src/index'); + durableNamespaces: 'Sandbox'; + } + interface Env { + OPENAI_API_KEY: string; + Sandbox: DurableObjectNamespace; + } +} +interface Env extends Cloudflare.Env {} +type StringifyValues> = { + [Binding in keyof EnvType]: EnvType[Binding] extends string + ? EnvType[Binding] + : string; +}; +declare namespace NodeJS { + interface ProcessEnv + extends StringifyValues> {} +} diff --git a/examples/openai-agents/index.html b/examples/openai-agents/index.html new file mode 100644 index 00000000..71d001ab --- /dev/null +++ b/examples/openai-agents/index.html @@ -0,0 +1,14 @@ + + + + + + OpenAI Agents - Sandbox SDK + + + + +
+ + + diff --git a/examples/openai-agents/package.json b/examples/openai-agents/package.json new file mode 100644 index 00000000..f9890bfa --- /dev/null +++ b/examples/openai-agents/package.json @@ -0,0 +1,29 @@ +{ + "name": "@cloudflare/sandbox-openai-agents-example", + "version": "1.0.0", + "description": "Using OpenAI Agents with Sandbox SDK", + "private": true, + "scripts": { + "start": "vite dev", + "deploy": "vite build && wrangler deploy", + "types": "wrangler types env.d.ts --include-runtime false", + "typecheck": "tsc --noEmit" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "@openai/agents": "^0.3.2", + "nanoid": "^3.3.11", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@cloudflare/vite-plugin": "^1.14.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "vite": "^7.2.2" + } +} diff --git a/examples/openai-agents/public/favicon.ico b/examples/openai-agents/public/favicon.ico new file mode 100644 index 00000000..ade1c1db Binary files /dev/null and b/examples/openai-agents/public/favicon.ico differ diff --git a/examples/openai-agents/public/normalize.css b/examples/openai-agents/public/normalize.css new file mode 100644 index 00000000..2768db43 --- /dev/null +++ b/examples/openai-agents/public/normalize.css @@ -0,0 +1,351 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { + /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { + /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type='button']::-moz-focus-inner, +[type='reset']::-moz-focus-inner, +[type='submit']::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type='button']:-moz-focusring, +[type='reset']:-moz-focusring, +[type='submit']:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type='checkbox'], +[type='radio'] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type='number']::-webkit-inner-spin-button, +[type='number']::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type='search'] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type='search']::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} diff --git a/examples/openai-agents/src/app.tsx b/examples/openai-agents/src/app.tsx new file mode 100644 index 00000000..9cff2316 --- /dev/null +++ b/examples/openai-agents/src/app.tsx @@ -0,0 +1,339 @@ +import { useEffect, useRef, useState } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import type { + CommandResult, + FileOperationResult +} from '@cloudflare/sandbox/openai'; +import { nanoid } from 'nanoid'; + +interface Response { + naturalResponse: string | null; + commandResults: CommandResult[]; + fileOperations?: FileOperationResult[]; +} + +interface Message { + id: string; + input: string; + response: Response | null; + timestamp: number; +} + +/** + * Get or create a session ID for this user. + * The session ID is stored in localStorage and persists across browser sessions. + */ +function getOrCreateSessionId(): string { + let sessionId = localStorage.getItem('session-id'); + + if (!sessionId) { + sessionId = nanoid(8); + localStorage.setItem('session-id', sessionId); + } + + return sessionId; +} + +const STORAGE_KEY = 'openai-agents-history'; + +async function makeApiCall(input: string): Promise { + try { + const response = await fetch('/run', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Session-Id': getOrCreateSessionId() + }, + body: JSON.stringify({ input }) + }); + + if (!response.ok) { + throw new Error(`API error: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error('API call failed:', error); + throw error; + } +} + +function App() { + const [input, setInput] = useState(''); + const [messages, setMessages] = useState([]); + const [loading, setLoading] = useState(false); + const messagesEndRef = useRef(null); + const inputRef = useRef(null); + + // Load messages from localStorage on mount + useEffect(() => { + const saved = localStorage.getItem(STORAGE_KEY); + if (saved) { + try { + const parsed = JSON.parse(saved); + setMessages(parsed); + } catch (error) { + console.error('Error loading history:', error); + } + } + }, []); + + // Save messages to localStorage whenever they change + useEffect(() => { + if (messages.length > 0) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(messages)); + } + }, [messages]); + + // Scroll to bottom whenever messages change + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, []); + + // Focus input on mount + useEffect(() => { + inputRef.current?.focus(); + }, []); + + // Focus input after response comes in + useEffect(() => { + if (!loading && messages.length > 0) { + // Small delay to ensure DOM is updated + setTimeout(() => { + inputRef.current?.focus(); + }, 100); + } + }, [loading, messages.length]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!input.trim() || loading) return; + + const userInput = input.trim(); + setInput(''); + setLoading(true); + + // Add user message immediately + const userMessage: Message = { + id: Date.now().toString(), + input: userInput, + response: null, + timestamp: Date.now() + }; + setMessages((prev) => [...prev, userMessage]); + + try { + const result = await makeApiCall(userInput); + // Update the message with the response + setMessages((prev) => + prev.map((msg) => + msg.id === userMessage.id ? { ...msg, response: result } : msg + ) + ); + } catch (error) { + console.error('Error:', error); + const errorResponse: Response = { + naturalResponse: 'An error occurred while processing your request.', + commandResults: [], + fileOperations: [] + }; + setMessages((prev) => + prev.map((msg) => + msg.id === userMessage.id ? { ...msg, response: errorResponse } : msg + ) + ); + } finally { + setLoading(false); + } + }; + + const clearHistory = () => { + if (confirm('Are you sure you want to clear all history?')) { + setMessages([]); + localStorage.removeItem(STORAGE_KEY); + } + }; + + const renderMessage = (message: Message) => ( +
+
+
You:
+
{message.input}
+
+ + {message.response ? ( +
+
+
+
+
Response:
+ {message.response.naturalResponse ? ( +
+ {message.response.naturalResponse} +
+ ) : ( +
No response received.
+ )} +
+
+ +
+ {(() => { + // Combine and sort all results by timestamp + const allResults = [ + ...message.response.commandResults.map((r) => ({ + type: 'command' as const, + ...r + })), + ...(message.response.fileOperations || []).map((r) => ({ + type: 'file' as const, + ...r + })) + ].sort((a, b) => a.timestamp - b.timestamp); + + if (allResults.length === 0) { + return ( +
+
Results:
+
+ No operations performed. +
+
+ ); + } + + return ( +
+
Results:
+ {allResults.map((result, index) => { + const timestamp = new Date( + result.timestamp + ).toLocaleTimeString(); + + if (result.type === 'command') { + return ( +
+
+
+ $ {result.command} +
+
{timestamp}
+
+ {result.stdout && ( +
{result.stdout}
+ )} + {result.stderr && ( +
{result.stderr}
+ )} + {result.exitCode !== null && + result.exitCode !== 0 && ( +
+ Exit code: {result.exitCode} +
+ )} +
+ ); + } else { + return ( +
+
+
+ {result.operation === 'create' && '📄 Create'} + {result.operation === 'update' && '✏️ Update'} + {result.operation === 'delete' && + '🗑️ Delete'}{' '} + {result.path} +
+
{timestamp}
+
+
+ {result.output} +
+ {result.error && ( +
+ Error: {result.error} +
+ )} +
+ ); + } + })} +
+ ); + })()} +
+
+
+ ) : ( +
+
Processing...
+
+ )} +
+ ); + + return ( +
+
+
+

Sandbox Studio

+ {messages.length > 0 && ( + + )} +
+ +
+ {messages.length === 0 ? ( +
+
+ Start a conversation by entering a command below. +
+
+ ) : ( + messages.map(renderMessage) + )} +
+
+ +
+ setInput(e.target.value)} + disabled={loading} + /> +
+
+
+ ); +} + +createRoot(document.getElementById('root')!).render(); diff --git a/examples/openai-agents/src/index.css b/examples/openai-agents/src/index.css new file mode 100644 index 00000000..0bb3ccd6 --- /dev/null +++ b/examples/openai-agents/src/index.css @@ -0,0 +1,313 @@ +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', + 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: #ffffff; + color: #1a1a1a; +} + +.app { + min-height: 100vh; + display: flex; + flex-direction: column; + padding: 0; +} + +.container { + width: 100%; + max-width: 1200px; + margin: 0 auto; + height: 100vh; + display: flex; + flex-direction: column; + padding: 1rem; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + padding-bottom: 1rem; + border-bottom: 1px solid #d0d0d0; +} + +.app-title { + margin: 0; + font-size: 1.5rem; + font-weight: 600; + color: #1a1a1a; +} + +.clear-button { + padding: 0.5rem 1rem; + font-size: 0.9rem; + background-color: #f5f5f5; + color: #1a1a1a; + border: 1px solid #808080; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.2s; +} + +.clear-button:hover { + background-color: #e0e0e0; +} + +.messages-container { + flex: 1; + overflow-y: auto; + margin-bottom: 1rem; + padding-right: 0.5rem; +} + +.messages-container::-webkit-scrollbar { + width: 8px; +} + +.messages-container::-webkit-scrollbar-track { + background: #f5f5f5; + border-radius: 4px; +} + +.messages-container::-webkit-scrollbar-thumb { + background: #808080; + border-radius: 4px; +} + +.messages-container::-webkit-scrollbar-thumb:hover { + background: #404040; +} + +.empty-state { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + padding: 2rem; +} + +.message { + margin-bottom: 2rem; +} + +.message:last-child { + margin-bottom: 0; +} + +.message-input { + margin-bottom: 1rem; + padding: 0.75rem; + background-color: #f0f0f0; + border-radius: 4px; +} + +.message-label { + font-weight: 600; + color: #404040; + margin-bottom: 0.5rem; + font-size: 0.9rem; +} + +.message-content { + color: #1a1a1a; + white-space: pre-wrap; + word-wrap: break-word; + line-height: 1.5; +} + +.message-response { + margin-left: 1rem; +} + +.input-form { + margin-top: auto; + padding-top: 1rem; + border-top: 1px solid #d0d0d0; +} + +.input { + width: 100%; + padding: 0.75rem 1rem; + font-size: 1rem; + border: 1px solid #808080; + border-radius: 4px; + background-color: #ffffff; + color: #1a1a1a; + outline: none; + transition: border-color 0.2s; +} + +.input:focus { + border-color: #404040; +} + +.input:disabled { + background-color: #f5f5f5; + cursor: not-allowed; +} + +.input::placeholder { + color: #808080; +} + +.output { + border: 1px solid #808080; + border-radius: 4px; + background-color: #fafafa; + padding: 1rem; + min-height: 100px; +} + +.output-columns { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; +} + +.output-column { + display: flex; + flex-direction: column; +} + +.output-column-left { + border-right: 1px solid #d0d0d0; + padding-right: 1.5rem; + max-height: 400px; + overflow-y: auto; +} + +.output-column-left::-webkit-scrollbar { + width: 6px; +} + +.output-column-left::-webkit-scrollbar-track { + background: #f5f5f5; + border-radius: 4px; +} + +.output-column-left::-webkit-scrollbar-thumb { + background: #808080; + border-radius: 4px; +} + +.output-column-left::-webkit-scrollbar-thumb:hover { + background: #404040; +} + +.output-column-right { + padding-left: 1.5rem; +} + +@media (max-width: 768px) { + .output-columns { + grid-template-columns: 1fr; + } + + .output-column-left { + border-right: none; + border-bottom: 1px solid #d0d0d0; + padding-right: 0; + padding-bottom: 1.5rem; + margin-bottom: 1.5rem; + } + + .output-column-right { + padding-left: 0; + } +} + +.output-section { + margin-bottom: 1.5rem; +} + +.output-section:last-child { + margin-bottom: 0; +} + +.output-label { + font-weight: 600; + color: #1a1a1a; + margin-bottom: 0.5rem; + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.output-content { + color: #1a1a1a; + white-space: pre-wrap; + word-wrap: break-word; + line-height: 1.5; +} + +.tool-result { + margin-bottom: 1rem; + padding: 0.75rem; + background-color: #ffffff; + border: 1px solid #d0d0d0; + border-radius: 4px; +} + +.tool-result:last-child { + margin-bottom: 0; +} + +.tool-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.tool-command { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 0.9rem; + color: #404040; + font-weight: 600; + flex: 1; +} + +.tool-timestamp { + font-size: 0.75rem; + color: #808080; + margin-left: 0.5rem; + white-space: nowrap; +} + +.tool-output { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 0.85rem; + color: #1a1a1a; + white-space: pre-wrap; + word-wrap: break-word; + margin-top: 0.5rem; + padding-left: 0.5rem; + border-left: 2px solid #808080; +} + +.tool-error { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 0.85rem; + color: #404040; + white-space: pre-wrap; + word-wrap: break-word; + margin-top: 0.5rem; + padding-left: 0.5rem; + border-left: 2px solid #808080; +} + +.tool-exit-code { + font-size: 0.8rem; + color: #808080; + margin-top: 0.5rem; + font-style: italic; +} diff --git a/examples/openai-agents/src/index.ts b/examples/openai-agents/src/index.ts new file mode 100644 index 00000000..21da90a4 --- /dev/null +++ b/examples/openai-agents/src/index.ts @@ -0,0 +1,190 @@ +import { getSandbox, Sandbox } from '@cloudflare/sandbox'; +import { Editor, Shell } from '@cloudflare/sandbox/openai'; +export { Sandbox }; // export the Sandbox class for the worker + +import { Agent, applyPatchTool, run, shellTool } from '@openai/agents'; + +// Helper functions for error handling +function isErrorWithProperties(error: unknown): error is { + message?: string; + exitCode?: number; + stdout?: string; + stderr?: string; + status?: number; + stack?: string; +} { + return typeof error === 'object' && error !== null; +} + +function getErrorMessage(error: unknown): string { + if (isErrorWithProperties(error) && typeof error.message === 'string') { + return error.message; + } + return String(error); +} + +function getErrorStack(error: unknown): string | undefined { + if (isErrorWithProperties(error) && typeof error.stack === 'string') { + return error.stack; + } + return undefined; +} + +async function handleRunRequest( + request: Request, + env: Env, + sessionId: string +): Promise { + console.debug('[openai-example]', 'handleRunRequest called', { + method: request.method, + url: request.url + }); + + try { + // Parse request body + console.debug('[openai-example]', 'Parsing request body'); + const body = (await request.json()) as { input?: string }; + const input = body.input; + + if (!input || typeof input !== 'string') { + console.warn('[openai-example]', 'Invalid or missing input field', { + input + }); + return new Response( + JSON.stringify({ error: 'Missing or invalid input field' }), + { status: 400, headers: { 'Content-Type': 'application/json' } } + ); + } + + console.info('[openai-example]', 'Processing request', { + inputLength: input.length + }); + + // Get sandbox instance (reused for both shell and editor) + console.debug('[openai-example]', 'Getting sandbox instance', { + sandboxId: `session-${sessionId}` + }); + const sandbox = getSandbox(env.Sandbox, `session-${sessionId}`); + + // Create shell (automatically collects results) + console.debug('[openai-example]', 'Creating SandboxShell'); + const shell = new Shell(sandbox); + + // Create workspace editor + console.debug('[openai-example]', 'Creating WorkspaceEditor', { + root: '/workspace' + }); + const editor = new Editor(sandbox, '/workspace'); + + // Create agent with both shell and patch tools, auto-approval for web API + console.debug('[openai-example]', 'Creating Agent', { + name: 'Sandbox Studio', + model: 'gpt-5.1' + }); + const agent = new Agent({ + name: 'Sandbox Studio', + model: 'gpt-5.1', + instructions: + 'You can execute shell commands and edit files in the workspace. Use shell commands to inspect the repository and the apply_patch tool to create, update, or delete files. Keep responses concise and include command output when helpful.', + tools: [ + shellTool({ + shell, + needsApproval: false // Auto-approve for web API + }), + applyPatchTool({ + editor, + needsApproval: false // Auto-approve for web API + }) + ] + }); + + // Run the agent + console.info('[openai-example]', 'Running agent', { input }); + const result = await run(agent, input); + console.debug('[openai-example]', 'Agent run completed', { + hasOutput: !!result.finalOutput, + outputLength: result.finalOutput?.length || 0 + }); + + // Combine and sort all results by timestamp for logging + const allResults = [ + ...shell.results.map((r) => ({ type: 'command' as const, ...r })), + ...editor.results.map((r) => ({ type: 'file' as const, ...r })) + ].sort((a, b) => a.timestamp - b.timestamp); + + console.debug('[openai-example]', 'Results collected', { + commandResults: shell.results.length, + fileOperations: editor.results.length, + totalResults: allResults.length + }); + + // Format response with combined and sorted results + const response = { + naturalResponse: result.finalOutput || null, + commandResults: shell.results.sort((a, b) => a.timestamp - b.timestamp), + fileOperations: editor.results.sort((a, b) => a.timestamp - b.timestamp) + }; + + console.info('[openai-example]', 'Request completed successfully'); + return new Response(JSON.stringify(response), { + headers: { + 'Content-Type': 'application/json' + } + }); + } catch (error: unknown) { + const errorMessage = getErrorMessage(error); + const errorStack = getErrorStack(error); + console.error('[openai-example]', 'Error handling run request', { + error: errorMessage, + stack: errorStack + }); + return new Response( + JSON.stringify({ + error: errorMessage || 'Internal server error', + naturalResponse: 'An error occurred while processing your request.', + commandResults: [], + fileOperations: [] + }), + { + status: 500, + headers: { + 'Content-Type': 'application/json' + } + } + ); + } +} + +export default { + async fetch( + request: Request, + env: Env, + _ctx: ExecutionContext + ): Promise { + const url = new URL(request.url); + console.debug('[openai-example]', 'Fetch handler called', { + pathname: url.pathname, + method: request.method + }); + + const sessionId = request.headers.get('X-Session-Id'); + console.log({ sessionId }); + if (!sessionId) { + return new Response('Missing X-Session-Id header', { status: 400 }); + } + + if (url.pathname === '/.well-known/appspecific/com.chrome.devtools.json') { + return Response.json({}); + } + + if (url.pathname === '/run' && request.method === 'POST') { + return handleRunRequest(request, env, sessionId); + } + + console.warn('[openai-example]', 'Route not found', { + pathname: url.pathname, + method: request.method + }); + return new Response('Not found', { status: 404 }); + } +}; diff --git a/examples/openai-agents/tsconfig.json b/examples/openai-agents/tsconfig.json new file mode 100644 index 00000000..f06dd47c --- /dev/null +++ b/examples/openai-agents/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["esnext", "dom", "dom.iterable"], + "module": "esnext", + "moduleResolution": "bundler", + "types": ["@types/node", "@cloudflare/workers-types"], + "jsx": "react-jsx", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "noEmit": true + } +} diff --git a/examples/openai-agents/vite.config.ts b/examples/openai-agents/vite.config.ts new file mode 100644 index 00000000..a32e36dc --- /dev/null +++ b/examples/openai-agents/vite.config.ts @@ -0,0 +1,7 @@ +import { cloudflare } from '@cloudflare/vite-plugin'; +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [react(), cloudflare()] +}); diff --git a/examples/openai-agents/wrangler.jsonc b/examples/openai-agents/wrangler.jsonc new file mode 100644 index 00000000..b16e5526 --- /dev/null +++ b/examples/openai-agents/wrangler.jsonc @@ -0,0 +1,35 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "sandbox-openai-agents-example", + "main": "src/index.ts", + "compatibility_date": "2025-11-15", + "compatibility_flags": ["nodejs_compat"], + "observability": { + "enabled": true + }, + "assets": { + "directory": "public" + }, + "containers": [ + { + "class_name": "Sandbox", + "image": "./Dockerfile", + "instance_type": "lite", + "max_instances": 1 + } + ], + "durable_objects": { + "bindings": [ + { + "class_name": "Sandbox", + "name": "Sandbox" + } + ] + }, + "migrations": [ + { + "new_sqlite_classes": ["Sandbox"], + "tag": "v1" + } + ] +} diff --git a/examples/typescript-validator/index.html b/examples/typescript-validator/index.html index 1dd5e4f6..ff0dff72 100644 --- a/examples/typescript-validator/index.html +++ b/examples/typescript-validator/index.html @@ -1,4 +1,4 @@ - + diff --git a/examples/typescript-validator/src/index.css b/examples/typescript-validator/src/index.css index 49b01f39..b4c2c93f 100644 --- a/examples/typescript-validator/src/index.css +++ b/examples/typescript-validator/src/index.css @@ -1,4 +1,4 @@ -@import "tailwindcss"; +@import 'tailwindcss'; @theme { /* Cloudflare Accent Colors */ @@ -31,14 +31,15 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', + 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background-color: var(--color-bg-cream); } -code, pre { +code, +pre { font-family: 'Monaco', 'Courier New', 'Courier', monospace; } diff --git a/examples/typescript-validator/wrangler.jsonc b/examples/typescript-validator/wrangler.jsonc index f42d6475..d993649f 100644 --- a/examples/typescript-validator/wrangler.jsonc +++ b/examples/typescript-validator/wrangler.jsonc @@ -14,7 +14,7 @@ "containers": [ { "class_name": "Sandbox", - "image": "./Dockerfile", + "image": "./Dockerfile" } ], "durable_objects": { diff --git a/package-lock.json b/package-lock.json index 21fa0c7d..62d521cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,6 +97,24 @@ "wrangler": "4.47.0" } }, + "examples/openai-agents": { + "name": "@cloudflare/sandbox-openai-agents-example", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@openai/agents": "^0.3.2", + "nanoid": "^3.3.11", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@cloudflare/vite-plugin": "^1.14.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "vite": "^7.2.2" + } + }, "examples/typescript-validator": { "name": "@cloudflare/sandbox-typescript-validator-example", "version": "1.0.0", @@ -200,6 +218,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -977,6 +996,10 @@ "resolved": "examples/minimal", "link": true }, + "node_modules/@cloudflare/sandbox-openai-agents-example": { + "resolved": "examples/openai-agents", + "link": true + }, "node_modules/@cloudflare/sandbox-typescript-validator-example": { "resolved": "examples/typescript-validator", "link": true @@ -1693,6 +1716,7 @@ "integrity": "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", @@ -1826,7 +1850,8 @@ "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20251115.0.tgz", "integrity": "sha512-aM7jp7IfKhqKvfSaK1IhVTbSzxB6KQ4gX8e/W29tOuZk+YHlYXuRd/bMm4hWkfd7B1HWNWdsx1GTaEUoZIuVsw==", "dev": true, - "license": "MIT OR Apache-2.0" + "license": "MIT OR Apache-2.0", + "peer": true }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", @@ -2878,6 +2903,39 @@ "node": ">=6 <7 || >=8" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.22.0.tgz", + "integrity": "sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + } + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", @@ -3004,6 +3062,7 @@ "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -3211,6 +3270,72 @@ "@octokit/openapi-types": "^20.0.0" } }, + "node_modules/@openai/agents": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@openai/agents/-/agents-0.3.2.tgz", + "integrity": "sha512-iMy5d6T0K6kxsPuPl3icAGifVb/7QpvxgXP5LrnPa7JTwKDCCe1XMuOJts5Zi1/JNbHpxWk4HCZ908s2auQSoA==", + "license": "MIT", + "dependencies": { + "@openai/agents-core": "0.3.2", + "@openai/agents-openai": "0.3.2", + "@openai/agents-realtime": "0.3.2", + "debug": "^4.4.0", + "openai": "^6" + }, + "peerDependencies": { + "zod": "^3.25.40 || ^4.0" + } + }, + "node_modules/@openai/agents-core": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@openai/agents-core/-/agents-core-0.3.2.tgz", + "integrity": "sha512-lt8KxmaDFXbGkQKxtuJz79arBvbN/BGvZhjLnVPEGC0p0WoUxGbq2HKG5NX3rajBfZZGA27aTGvYQirSXIUpUg==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "openai": "^6" + }, + "optionalDependencies": { + "@modelcontextprotocol/sdk": "^1.17.2" + }, + "peerDependencies": { + "zod": "^3.25.40 || ^4.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@openai/agents-openai": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@openai/agents-openai/-/agents-openai-0.3.2.tgz", + "integrity": "sha512-Qg0HixgR9pMzQix0x2gVigfx3S2GAWG/akYjSSMDcan4NTd/luGoiJ/ehDNODMhCAudhwM+ciSfwM9AbhJDKmg==", + "license": "MIT", + "dependencies": { + "@openai/agents-core": "0.3.2", + "debug": "^4.4.0", + "openai": "^6" + }, + "peerDependencies": { + "zod": "^3.25.40 || ^4.0" + } + }, + "node_modules/@openai/agents-realtime": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@openai/agents-realtime/-/agents-realtime-0.3.2.tgz", + "integrity": "sha512-T8tOWuMDsk+TsoD0VAzeKDazopEIpGhrpeNunEpIsgIN9wXGI96f2im+BIkPWvPDuaztTmkOul8Zbx1vWhPV5g==", + "license": "MIT", + "dependencies": { + "@openai/agents-core": "0.3.2", + "@types/ws": "^8.18.1", + "debug": "^4.4.0", + "ws": "^8.18.1" + }, + "peerDependencies": { + "zod": "^3.25.40 || ^4.0" + } + }, "node_modules/@oxc-project/runtime": { "version": "0.97.0", "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.97.0.tgz", @@ -4284,7 +4409,6 @@ "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -4301,6 +4425,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.5.tgz", "integrity": "sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -4342,7 +4467,6 @@ "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -4438,6 +4562,7 @@ "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", @@ -4453,6 +4578,7 @@ "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", @@ -4481,6 +4607,7 @@ "integrity": "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/utils": "3.2.4", "fflate": "^0.8.2", @@ -4512,6 +4639,20 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "optional": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -4535,6 +4676,41 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -4669,6 +4845,40 @@ "dev": true, "license": "MIT" }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "optional": true, + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -4702,6 +4912,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -4729,6 +4940,16 @@ "@types/react": "^19" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -4739,6 +4960,37 @@ "node": ">=8" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-me-maybe": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", @@ -4970,6 +5222,29 @@ "dev": true, "license": "MIT" }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4987,11 +5262,35 @@ "node": ">=18" } }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "optional": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -5072,6 +5371,16 @@ "dev": true, "license": "MIT" }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -5182,6 +5491,28 @@ } } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT", + "optional": true + }, "node_modules/electron-to-chromium": { "version": "1.5.243", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.243.tgz", @@ -5199,6 +5530,16 @@ "node": ">=14" } }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -5237,6 +5578,26 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -5244,6 +5605,19 @@ "dev": true, "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.25.11", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", @@ -5296,6 +5670,13 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT", + "optional": true + }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -5342,6 +5723,39 @@ "@types/estree": "^1.0.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "optional": true, + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/exit-hook": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", @@ -5365,18 +5779,87 @@ "node": ">=12.0.0" } }, - "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "dev": true, - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "optional": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "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.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" }, "node_modules/extendable-error": { "version": "0.1.7", @@ -5385,6 +5868,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT", + "optional": true + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -5402,6 +5892,23 @@ "node": ">=8.6.0" } }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -5445,6 +5952,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -5466,6 +5991,26 @@ "dev": true, "license": "ISC" }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -5496,6 +6041,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5506,6 +6061,31 @@ "node": ">=6.9.0" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "optional": true, + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-port": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", @@ -5519,6 +6099,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "optional": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-tsconfig": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", @@ -5573,6 +6167,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -5612,6 +6219,32 @@ "dev": true, "license": "MIT" }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -5669,6 +6302,33 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/human-id": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.2.tgz", @@ -5683,7 +6343,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -5706,12 +6366,29 @@ "node": ">= 4" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC", + "optional": true + }, "node_modules/inline-style-parser": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", "license": "MIT" }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -5808,6 +6485,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT", + "optional": true + }, "node_modules/is-subdir": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", @@ -5848,7 +6532,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/jiti": { @@ -5894,6 +6578,13 @@ "node": ">=6" } }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT", + "optional": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -6149,7 +6840,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6171,7 +6861,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6193,7 +6882,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6215,7 +6903,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6237,7 +6924,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6259,7 +6945,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6281,7 +6966,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6303,7 +6987,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6325,7 +7008,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6347,7 +7029,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6369,7 +7050,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6413,7 +7093,6 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -6458,6 +7137,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", @@ -6728,6 +7417,29 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -7328,6 +8040,29 @@ "node": ">=10.0.0" } }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "optional": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/miniflare": { "version": "4.20251011.1", "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20251011.1.tgz", @@ -7443,7 +8178,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -7458,6 +8192,16 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -7491,11 +8235,23 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obug": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/obug/-/obug-1.0.0.tgz", @@ -7522,11 +8278,24 @@ "dev": true, "license": "MIT" }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "optional": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -7657,6 +8426,16 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7671,7 +8450,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -7741,6 +8520,16 @@ "node": ">=6" } }, + "node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/pkg-pr-new": { "version": "0.0.60", "resolved": "https://registry.npmjs.org/pkg-pr-new/-/pkg-pr-new-0.0.60.tgz", @@ -7853,6 +8642,36 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "optional": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/quansync": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", @@ -7940,11 +8759,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7965,8 +8811,7 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-katex": { "version": "3.1.0", @@ -8114,6 +8959,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -8151,6 +9006,7 @@ "integrity": "sha512-JFULvCNl/anKn99eKjOSEubi0lLmNqQDAjyEMME2T4CwezUDL0i6t1O9xZsu2OMehPnV2caNefWpGF+8TnzB6A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@oxc-project/types": "=0.97.0", "@rolldown/pluginutils": "1.0.0-beta.50" @@ -8282,6 +9138,34 @@ "fsevents": "~2.3.2" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8306,11 +9190,32 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/scheduler": { @@ -8332,6 +9237,52 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC", + "optional": true + }, "node_modules/sharp": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", @@ -8376,7 +9327,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -8389,7 +9340,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -8527,6 +9478,82 @@ "win32" ] }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -8640,6 +9667,16 @@ "dev": true, "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", @@ -8848,6 +9885,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8898,6 +9936,16 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -9020,6 +10068,7 @@ "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -9156,6 +10205,21 @@ "node": ">=4" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "optional": true, + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -9205,7 +10269,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, "license": "MIT" }, "node_modules/unenv": { @@ -9214,6 +10277,7 @@ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "pathe": "^2.0.3" } @@ -9322,6 +10386,16 @@ "node": ">= 4.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unrun": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/unrun/-/unrun-0.2.9.tgz", @@ -9401,6 +10475,16 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -9435,6 +10519,7 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -9551,6 +10636,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -9564,6 +10650,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -9683,7 +10770,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -9719,6 +10806,7 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "bin": { "workerd": "bin/workerd" }, @@ -10369,6 +11457,7 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "bin": { "workerd": "bin/workerd" }, @@ -10419,14 +11508,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -10481,6 +11569,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -10498,6 +11587,16 @@ "node": ">=20" } }, + "node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "license": "ISC", + "optional": true, + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", @@ -10516,7 +11615,16 @@ "@cloudflare/containers": "^0.0.30" }, "devDependencies": { + "@openai/agents": "^0.3.2", "@repo/shared": "*" + }, + "peerDependencies": { + "@openai/agents": "^0.3.2" + }, + "peerDependenciesMeta": { + "@openai/agents": { + "optional": true + } } }, "packages/sandbox-container": { diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index b5f6acc1..41a9d66d 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -11,8 +11,17 @@ "@cloudflare/containers": "^0.0.30" }, "devDependencies": { + "@openai/agents": "^0.3.2", "@repo/shared": "*" }, + "peerDependencies": { + "@openai/agents": "^0.3.2" + }, + "peerDependenciesMeta": { + "@openai/agents": { + "optional": true + } + }, "tags": [ "sandbox", "codegen", @@ -36,6 +45,11 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.js" + }, + "./openai": { + "types": "./dist/openai/index.d.ts", + "import": "./dist/openai/index.js", + "require": "./dist/openai/index.js" } }, "keywords": [], diff --git a/packages/sandbox/src/openai/index.ts b/packages/sandbox/src/openai/index.ts new file mode 100644 index 00000000..a2b5311b --- /dev/null +++ b/packages/sandbox/src/openai/index.ts @@ -0,0 +1,455 @@ +/** + * OpenAI Agents adapters for executing shell commands and file operations + * inside a Cloudflare Sandbox. + */ +import { + type ApplyPatchOperation, + type ApplyPatchResult, + applyDiff, + type Editor as OpenAIEeditor, + type Shell as OpenAIShell, + type ShellAction, + type ShellOutputResult, + type ShellResult +} from '@openai/agents'; + +// Command result for API responses +export interface CommandResult { + command: string; + stdout: string; + stderr: string; + exitCode: number | null; + timestamp: number; +} + +// File operation result for API responses +export interface FileOperationResult { + operation: 'create' | 'update' | 'delete'; + path: string; + status: 'completed' | 'failed'; + output: string; + error?: string; + timestamp: number; +} + +import { createLogger, type Logger } from '@repo/shared'; +import type { Sandbox } from '../sandbox'; + +const logger: Logger = createLogger({ + component: 'sandbox-do', + operation: 'openai-agent' +}); + +// Helper functions for error handling +function isErrorWithProperties(error: unknown): error is { + message?: string; + exitCode?: number; + stdout?: string; + stderr?: string; + status?: number; + stack?: string; +} { + return typeof error === 'object' && error !== null; +} + +function getErrorMessage(error: unknown): string { + if (isErrorWithProperties(error) && typeof error.message === 'string') { + return error.message; + } + return String(error); +} + +/** + * Convert unknown values to Error instances when possible so downstream + * loggers can include stack traces without losing type safety. + */ +function toError(error: unknown): Error | undefined { + return error instanceof Error ? error : undefined; +} + +/** + * Shell implementation that adapts Cloudflare Sandbox exec calls to the + * OpenAI Agents `Shell` contract, including structured result collection. + */ +export class Shell implements OpenAIShell { + private cwd: string = '/workspace'; + public results: CommandResult[] = []; + + constructor(private readonly sandbox: Sandbox) {} + + async run(action: ShellAction): Promise { + logger.debug('SandboxShell.run called', { + commands: action.commands, + timeout: action.timeoutMs + }); + const output: ShellResult['output'] = []; + + for (const command of action.commands) { + logger.debug('Executing command', { command, cwd: this.cwd }); + let stdout = ''; + let stderr = ''; + let exitCode: number | null = 0; + let outcome: ShellOutputResult['outcome'] = { + type: 'exit', + exitCode: 0 + }; + try { + const result = await this.sandbox.exec(command, { + timeout: action.timeoutMs, + cwd: this.cwd + }); + stdout = result.stdout; + stderr = result.stderr; + exitCode = result.exitCode; + // exec returns a result even for failed commands, so check success field + // Timeout would be indicated by a specific error or exit code + outcome = { type: 'exit', exitCode }; + + logger.debug('Command executed successfully', { + command, + exitCode, + stdoutLength: stdout.length, + stderrLength: stderr.length + }); + + // Log warnings for non-zero exit codes or stderr output + if (exitCode !== 0) { + logger.warn(`Command failed with exit code ${exitCode}`, { + command, + stderr + }); + } else if (stderr) { + logger.warn(`Command produced stderr output`, { command, stderr }); + } else { + logger.info(`Command completed successfully`, { command }); + } + } catch (error: unknown) { + // Handle network/HTTP errors or timeout errors + const errorObj = isErrorWithProperties(error) ? error : {}; + exitCode = + typeof errorObj.exitCode === 'number' ? errorObj.exitCode : null; + stdout = typeof errorObj.stdout === 'string' ? errorObj.stdout : ''; + stderr = typeof errorObj.stderr === 'string' ? errorObj.stderr : ''; + + // Check if it's a timeout error + const errorMessage = getErrorMessage(error); + if ( + errorMessage.includes('timeout') || + errorMessage.includes('Timeout') || + errorMessage.includes('timed out') + ) { + logger.error(`Command timed out`, undefined, { + command, + timeout: action.timeoutMs + }); + outcome = { type: 'timeout' }; + } else { + logger.error(`Error executing command`, toError(error), { + command, + error: errorMessage || error, + exitCode + }); + outcome = { type: 'exit', exitCode: exitCode ?? 1 }; + } + } + output.push({ + command, + stdout, + stderr, + outcome + }); + + // Collect results for API responses + const collectedExitCode = + outcome.type === 'exit' ? outcome.exitCode : null; + const timestamp = Date.now(); + this.results.push({ + command: String(command), + stdout: String(stdout), + stderr: String(stderr), + exitCode: collectedExitCode, + timestamp + }); + logger.debug('Result collected', { + command, + exitCode: collectedExitCode, + timestamp + }); + + if (outcome.type === 'timeout') { + logger.warn('Breaking command loop due to timeout'); + break; + } + } + + logger.debug('SandboxShell.run completed', { + totalCommands: action.commands.length, + resultsCount: this.results.length + }); + return { + output, + providerData: { + working_directory: this.cwd + } + }; + } +} + +/** + * Editor implementation that projects applyPatch operations from Agents + * into calls against the sandbox filesystem APIs. + */ +export class Editor implements OpenAIEeditor { + public results: FileOperationResult[] = []; + + constructor( + private readonly sandbox: Sandbox, + private readonly root: string = '/workspace' + ) {} + + /** + * Create a new file inside the sandbox by applying the provided diff. + */ + async createFile( + operation: Extract + ): Promise { + const targetPath = this.resolve(operation.path); + logger.debug('WorkspaceEditor.createFile called', { + path: operation.path, + targetPath + }); + + try { + // Create parent directory if needed + const dirPath = this.getDirname(targetPath); + if (dirPath !== this.root && dirPath !== '/') { + logger.debug('Creating parent directory', { dirPath }); + await this.sandbox.mkdir(dirPath, { recursive: true }); + } + + const content = applyDiff('', operation.diff, 'create'); + logger.debug('Writing file content', { + path: targetPath, + contentLength: content.length + }); + await this.sandbox.writeFile(targetPath, content, { encoding: 'utf-8' }); + const timestamp = Date.now(); + const result: FileOperationResult = { + operation: 'create', + path: operation.path, + status: 'completed', + output: `Created ${operation.path}`, + timestamp + }; + this.results.push(result); + logger.info('File created successfully', { + path: operation.path, + timestamp + }); + return { status: 'completed', output: `Created ${operation.path}` }; + } catch (error: unknown) { + const timestamp = Date.now(); + const errorMessage = getErrorMessage(error); + const result: FileOperationResult = { + operation: 'create', + path: operation.path, + status: 'failed', + output: `Failed to create ${operation.path}`, + error: errorMessage, + timestamp + }; + this.results.push(result); + logger.error('Failed to create file', toError(error), { + path: operation.path, + error: errorMessage + }); + throw error; + } + } + + /** + * Update an existing file by reading its content, applying a diff, and + * writing the patched output back to the sandbox. + */ + async updateFile( + operation: Extract + ): Promise { + const targetPath = this.resolve(operation.path); + logger.debug('WorkspaceEditor.updateFile called', { + path: operation.path, + targetPath + }); + + try { + let original: string; + try { + logger.debug('Reading original file', { path: targetPath }); + const fileInfo = await this.sandbox.readFile(targetPath, { + encoding: 'utf-8' + }); + original = fileInfo.content; + logger.debug('Original file read', { + path: targetPath, + originalLength: original.length + }); + } catch (error: unknown) { + // Sandbox API may throw errors for missing files + const errorObj = isErrorWithProperties(error) ? error : {}; + const errorMessage = getErrorMessage(error); + if ( + errorMessage.includes('not found') || + errorMessage.includes('ENOENT') || + errorObj.status === 404 + ) { + logger.error('Cannot update missing file', undefined, { + path: operation.path + }); + throw new Error(`Cannot update missing file: ${operation.path}`); + } + logger.error('Error reading file', toError(error), { + path: operation.path, + error: errorMessage + }); + throw error; + } + + const patched = applyDiff(original, operation.diff); + logger.debug('Applied diff', { + path: targetPath, + originalLength: original.length, + patchedLength: patched.length + }); + await this.sandbox.writeFile(targetPath, patched, { encoding: 'utf-8' }); + const timestamp = Date.now(); + const result: FileOperationResult = { + operation: 'update', + path: operation.path, + status: 'completed', + output: `Updated ${operation.path}`, + timestamp + }; + this.results.push(result); + logger.info('File updated successfully', { + path: operation.path, + timestamp + }); + return { status: 'completed', output: `Updated ${operation.path}` }; + } catch (error: unknown) { + const timestamp = Date.now(); + const errorMessage = getErrorMessage(error); + const result: FileOperationResult = { + operation: 'update', + path: operation.path, + status: 'failed', + output: `Failed to update ${operation.path}`, + error: errorMessage, + timestamp + }; + this.results.push(result); + logger.error('Failed to update file', toError(error), { + path: operation.path, + error: errorMessage + }); + throw error; + } + } + + /** + * Delete a file that was previously created through applyPatch calls. + */ + async deleteFile( + operation: Extract + ): Promise { + const targetPath = this.resolve(operation.path); + logger.debug('WorkspaceEditor.deleteFile called', { + path: operation.path, + targetPath + }); + + try { + await this.sandbox.deleteFile(targetPath); + const timestamp = Date.now(); + const result: FileOperationResult = { + operation: 'delete', + path: operation.path, + status: 'completed', + output: `Deleted ${operation.path}`, + timestamp + }; + this.results.push(result); + logger.info('File deleted successfully', { + path: operation.path, + timestamp + }); + return { status: 'completed', output: `Deleted ${operation.path}` }; + } catch (error: unknown) { + const timestamp = Date.now(); + const errorMessage = getErrorMessage(error); + const result: FileOperationResult = { + operation: 'delete', + path: operation.path, + status: 'failed', + output: `Failed to delete ${operation.path}`, + error: errorMessage, + timestamp + }; + this.results.push(result); + logger.error('Failed to delete file', toError(error), { + path: operation.path, + error: errorMessage + }); + throw error; + } + } + + private resolve(relativePath: string): string { + // If the path already starts with the root, strip it to get the relative part + let pathToProcess = relativePath; + if (relativePath.startsWith(this.root)) { + pathToProcess = relativePath.slice(this.root.length); + // Remove leading slash if present after stripping root + pathToProcess = pathToProcess.replace(/^\//, ''); + } + + // Remove leading ./ or / if present, then join with root + const normalized = pathToProcess.replace(/^\.\//, '').replace(/^\//, ''); + const resolved = normalized ? `${this.root}/${normalized}` : this.root; + + // Normalize path separators first + const pathWithNormalizedSeparators = resolved.replace(/\/+/g, '/'); + + // Normalize .. segments by processing path segments + const segments = pathWithNormalizedSeparators + .split('/') + .filter((s) => s && s !== '.'); + const stack: string[] = []; + + for (const segment of segments) { + if (segment === '..') { + if (stack.length === 0) { + throw new Error(`Operation outside workspace: ${relativePath}`); + } + stack.pop(); + } else { + stack.push(segment); + } + } + + const normalizedPath = `/${stack.join('/')}`; + + // Ensure the resolved path is within the workspace + if (!normalizedPath.startsWith(this.root)) { + throw new Error(`Operation outside workspace: ${relativePath}`); + } + + return normalizedPath; + } + + private getDirname(filePath: string): string { + const lastSlash = filePath.lastIndexOf('/'); + if (lastSlash === -1) { + return '/'; + } + return filePath.substring(0, lastSlash) || '/'; + } +} diff --git a/packages/sandbox/tests/openai-shell-editor.test.ts b/packages/sandbox/tests/openai-shell-editor.test.ts new file mode 100644 index 00000000..7df23f54 --- /dev/null +++ b/packages/sandbox/tests/openai-shell-editor.test.ts @@ -0,0 +1,434 @@ +import type { ApplyPatchOperation } from '@openai/agents'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { Editor, Shell } from '../src/openai/index.ts'; +import type { Sandbox } from '../src/sandbox.ts'; + +interface MockSandbox { + exec?: ReturnType; + mkdir?: ReturnType; + writeFile?: ReturnType; + readFile?: ReturnType; + deleteFile?: ReturnType; +} + +const { loggerSpies, createLoggerMock, applyDiffMock } = vi.hoisted(() => { + const logger = { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + child: vi.fn().mockReturnThis() + }; + + return { + loggerSpies: logger, + createLoggerMock: vi.fn(() => logger), + applyDiffMock: vi.fn<(...args: any[]) => string>() + }; +}); + +vi.mock('@repo/shared', () => ({ + createLogger: createLoggerMock +})); + +vi.mock('@openai/agents', () => ({ + applyDiff: applyDiffMock +})); + +describe('Shell', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('runs commands and collects results', async () => { + const execMock = vi.fn().mockResolvedValue({ + stdout: 'hello\n', + stderr: '', + exitCode: 0 + }); + + const mockSandbox: MockSandbox = { exec: execMock }; + const shell = new Shell(mockSandbox as unknown as Sandbox); + + const result = await shell.run({ + commands: ['echo hello'], + timeoutMs: 500 + }); + + expect(execMock).toHaveBeenCalledWith('echo hello', { + timeout: 500, + cwd: '/workspace' + }); + expect(result.output).toHaveLength(1); + expect(result.output[0]).toMatchObject({ + command: 'echo hello', + stdout: 'hello\n', + stderr: '', + outcome: { type: 'exit', exitCode: 0 } + }); + expect(shell.results).toHaveLength(1); + expect(loggerSpies.info).toHaveBeenCalledWith( + 'Command completed successfully', + { command: 'echo hello' } + ); + }); + + it('halts subsequent commands after a timeout error', async () => { + const timeoutError = new Error('Command timed out'); + const execMock = vi.fn().mockRejectedValue(timeoutError); + + const mockSandbox: MockSandbox = { exec: execMock }; + const shell = new Shell(mockSandbox as unknown as Sandbox); + const action = { + commands: ['sleep 1', 'echo never'], + timeoutMs: 25 + }; + + const result = await shell.run(action); + + expect(execMock).toHaveBeenCalledTimes(1); + expect(result.output[0].outcome).toEqual({ type: 'timeout' }); + expect(shell.results[0].exitCode).toBeNull(); + expect(loggerSpies.warn).toHaveBeenCalledWith( + 'Breaking command loop due to timeout' + ); + expect(loggerSpies.error).toHaveBeenCalledWith( + 'Command timed out', + undefined, + expect.objectContaining({ + command: 'sleep 1', + timeout: 25 + }) + ); + }); +}); + +describe('Editor', () => { + beforeEach(() => { + vi.clearAllMocks(); + applyDiffMock.mockReset(); + }); + + it('creates files using applyDiff output', async () => { + applyDiffMock.mockReturnValueOnce('file contents'); + + const mockSandbox: MockSandbox = { + mkdir: vi.fn().mockResolvedValue(undefined), + writeFile: vi.fn().mockResolvedValue(undefined) + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: 'src/app.ts', + diff: '--- diff ---' + } as Extract; + + await editor.createFile(operation); + + expect(applyDiffMock).toHaveBeenCalledWith('', operation.diff, 'create'); + expect(mockSandbox.mkdir).toHaveBeenCalledWith('/workspace/src', { + recursive: true + }); + expect(mockSandbox.writeFile).toHaveBeenCalledWith( + '/workspace/src/app.ts', + 'file contents', + { encoding: 'utf-8' } + ); + expect(editor.results[0]).toMatchObject({ + operation: 'create', + path: 'src/app.ts', + status: 'completed' + }); + expect(loggerSpies.info).toHaveBeenCalledWith( + 'File created successfully', + expect.objectContaining({ path: 'src/app.ts' }) + ); + }); + + it('applies diffs when updating existing files', async () => { + applyDiffMock.mockReturnValueOnce('patched content'); + + const mockSandbox: MockSandbox = { + readFile: vi.fn().mockResolvedValue({ content: 'original content' }), + writeFile: vi.fn().mockResolvedValue(undefined) + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'update_file', + path: 'README.md', + diff: 'patch diff' + } as Extract; + + await editor.updateFile(operation); + + expect(mockSandbox.readFile).toHaveBeenCalledWith('/workspace/README.md', { + encoding: 'utf-8' + }); + expect(applyDiffMock).toHaveBeenCalledWith( + 'original content', + operation.diff + ); + expect(mockSandbox.writeFile).toHaveBeenCalledWith( + '/workspace/README.md', + 'patched content', + { encoding: 'utf-8' } + ); + expect(editor.results[0]).toMatchObject({ + operation: 'update', + path: 'README.md', + status: 'completed' + }); + }); + + it('throws descriptive error when attempting to update a missing file', async () => { + const missingError = Object.assign(new Error('not found'), { status: 404 }); + const mockSandbox: MockSandbox = { + readFile: vi.fn().mockRejectedValue(missingError) + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'update_file', + path: 'missing.txt', + diff: 'patch diff' + } as Extract; + + await expect(editor.updateFile(operation)).rejects.toThrow( + 'Cannot update missing file: missing.txt' + ); + expect(loggerSpies.error).toHaveBeenCalledWith( + 'Cannot update missing file', + undefined, + { path: 'missing.txt' } + ); + }); + + describe('Path traversal security', () => { + it('should reject path traversal attempts with ../', async () => { + const mockSandbox: MockSandbox = { + mkdir: vi.fn(), + writeFile: vi.fn() + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: '../etc/passwd', + diff: 'malicious content' + } as Extract; + + await expect(editor.createFile(operation)).rejects.toThrow( + 'Operation outside workspace: ../etc/passwd' + ); + expect(mockSandbox.writeFile).not.toHaveBeenCalled(); + }); + + it('should reject path traversal attempts with ../../', async () => { + const mockSandbox: MockSandbox = { + mkdir: vi.fn(), + writeFile: vi.fn() + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: '../../etc/passwd', + diff: 'malicious content' + } as Extract; + + await expect(editor.createFile(operation)).rejects.toThrow( + 'Operation outside workspace: ../../etc/passwd' + ); + expect(mockSandbox.writeFile).not.toHaveBeenCalled(); + }); + + it('should reject path traversal attempts with mixed paths like src/../../etc/passwd', async () => { + const mockSandbox: MockSandbox = { + mkdir: vi.fn(), + writeFile: vi.fn() + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: 'src/../../etc/passwd', + diff: 'malicious content' + } as Extract; + + await expect(editor.createFile(operation)).rejects.toThrow( + 'Operation outside workspace: src/../../etc/passwd' + ); + expect(mockSandbox.writeFile).not.toHaveBeenCalled(); + }); + + it('should reject path traversal attempts with leading slash /../../etc/passwd', async () => { + const mockSandbox: MockSandbox = { + mkdir: vi.fn(), + writeFile: vi.fn() + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: '/../../etc/passwd', + diff: 'malicious content' + } as Extract; + + await expect(editor.createFile(operation)).rejects.toThrow( + 'Operation outside workspace: /../../etc/passwd' + ); + expect(mockSandbox.writeFile).not.toHaveBeenCalled(); + }); + + it('should reject path traversal attempts with leading dot-slash ./../../etc/passwd', async () => { + const mockSandbox: MockSandbox = { + mkdir: vi.fn(), + writeFile: vi.fn() + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: './../../etc/passwd', + diff: 'malicious content' + } as Extract; + + await expect(editor.createFile(operation)).rejects.toThrow( + 'Operation outside workspace: ./../../etc/passwd' + ); + expect(mockSandbox.writeFile).not.toHaveBeenCalled(); + }); + + it('should reject path traversal in updateFile operations', async () => { + const mockSandbox: MockSandbox = { + readFile: vi.fn() + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'update_file', + path: '../../etc/passwd', + diff: 'patch diff' + } as Extract; + + await expect(editor.updateFile(operation)).rejects.toThrow( + 'Operation outside workspace: ../../etc/passwd' + ); + expect(mockSandbox.readFile).not.toHaveBeenCalled(); + }); + + it('should reject path traversal in deleteFile operations', async () => { + const mockSandbox: MockSandbox = { + deleteFile: vi.fn() + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'delete_file', + path: '../../etc/passwd' + } as Extract; + + await expect(editor.deleteFile(operation)).rejects.toThrow( + 'Operation outside workspace: ../../etc/passwd' + ); + expect(mockSandbox.deleteFile).not.toHaveBeenCalled(); + }); + + it('should allow valid paths that use .. but stay within workspace', async () => { + applyDiffMock.mockReturnValueOnce('file contents'); + + const mockSandbox: MockSandbox = { + mkdir: vi.fn().mockResolvedValue(undefined), + writeFile: vi.fn().mockResolvedValue(undefined) + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: 'src/subdir/../../file.txt', + diff: '--- diff ---' + } as Extract; + + await editor.createFile(operation); + + // Should resolve to /workspace/file.txt + expect(mockSandbox.writeFile).toHaveBeenCalledWith( + '/workspace/file.txt', + 'file contents', + { encoding: 'utf-8' } + ); + }); + + it('should handle paths with multiple consecutive slashes correctly', async () => { + applyDiffMock.mockReturnValueOnce('file contents'); + + const mockSandbox: MockSandbox = { + mkdir: vi.fn().mockResolvedValue(undefined), + writeFile: vi.fn().mockResolvedValue(undefined) + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: 'src//subdir///file.txt', + diff: '--- diff ---' + } as Extract; + + await editor.createFile(operation); + + expect(mockSandbox.writeFile).toHaveBeenCalledWith( + '/workspace/src/subdir/file.txt', + 'file contents', + { encoding: 'utf-8' } + ); + }); + + it('should reject deep path traversal attempts', async () => { + const mockSandbox: MockSandbox = { + mkdir: vi.fn(), + writeFile: vi.fn() + }; + + const editor = new Editor(mockSandbox as unknown as Sandbox); + const operation = { + type: 'create_file', + path: 'a/b/c/../../../../etc/passwd', + diff: 'malicious content' + } as Extract; + + await expect(editor.createFile(operation)).rejects.toThrow( + 'Operation outside workspace: a/b/c/../../../../etc/passwd' + ); + expect(mockSandbox.writeFile).not.toHaveBeenCalled(); + }); + + it('should handle absolute paths within workspace', async () => { + applyDiffMock.mockReturnValueOnce('content'); + + const mockSandbox: MockSandbox = { + mkdir: vi.fn().mockResolvedValue(undefined), + writeFile: vi.fn().mockResolvedValue(undefined) + }; + + const editor = new Editor( + mockSandbox as unknown as Sandbox, + '/workspace' + ); + const operation = { + type: 'create_file', + path: '/workspace/file.txt', + diff: 'content' + } as Extract; + + await editor.createFile(operation); + + expect(mockSandbox.writeFile).toHaveBeenCalledWith( + '/workspace/file.txt', // Not /workspace/workspace/file.txt + 'content', + { encoding: 'utf-8' } + ); + }); + }); +}); diff --git a/packages/sandbox/tsdown.config.ts b/packages/sandbox/tsdown.config.ts index 61438e48..f15b86bb 100644 --- a/packages/sandbox/tsdown.config.ts +++ b/packages/sandbox/tsdown.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsdown'; export default defineConfig({ - entry: 'src/index.ts', + entry: ['src/index.ts', 'src/openai/index.ts'], outDir: 'dist', dts: { sourcemap: true,