diff --git a/browsers/file-io.mdx b/browsers/file-io.mdx index c9691d4..40d8431 100644 --- a/browsers/file-io.mdx +++ b/browsers/file-io.mdx @@ -13,10 +13,14 @@ Kernel browsers run in fully sandboxed environments with writable filesystems. W Playwright performs downloads via the browser itself, so there are a few steps: - Create a browser session -- Configure where the browser saves downloads using CDP +- Configure browser download behavior using CDP - Perform the download - Retrieve the file from the browser's filesystem + + With `behavior: 'default'`, downloads are saved to the browser's default download directory at `~/Downloads`. Use Kernel's File I/O APIs to retrieve files from this location. + + The CDP `downloadProgress` event signals when the browser finishes writing a file, but there may be a brief delay before the file becomes available through @@ -30,9 +34,10 @@ Playwright performs downloads via the browser itself, so there are a few steps: import Kernel from '@onkernel/sdk'; import { chromium } from 'playwright'; import fs from 'fs'; +import os from 'os'; import pTimeout from 'p-timeout'; -const DOWNLOAD_DIR = '/tmp/downloads'; +const DOWNLOAD_DIR = `${os.homedir()}/Downloads`; const kernel = new Kernel(); // Poll listFiles until the expected file appears in the directory @@ -63,8 +68,7 @@ async function main() { const client = await context.newCDPSession(page); await client.send('Browser.setDownloadBehavior', { - behavior: 'allow', - downloadPath: DOWNLOAD_DIR, + behavior: 'default', eventsEnabled: true, }); @@ -139,11 +143,12 @@ main(); ```python Python import asyncio import os +from pathlib import Path import time from kernel import Kernel from playwright.async_api import async_playwright -DOWNLOAD_DIR = "/tmp/downloads" +DOWNLOAD_DIR = str(Path.home() / "Downloads") kernel = Kernel() @@ -173,8 +178,7 @@ async def main(): await cdp_session.send( "Browser.setDownloadBehavior", { - "behavior": "allow", - "downloadPath": DOWNLOAD_DIR, + "behavior": "default", "eventsEnabled": True, }, ) @@ -353,135 +357,96 @@ Browser Use handles downloads automatically when configured properly. Documentat ## Uploads -You can upload from your local filesystem into the browser directly using Playwright's file input helpers. +Playwright's `setInputFiles()` method allows you to upload files directly to file input elements. You can provide either a local file path or a buffer with file data. ```typescript Typescript/Javascript import Kernel from '@onkernel/sdk'; import { chromium } from 'playwright'; -import { config } from 'dotenv'; - -config(); - -const REMOTE_DIR = '/tmp/downloads'; -const FILENAME = 'Kernel-Logo_Accent.png'; -const IMAGE_URL = 'https://www.onkernel.com/brand_assets/Kernel-Logo_Accent.png'; +import fs from 'fs'; const kernel = new Kernel(); async function main() { - // 1. Create Kernel browser session + // Create Kernel browser session const kernelBrowser = await kernel.browsers.create(); console.log('Live view:', kernelBrowser.browser_live_view_url); - // 2. Fetch the image from URL - console.log(`Fetching image from ${IMAGE_URL}`); - const response = await fetch(IMAGE_URL); - if (!response.ok) { - throw new Error(`Failed to fetch image: ${response.status}`); - } - const imageBlob = await response.blob(); - - // 3. Write the fetched image to the remote browser's filesystem - const remotePath = `${REMOTE_DIR}/${FILENAME}`; - console.log(`Writing to remote browser at ${remotePath}`); - await kernel.browsers.fs.writeFile(kernelBrowser.session_id, imageBlob, { - path: remotePath, - }); - console.log('File written to remote browser'); - - // 4. Connect Playwright and navigate to upload test page + // Connect Playwright const browser = await chromium.connectOverCDP(kernelBrowser.cdp_ws_url); const context = browser.contexts()[0] || (await browser.newContext()); const page = context.pages()[0] || (await context.newPage()); + // Navigate to upload test page console.log('Navigating to upload test page'); await page.goto('https://browser-tests-alpha.vercel.app/api/upload-test'); - // 5. Upload the file using Playwright's file input helper - console.log(`Uploading ${remotePath} via file input`); - const remoteFile = await kernel.browsers.fs.readFile(kernelBrowser.session_id, { path: remotePath }); - const fileBuffer = Buffer.from(await remoteFile.bytes()); - await page.locator('#fileUpload').setInputFiles([{ - name: FILENAME, - mimeType: 'image/png', + // Option 1: Upload from a local file path + // await page.locator('#fileUpload').setInputFiles('./path/to/local/file.png'); + + // Option 2: Upload from a buffer (useful for dynamically generated content) + const fileBuffer = fs.readFileSync('./test-file.txt'); + await page.locator('#fileUpload').setInputFiles({ + name: 'test-file.txt', + mimeType: 'text/plain', buffer: fileBuffer, - }]); - console.log('Upload completed'); + }); + console.log('File selected'); + + // Submit the form and verify the upload + await page.getByRole('button', { name: 'Upload' }).click(); + await page.waitForSelector('#uploadResult'); + const result = await page.locator('#uploadResult').textContent(); + console.log('Upload result:', result); await kernel.browsers.deleteByID(kernelBrowser.session_id); console.log('Browser deleted'); - - return null; } main(); - ```` ```python Python import asyncio -import os +from pathlib import Path from kernel import Kernel from playwright.async_api import async_playwright -from dotenv import load_dotenv - -load_dotenv() - -REMOTE_DIR = '/tmp/downloads' -FILENAME = 'Kernel-Logo_Accent.png' -IMAGE_URL = 'https://www.onkernel.com/brand_assets/Kernel-Logo_Accent.png' kernel = Kernel() async def main(): - # 1. Create Kernel browser session + # Create Kernel browser session kernel_browser = kernel.browsers.create() print(f'Live view: {kernel_browser.browser_live_view_url}') - # 2. Fetch the image from URL - print(f'Fetching image from {IMAGE_URL}') - import aiohttp - async with aiohttp.ClientSession() as session: - async with session.get(IMAGE_URL) as response: - if response.status != 200: - raise Exception(f'Failed to fetch image: {response.status}') - image_bytes = await response.read() - - # 3. Write the fetched image to the remote browser's filesystem - remote_path = f'{REMOTE_DIR}/{FILENAME}' - print(f'Writing to remote browser at {remote_path}') - kernel.browsers.fs.write_file( - kernel_browser.session_id, - image_bytes, - path=remote_path - ) - print('File written to remote browser') - - # 4. Connect Playwright and navigate to upload test page async with async_playwright() as playwright: + # Connect Playwright browser = await playwright.chromium.connect_over_cdp(kernel_browser.cdp_ws_url) context = browser.contexts[0] if browser.contexts else await browser.new_context() page = context.pages[0] if context.pages else await context.new_page() + # Navigate to upload test page print('Navigating to upload test page') await page.goto('https://browser-tests-alpha.vercel.app/api/upload-test') - # 5. Upload the file using Playwright's file input helper - print(f'Uploading {remote_path} via file input') - remote_file = kernel.browsers.fs.read_file( - kernel_browser.session_id, - path=remote_path - ) - file_buffer = remote_file.read() + # Option 1: Upload from a local file path + # await page.locator('#fileUpload').set_input_files('./path/to/local/file.png') + # Option 2: Upload from a buffer (useful for dynamically generated content) + file_buffer = Path('./test-file.txt').read_bytes() await page.locator('#fileUpload').set_input_files({ - 'name': FILENAME, - 'mimeType': 'image/png', + 'name': 'test-file.txt', + 'mimeType': 'text/plain', 'buffer': file_buffer, }) - print('Upload completed') + print('File selected') + + # Submit the form and verify the upload + await page.get_by_role('button', name='Upload').click() + await page.wait_for_selector('#uploadResult') + result = await page.locator('#uploadResult').text_content() + print(f'Upload result: {result}') await browser.close()