Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: Release Package

on:
workflow_dispatch:
inputs:
version_type:
description: 'Version bump type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
prerelease:
description: 'Create as prerelease'
required: false
default: false
type: boolean

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: Install dependencies
run: bun install

- name: Run linter
run: bun run lint

- name: Build package
run: bun run build

- name: Verify build output
run: |
echo "Checking dist folder contents:"
ls -la dist/ || echo "dist folder not found"
echo "Checking if main entry point exists:"
ls -la dist/index.js || echo "dist/index.js not found"

- name: Configure Git
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"

- name: Get current version
id: current_version
run: |
CURRENT_VERSION=$(node -p "require('./package.json').version")
echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT

- name: Bump version
id: version
run: |
# Install semver for version bumping
npm install -g semver

CURRENT_VERSION="${{ steps.current_version.outputs.current }}"
NEW_VERSION=$(semver -i ${{ github.event.inputs.version_type }} $CURRENT_VERSION)

echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "tag=v$NEW_VERSION" >> $GITHUB_OUTPUT

# Update package.json
npm version $NEW_VERSION --no-git-tag-version

- name: Update package.json and commit
run: |
git add package.json
git add -f dist/
git commit -m "chore: bump version to ${{ steps.version.outputs.new_version }} with built files"
git push origin main

- name: Create Git tag
run: |
git tag ${{ steps.version.outputs.tag }}
git push origin ${{ steps.version.outputs.tag }}

- name: Generate changelog
id: changelog
run: |
# Get commits since last tag
LAST_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")

if [ -z "$LAST_TAG" ]; then
COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges)
else
COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges)
fi

# Create changelog
CHANGELOG="## What's Changed"$'\n\n'"$COMMITS"

# Handle multiline output for GitHub Actions
{
echo 'changelog<<EOF'
echo "$CHANGELOG"
echo EOF
} >> $GITHUB_OUTPUT

- name: Create distribution archive
run: |
if [ -d "dist" ]; then
echo "Creating distribution archive from dist folder"
cd dist
zip -r ../dist.zip .
cd ..
else
echo "Warning: dist folder not found, creating empty archive"
touch empty.txt
zip dist.zip empty.txt
rm empty.txt
fi

- name: Create GitHub Release
id: create_release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.version.outputs.tag }}
name: Release ${{ steps.version.outputs.tag }}
body: |
# Release ${{ steps.version.outputs.tag }}

${{ steps.changelog.outputs.changelog }}

## Installation

### NPM/Yarn/Bun
```bash
npm install @onkernel/cu-playwright-ts@${{ steps.version.outputs.new_version }}
# or
yarn add @onkernel/cu-playwright-ts@${{ steps.version.outputs.new_version }}
# or
bun add @onkernel/cu-playwright-ts@${{ steps.version.outputs.new_version }}
```

### GitHub Dependency
```json
{
"dependencies": {
"@onkernel/cu-playwright-ts": "github:bigboateng/cu-playwright-ts#${{ steps.version.outputs.tag }}"
}
}
```
draft: false
prerelease: ${{ github.event.inputs.prerelease }}
files: |
dist.zip
package.json
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ coverage/
# Misc
.cache/
.temp/
.tmp/
.tmp/

# Examples with sensitive data
example-beam-benefits.ts
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,67 @@
# Computer Use Playwright SDK

> **Fork Notice**: This is a fork of [@onkernel/cu-playwright-ts](https://github.com/onkernel/cu-playwright-ts) with additional custom features.

A TypeScript SDK that combines Anthropic's Computer Use capabilities with Playwright for browser automation tasks. This SDK provides a clean, type-safe interface for automating browser interactions using Claude's computer use abilities.

## 🆕 Additional Features in This Fork

This fork extends the original SDK with powerful new capabilities:

### 🔗 URL Extraction Tool

This fork includes a powerful custom URL extraction tool that intelligently finds and extracts URLs from elements on the page using visible text. This feature is **unique to this fork** and not available in the original SDK.

#### How It Works

The agent automatically uses the URL extraction tool when you ask for URLs by visible text:

```typescript
// Simple URL extraction - just ask naturally!
const url = await agent.execute(
'Extract the URL from the "Learn More" link'
);

// Extract from article titles
const articleUrl = await agent.execute(
'Get the URL from the article titled "Introduction to AI"'
);

// Extract multiple URLs with structured output
const urls = await agent.execute(
'Extract URLs from the top 3 navigation links',
z.array(z.object({
linkText: z.string(),
url: z.string(),
}))
);
```

#### Advanced Capabilities

**Smart Search Strategies** (prioritized in order):
1. **Exact text matching** - Finds elements containing the exact visible text
2. **Partial text matching** - Matches text within larger content blocks
3. **Anchor tag detection** - Locates `<a>` tags containing the text
4. **CSS selector fallback** - Direct element selection if text is a valid selector
5. **Clickable element search** - Finds interactive elements with the text
6. **URL pattern extraction** - Detects URLs directly within text content

**Technical Features**:
- **Computer Use optimized** - Works seamlessly with Claude's visual perception
- **Multiple HTML structures** - Handles complex nested elements and dynamic content
- **Automatic URL normalization** - Converts relative to absolute URLs
- **Smart error handling** - Provides helpful feedback when elements aren't found
- **Logging and debugging** - Built-in console logging for troubleshooting

**Best Practices**:
- Use the exact visible text you can see on the page
- For buttons or links, use their label text (e.g., "Download", "Read More", "View Details")
- For articles or stories, use their title text
- The tool will automatically handle finding the associated URL

---

## Features

- 🤖 **Simple API**: Single `ComputerUseAgent` class for all computer use tasks
Expand Down Expand Up @@ -166,6 +226,8 @@ const result = await agent.execute(
);
```



## Environment Setup

1. **Anthropic API Key**: Set your API key as an environment variable:
Expand Down
52 changes: 52 additions & 0 deletions examples/example-url-extraction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { chromium } from 'playwright';
import { ComputerUseAgent } from './index';
import { z } from 'zod';
async function urlExtractionExample(): Promise<void> {
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
if (!ANTHROPIC_API_KEY) {
throw new Error('ANTHROPIC_API_KEY environment variable is required');
}

const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();

try {
console.log('=== URL Extraction Examples ===\n');

// Example 1: Extract URL from a specific story on Hacker News
await page.goto("https://news.ycombinator.com/");

const agent = new ComputerUseAgent({
apiKey: ANTHROPIC_API_KEY,
page,
});

console.log('1. Extracting URL from the top story...');
const result = await agent.execute(
'Extract the URL from the top story on this page'
,
z.object({
url: z.string(),
})
);
console.log('Top story URL:', JSON.stringify(result, null, 2));


} catch (error) {
console.error('Error in URL extraction example:', error);
} finally {
await browser.close();
}
}

// Run examples
async function runExamples(): Promise<void> {
console.log('Running URL Extraction Examples...\n');

await urlExtractionExample();
// await structuredUrlExtractionExample();

console.log('\nAll examples completed!');
}

runExamples().catch(console.error);
File renamed without changes.
Loading