
Research
/Security News
737 Chrome VPN Extensions Linked to Brand Impersonation and Browser Traffic Redirection
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.
@shiplightai/sdk
Advanced tools
A companion SDK for Playwright that makes your tests resilient to UI changes like dynamic IDs, layout rearrangements, and styling updates.
npm install @shiplightai/sdk playwright
import { chromium } from 'playwright';
import { createAgent, configureSdk } from '@shiplightai/sdk';
// Configure SDK with API key (call once at startup)
configureSdk({
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
// Create an agent
const agent = createAgent({
model: 'claude-haiku-4-5',
});
// Use with Playwright
const browser = await chromium.launch();
const page = await browser.newPage();
// Login using the Sauce Labs demo site (public test site)
await agent.login(page, {
url: 'https://www.saucedemo.com/',
username: 'standard_user',
password: 'secret_sauce',
});
// Verify login succeeded
await agent.assert(page, 'Products page is visible');
// Extract data from the page
await agent.extract(page, 'the first product name', 'productName');
console.log('First product:', agent.getVariable('productName'));
await browser.close();
Extend the agent with custom actions for your specific use case:
import { createAgent, z } from '@shiplightai/sdk';
const agent = createAgent({ model: 'claude-haiku-4-5' });
// Register a custom action
agent.registerAction({
name: 'extract_email_code',
description: 'Extract verification code from email inbox',
schema: z.object({
email_address: z.string().describe('The email address to check'),
code_type: z.enum(['verification', 'reset']).describe('Type of code'),
}),
async execute(args, ctx) {
// Your custom logic here
const code = await myEmailService.getCode(args.email_address, args.code_type);
// Store the result for later use
ctx.variableStore.set('verification_code', code);
return { success: true, message: `Found code: ${code}` };
},
});
// The agent will automatically use your action when needed
await agent.act(page, 'Get the verification code from email');
await agent.act(page, 'Enter {{ verification_code }} in the input field');
createAgent(options)Create a new agent instance.
const agent = createAgent({
// Required: LLM model to use
model: 'claude-haiku-4-5',
// Optional: Initial variables
variables: { username: 'test@example.com' },
// Optional: Keys to mark as sensitive (masked in logs)
sensitiveKeys: ['password', 'apiKey'],
// Optional: Directory for test data files
testDataDir: './test-data',
// Optional: Directory for downloads
downloadDir: './downloads',
});
The SDK supports any model from the following providers. Pass the model name directly or use provider:model syntax for explicit routing.
| Provider | Model pattern | Example models | API key |
|---|---|---|---|
gemini-* | gemini-2.5-pro, gemini-2.5-flash | GOOGLE_API_KEY | |
| Anthropic | claude-* | claude-haiku-4-5, claude-sonnet-4-6, claude-opus-4-6 | ANTHROPIC_API_KEY |
| OpenAI | gpt-*, o1*, o3*, o4*, chatgpt-* | gpt-4o, o4-mini | OPENAI_API_KEY |
Explicit provider routing — use provider:model to bypass auto-detection:
// Route through specific provider
const agent = createAgent({ model: 'openai:gpt-4o' });
const agent = createAgent({ model: 'anthropic:claude-sonnet-4-6' });
// Useful for fine-tuned models or cloud-hosted variants
const agent = createAgent({ model: 'openai:ft:gpt-4o:my-org' });
agent.registerAction(action)Register a custom action.
agent.registerAction({
// Unique action name (snake_case recommended)
name: 'my_action',
// Description for the agent
description: 'What this action does and when to use it',
// Zod schema for parameters
schema: z.object({
param1: z.string().describe('Description of the parameter'),
param2: z.number().optional(),
}),
// Execute function
async execute(args, ctx) {
// args: validated parameters
// ctx.page: Playwright page
// ctx.variableStore: access to variables
return { success: true, message: 'Optional status message' };
},
});
agent.act(page, instruction)Perform a single action on the page.
await agent.act(page, 'Click the login button');
await agent.act(page, 'Fill the email field with test@example.com');
await agent.act(page, 'Select "Express" from shipping dropdown');
agent.run(page, instruction, options?)Run a multi-step instruction until the goal is achieved.
await agent.run(page, 'Complete the checkout process');
await agent.run(page, 'Fill out the entire registration form');
await agent.run(page, 'Add 3 items to cart', { maxSteps: 10 });
agent.assert(page, statement)Assert a condition (throws on failure).
await agent.assert(page, 'Login button is visible');
await agent.assert(page, 'Cart contains 3 items');
agent.evaluate(page, statement)Evaluate a condition (returns boolean, doesn't throw).
const isLoggedIn = await agent.evaluate(page, 'User is logged in');
if (!isLoggedIn) {
await agent.run(page, 'Click the login button');
}
agent.extract(page, description, variableName)Extract data from the page and store in a variable.
await agent.extract(page, 'the order total', 'orderTotal');
await agent.run(page, 'Verify {{ orderTotal }} is displayed on receipt');
agent.step(page, action, description, options?)Wrap Playwright code with self-healing. If action throws, the agent analyzes the page and attempts to accomplish the goal described in description.
// Single action with self-healing
await agent.step(
page,
async () => await page.click('#submit-btn'),
'Click the submit button'
);
// Code block with multiple actions
await agent.step(
page,
async () => {
await page.fill('#email', 'user@example.com');
await page.fill('#password', 'secret');
await page.click('#login');
},
'Fill login form and submit'
);
// Control recovery depth with maxSteps (default: 5)
await agent.step(
page,
async () => await page.click('.dynamic-button'),
'Click the dynamic button that appears after loading',
{ maxSteps: 3 }
);
agent.setVariable(name, value, sensitive?)Set a variable. Variables can be referenced in instructions using $variableName.
agent.setVariable('couponCode', 'SAVE20');
await agent.act(page, 'Enter $couponCode in the promo field');
// Sensitive values are masked in logs
agent.setVariable('apiKey', 'secret123', true);
agent.getVariable(name)Get a variable value.
await agent.extract(page, 'the order total', 'orderTotal');
console.log('Order total:', agent.getVariable('orderTotal'));
agent.waitUntil(page, condition, timeoutSeconds?)Wait until a condition becomes true.
await agent.waitUntil(page, 'Loading spinner is no longer visible');
const appeared = await agent.waitUntil(page, 'Table shows at least 5 rows', 30);
if (!appeared) {
throw new Error('Data did not load in time');
}
The execute function receives a context object:
interface IActionExecutionContext {
// Playwright page instance
page: Page;
// Variable store for reading/writing variables
variableStore: VariableStore;
}
async execute(args, ctx) {
// Read a variable
const email = ctx.variableStore.get('email');
// Set a variable
ctx.variableStore.set('result', 'some value');
// Set a sensitive variable (masked in logs)
ctx.variableStore.set('token', secretToken, true);
return { success: true };
}
Configure SDK-wide settings before creating agents:
import { configureSdk, getSdkConfig, LogLevel } from '@shiplightai/sdk';
configureSdk({
// Log level: DEBUG, INFO, WARN, ERROR
logLevel: LogLevel.INFO,
// Enable detailed agent logging
debugAgent: false,
// Environment variables (API keys — provide the key for your chosen model's provider)
env: {
GOOGLE_API_KEY: 'your-google-api-key', // for gemini-* models
// ANTHROPIC_API_KEY: 'your-anthropic-key', // for claude-* models
// OPENAI_API_KEY: 'sk-...', // for gpt-*, o1*, o3*, o4* models
},
// Optional: paths for logs and results
agentLogPath: './logs/agent.log',
testResultsJsonPath: './results.json',
});
// Read current config
const config = getSdkConfig();
API keys must be passed via configureSdk({ env }) — the SDK does not read process.env directly.
| Key | Required | Description |
|---|---|---|
GOOGLE_API_KEY | Yes* | Google AI API key for gemini-* models |
ANTHROPIC_API_KEY | Yes* | Anthropic API key for claude-* models |
OPENAI_API_KEY | Yes* | OpenAI API key for gpt-*, o1*, o3*, o4* models |
*Provide the key for the provider matching your chosen model.
MIT
FAQs
Shiplight SDK with pluggable custom actions
The npm package @shiplightai/sdk receives a total of 46 weekly downloads. As such, @shiplightai/sdk popularity was classified as not popular.
We found that @shiplightai/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.