Anon’s Runtime SDK allows you to run custom automation scripts on connected user sessions. This guide will show you how to get started with the Runtime SDK.

Getting Started

Create a .npmrc file in the root of your project to gain access to our private package registry:

.npmrc
@anon:registry=https://npm.cloudsmith.io/anon/anon-sdk/
//npm.cloudsmith.io/anon/anon-sdk/:_authToken=YQtlU86MhKOXijPS
@npm:registry=https://registry.npmjs.org/

Add the SDK to your project’s dependencies:

npm install '@anon/sdk-typescript'
npx playwright install
npm install '@anon/actions'

This reference is for the v0.8.0 SDK. Please see here for older versions.

Below is an example of how to use the Runtime SDK to post content on LinkedIn:

import { AnonRuntime } from "@anon/sdk-typescript";
import { LinkedIn } from "@anon/actions";

const anon = new AnonRuntime({ apiKey: "your api key here" });

// The username of the user whose account to use
const appUserId = "myProduct-user";
// The app sessions to inject
const apps = ["linkedin"];
// The action to be performed
const action = async (page) => {
  await page.goto("https://linkedin.com");
  await LinkedIn.createPost(page, {
    title: "I love Anon",
    content: "They make automating actions so easy! Check out docs.anon.com to learn more."
  });
}

// Run the action
(async () => {
  const { result, liveStreamingUrl } = await anon.run({
    appUserId,
    apps,
    action
  });

  // You can use the liveStreamingUrl to watch your action be performed.
  console.log(liveStreamingUrl);

  // Await the successful completion of the action
  await result;
})();

You’ll need the appUserId of a user that you’ve previously linked an account with to run this code example. This is typically an email address.

Configuration

The anon.run() function has a variety of configuration options to fit any use case. You can configure where the browser runs, how network traffic is proxied, timeouts, and more.

Runtime Hosting

By default, the browser is a managed instance hosted by Browserbase. This is best for most use cases as it alleviates the need for you to manage your own browser infrastructure.

You can also use your own instance if it has an externally accessible CDP (Chrome Devtools Protocol) URL. Note that localhost endpoints are not typically externally accessible.

Remote Browser

This example injects your account sessions into an existing browser instance already running on a Browserbase CDP URL.

const { result } = await anon.run({
  appUserId,
  apps,
  action
  cdpUrl: `wss://connect.browserbase.com?apiKey=your-browserbase-api-key`,
});

Timeouts

A managed browser instance has a finite lifetime. You can adjust the lifetime using the sessionDuration parameter. This specifies the browser lifetime in seconds.

Setting browser instance lifetime:

const sessionDuration = 30 * 60 // increase browser lifetime to 30 minutes
const { result } = await anon.run({ appUserId, apps, action, sessionDuration });

Monitoring

Live Session Rendering

Even though our managed instances are headless, you and your users can observe a rendered view of the webpage live.

const { result, liveStreamingUrl } = await anon.run({ appUserId, apps, action });

/// Opens a browser pointed at the liveStreamingUrl
async function launchMonitoringPage(liveStreamingUrl: string) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // Navigate to a website (replace with your desired URL)
  await page.goto(liveStreamingUrl);

  // Wait for 5 seconds to keep the browser open (you can adjust this)
  await page.waitForTimeout(5000);

  // Close the browser
  await browser.close();
}

await launchMonitoringPage(liveStreamingUrl)

You can even embed the webpage as an iframe. This can be shared with your users so that they can watch the automations as they happen.

import React, { useState } from 'react';
import { Button } from '@/components/ui/button';

const VNCViewer = ({ vncUrl }) => {
  const [isConnected, setIsConnected] = useState(false);

  const handleConnect = () => {
    setIsConnected(true);
  };

  const handleDisconnect = () => {
    setIsConnected(false);
  };

  return (
    <div className="p-4">
      <h2 className="text-2xl font-bold mb-4">VNC Viewer</h2>
      <div className="flex space-x-2 mb-4">
        <Button onClick={handleConnect} disabled={isConnected}>
          Connect
        </Button>
        <Button onClick={handleDisconnect} disabled={!isConnected}>
          Disconnect
        </Button>
      </div>
      {isConnected && (
        <div className="border border-gray-300 rounded">
          <iframe
            src={vncUrl}
            width="800"
            height="600"
            title="VNC Viewer"
            className="w-full h-[600px]"
          />
        </div>
      )}
    </div>
  );
};

Logs

You can pass in a custom Logger to the browser initialization for fine-grained control over logging:

// For example, to accumulate a list of all the logs for analytics, etc
const myLogs = [];

const { result } = await anon.run({
  appUserId,
  logger: {
    isEnabled: true,
    log: (name, severity, message, args) => myLogs.push({ name, severity, message, args})
  }
});

Screenshots

You can plug into the existing Playwright screenshot functionality as follows:

page.on("pageLoad", (e) => {
  const filename = `loaded_page_screenshot_${Date.now()}.png`;
  await page.screenshot(filename);
})

Next Steps