Documentation Index Fetch the complete documentation index at: https://tbd-6fc993ce-mason-add-copy-page-to-context-menu.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Kernel browsers run in fully sandboxed environments with a writable filesystem that you control. Anything your automation downloads during a session is saved inside this filesystem and can be retrieved directly while the session is running.
Downloads
Playwright performs downloads via the browser itself, so there are a few steps:
Create a browser session
Configure where the browser saves downloads
Perform the download
Retrieve the file from the browser’s filesystem
Typescript/Javascript
Python
import { Kernel } from "@onkernel/sdk" ;
import fs from "fs" ;
import pTimeout from "p-timeout" ;
import { chromium } from "playwright" ;
const DOWNLOAD_DIR = "/tmp/downloads" ;
const kernel = new Kernel ();
async function main () {
const kernelBrowser = await kernel . browsers . create ();
console . log ( "live view:" , kernelBrowser . browser_live_view_url );
const browser = await chromium . connectOverCDP ( kernelBrowser . cdp_ws_url );
const context = ( await browser . contexts ()[ 0 ]) || ( await browser . newContext ());
const page = ( await context . pages ()[ 0 ]) || ( await context . newPage ());
// Required to prevent Playwright from overriding the location of the downloaded file
const client = await context . newCDPSession ( page );
await client . send ( "Browser.setDownloadBehavior" , {
behavior: "allow" ,
downloadPath: DOWNLOAD_DIR ,
eventsEnabled: true ,
});
// Set up CDP listeners to capture download filename and completion
let downloadFilename : string | undefined ;
let downloadCompletedResolve !: () => void ;
const downloadCompleted = new Promise < void >(( resolve ) => {
downloadCompletedResolve = resolve ;
});
client . on ( "Browser.downloadWillBegin" , ( event ) => {
downloadFilename = event . suggestedFilename ?? "unknown" ;
console . log ( "Download started:" , downloadFilename );
});
client . on ( "Browser.downloadProgress" , ( event ) => {
if ( event . state === "completed" || event . state === "canceled" ) {
downloadCompletedResolve ();
}
});
// Trigger the download in the page
console . log ( "Navigating to download test page" );
await page . goto ( "https://browser-tests-alpha.vercel.app/api/download-test" );
await page . getByRole ( "link" , { name: "Download File" }). click ();
try {
await pTimeout ( downloadCompleted , {
milliseconds: 10_000 ,
message: new Error ( "Download timed out after 10 seconds" ),
});
console . log ( "Download completed" );
} catch ( err ) {
console . error ( err );
throw err ;
}
if ( ! downloadFilename ) {
throw new Error ( "Unable to determine download filename" );
}
// Download the file directly from the browser instance
const remotePath = ` ${ DOWNLOAD_DIR } / ${ downloadFilename } ` ;
console . log ( `Reading file: ${ remotePath } ` );
const resp = await kernel . browsers . fs . readFile ( kernelBrowser . session_id , {
path: remotePath ,
});
const bytes = await resp . bytes ();
fs . mkdirSync ( "downloads" , { recursive: true });
const localPath = `downloads/ ${ downloadFilename } ` ;
fs . writeFileSync ( localPath , bytes );
console . log ( `Saved to ${ localPath } ` );
// Alternatively, stream directly to disk:
// import { pipeline } from 'node:stream/promises';
// import { createWriteStream } from 'node:fs';
// import { Readable } from 'node:stream';
// await pipeline(Readable.fromWeb(resp.body!), createWriteStream(localPath));
await browser . close ();
}
main ();
For more complex scenarios, you can also use the list files API together with read file to enumerate and save all downloads at the end of a session.
Uploads
You can upload from your local filesystem into the browser directly using Playwright’s file input helpers.
Typescript/Javascript
Python
const localPath = "/path/to/a/file.txt" ;
console . log ( `Uploading ${ localPath } ...` );
await page . locator ( "#fileUpload" ). setInputFiles ( localPath );
console . log ( "Upload completed" );