Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
*.yaml text eol=lf



2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Thumbs.db
# Project specific
snapshot_*.json
*.test.js.map
traces/
tests/tracing/test-traces/

# Temporary directories from sync workflows
extension-temp/
Expand Down
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ docs/
*.temp



58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,64 @@ const browser = new SentienceBrowser(undefined, undefined, true);
const browser = new SentienceBrowser(); // headless=true if CI=true, else false
```

### Residential Proxy Support

For users running from datacenters (AWS, DigitalOcean, etc.), you can configure a residential proxy to prevent IP-based detection by Cloudflare, Akamai, and other anti-bot services.

**Supported Formats:**
- HTTP: `http://username:password@host:port`
- HTTPS: `https://username:password@host:port`
- SOCKS5: `socks5://username:password@host:port`

**Usage:**

```typescript
// Via constructor parameter
const browser = new SentienceBrowser(
undefined,
undefined,
false,
'http://username:password@residential-proxy.com:8000'
);
await browser.start();

// Via environment variable
process.env.SENTIENCE_PROXY = 'http://username:password@proxy.com:8000';
const browser = new SentienceBrowser();
await browser.start();

// With agent
import { SentienceAgent, OpenAIProvider } from 'sentience-ts';

const browser = new SentienceBrowser(
'your-api-key',
undefined,
false,
'http://user:pass@proxy.com:8000'
);
await browser.start();

const agent = new SentienceAgent(browser, new OpenAIProvider('openai-key'));
await agent.act('Navigate to example.com');
```

**WebRTC Protection:**
The SDK automatically adds WebRTC leak protection flags when a proxy is configured, preventing your real datacenter IP from being exposed via WebRTC even when using proxies.

**HTTPS Certificate Handling:**
The SDK automatically ignores HTTPS certificate errors when a proxy is configured, as residential proxies often use self-signed certificates for SSL interception. This ensures seamless navigation to HTTPS sites through the proxy.

**Example:**
```bash
# Run with proxy via environment variable
SENTIENCE_PROXY=http://user:pass@proxy.com:8000 npm run example:proxy

# Or via command line argument
ts-node examples/proxy-example.ts --proxy=http://user:pass@proxy.com:8000
```

**Note:** The proxy is configured at the browser level, so all traffic (including the Chrome extension) routes through the proxy. No changes to the extension are required.

## Best Practices

### 1. Wait for Dynamic Content
Expand Down
1 change: 1 addition & 0 deletions docs/QUERY_DSL.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,3 +507,4 @@ const center = query(snap, 'bbox.x>400 bbox.x<600 bbox.y>300 bbox.y<500');
- [Snapshot Schema](../../spec/snapshot.schema.json)



1 change: 1 addition & 0 deletions examples/click-rect-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,4 @@ async function main() {
main().catch(console.error);



153 changes: 153 additions & 0 deletions examples/cloud-tracing-agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* Example: Agent with Cloud Tracing
*
* Demonstrates how to use cloud tracing with SentienceAgent to upload traces
* and screenshots to cloud storage for remote viewing and analysis.
*
* Requirements:
* - Pro or Enterprise tier API key (SENTIENCE_API_KEY)
* - OpenAI API key (OPENAI_API_KEY) for LLM
*
* Usage:
* ts-node examples/cloud-tracing-agent.ts
* or
* npm run example:cloud-tracing
*/

import { SentienceBrowser } from '../src/browser';
import { SentienceAgent } from '../src/agent';
import { OpenAIProvider } from '../src/llm-provider';
import { createTracer } from '../src/tracing/tracer-factory';

async function main() {
// Get API keys from environment
const sentienceKey = process.env.SENTIENCE_API_KEY;
const openaiKey = process.env.OPENAI_API_KEY;

if (!sentienceKey) {
console.error('❌ Error: SENTIENCE_API_KEY not set');
console.error(' Cloud tracing requires Pro or Enterprise tier');
console.error(' Get your API key at: https://sentience.studio');
process.exit(1);
}

if (!openaiKey) {
console.error('❌ Error: OPENAI_API_KEY not set');
process.exit(1);
}

console.log('🚀 Starting Agent with Cloud Tracing Demo\n');

// 1. Create tracer with automatic tier detection
// If apiKey is Pro/Enterprise, uses CloudTraceSink
// If apiKey is missing/invalid, falls back to local JsonlTraceSink
const runId = 'cloud-tracing-demo';
const tracer = await createTracer({
apiKey: sentienceKey,
runId: runId
});

console.log(`🆔 Run ID: ${runId}\n`);

// 2. Create browser and LLM
console.log('🌐 Starting browser...');
const browser = new SentienceBrowser(sentienceKey, undefined, false);

try {
await browser.start();
console.log('✅ Browser started successfully');
} catch (error: any) {
console.error(`❌ Failed to start browser: ${error.message}`);
throw error;
}

const llm = new OpenAIProvider(openaiKey, 'gpt-4o-mini');

// 3. Create agent with tracer
// Note: Screenshot capture is handled automatically by the tracer
// The agent will capture screenshots for each step when tracer is provided
const agent = new SentienceAgent(browser, llm, 50, true, tracer);

try {
// 5. Navigate and execute agent actions
console.log('🌐 Navigating to Google...\n');
const page = browser.getPage();
console.log(' Getting page...');

try {
console.log(' Navigating to Google...');
await page.goto('https://www.google.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
console.log(' Page loaded!');

// Wait a bit for page to stabilize (instead of networkidle which can hang)
console.log(' Waiting for page to stabilize...');
await new Promise(resolve => setTimeout(resolve, 2000));

// Wait for extension to inject (required for snapshot)
console.log(' Waiting for Sentience extension to inject...');
try {
await page.waitForFunction(
() => typeof (window as any).sentience !== 'undefined',
{ timeout: 10000 }
);
console.log(' ✅ Extension ready!\n');
} catch (error: any) {
console.error(` ⚠️ Extension not ready after 10s: ${error.message}`);
console.error(' Continuing anyway - snapshot may fail if extension not loaded');
}
} catch (error: any) {
console.error(` ❌ Navigation/extension error: ${error.message}`);
throw error;
}

// All actions are automatically traced!
console.log('📝 Executing agent actions (all automatically traced)...\n');
console.log(' Action 1: Click the search box...');
await agent.act('Click the search box');
console.log(' ✅ Action 1 complete');
console.log(' Action 2: Type into search field...');
await agent.act("Type 'Sentience AI agent SDK' into the search field");
console.log(' ✅ Action 2 complete');

console.log(' Action 3: Press Enter...');
await agent.act('Press Enter key');
console.log(' ✅ Action 3 complete');

// Wait for results
console.log(' Waiting for search results...');
await new Promise(resolve => setTimeout(resolve, 2000));

console.log(' Action 4: Click first result...');
await agent.act('Click the first non-ad search result');
console.log(' ✅ Action 4 complete');

console.log('\n✅ Agent execution complete!');

// 6. Get token usage stats
const stats = agent.getTokenStats();
console.log('\n📊 Token Usage:');
console.log(` Total tokens: ${stats.totalTokens}`);
console.log(` Prompt tokens: ${stats.totalPromptTokens}`);
console.log(` Completion tokens: ${stats.totalCompletionTokens}`);

} catch (error: any) {
console.error(`\n❌ Error during execution: ${error.message}`);
throw error;
} finally {
// 7. Close tracer (uploads to cloud)
console.log('\n📤 Uploading trace to cloud...');
try {
await tracer.close(true); // Wait for upload to complete
console.log('✅ Trace uploaded successfully!');
console.log(` View at: https://studio.sentienceapi.com (run_id: ${runId})`);
} catch (error: any) {
console.error(`⚠️ Upload failed: ${error.message}`);
console.error(` Trace preserved locally at: ~/.sentience/traces/pending/${runId}.jsonl`);
}

await browser.close();
}
}

main().catch(console.error);

85 changes: 85 additions & 0 deletions examples/proxy-example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Example: Using Residential Proxy with Sentience SDK
*
* This example demonstrates how to configure a residential proxy
* for use with the Sentience SDK when running from datacenters.
*
* Requirements:
* - Residential proxy connection string (e.g., from Bright Data, Oxylabs, etc.)
* - Sentience API key (optional, for server-side snapshots)
*
* Usage:
* SENTIENCE_PROXY=http://user:pass@proxy.com:8000 ts-node examples/proxy-example.ts
* or
* ts-node examples/proxy-example.ts --proxy http://user:pass@proxy.com:8000
*/

import { SentienceBrowser } from '../src/browser';
import { snapshot } from '../src/snapshot';

async function main() {
// Get proxy from command line argument or environment variable
const proxyArg = process.argv.find(arg => arg.startsWith('--proxy='));
const proxy = proxyArg
? proxyArg.split('=')[1]
: process.env.SENTIENCE_PROXY;

if (!proxy) {
console.error('❌ Error: Proxy not provided');
console.error(' Usage: ts-node examples/proxy-example.ts --proxy=http://user:pass@proxy.com:8000');
console.error(' Or set SENTIENCE_PROXY environment variable');
process.exit(1);
}

console.log('🌐 Starting browser with residential proxy...\n');
console.log(` Proxy: ${proxy.replace(/:[^:@]+@/, ':****@')}\n`); // Mask password in logs

// Create browser with proxy
const browser = new SentienceBrowser(undefined, undefined, false, proxy);

try {
await browser.start();
console.log('✅ Browser started with proxy\n');

// Navigate to a page that shows your IP
console.log('📍 Navigating to IP check service...');
try {
await browser.getPage().goto('https://api.ipify.org?format=json', {
waitUntil: 'domcontentloaded',
timeout: 30000
});

const ipInfo = await browser.getPage().evaluate(() => document.body.textContent);
console.log(` Your IP (via proxy): ${ipInfo}\n`);
} catch (error: any) {
console.warn(` ⚠️ Could not check IP: ${error.message}`);
console.warn(' This is normal if the proxy uses self-signed certificates.\n');
}

// Take a snapshot
console.log('📸 Taking snapshot...');
const snap = await snapshot(browser);
console.log(` ✅ Captured ${snap.elements.length} elements\n`);

// Navigate to another site
console.log('📍 Navigating to example.com...');
await browser.getPage().goto('https://example.com');
await browser.getPage().waitForLoadState('domcontentloaded');

const snap2 = await snapshot(browser);
console.log(` ✅ Captured ${snap2.elements.length} elements\n`);

console.log('✅ Proxy example complete!');
console.log('\n💡 Note: WebRTC leak protection is automatically enabled when using proxies.');
console.log(' This prevents your real IP from being exposed via WebRTC.');

} catch (error: any) {
console.error(`\n❌ Error: ${error.message}`);
throw error;
} finally {
await browser.close();
}
}

main().catch(console.error);

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sentience-ts",
"version": "0.14.0",
"version": "0.90.0",
"description": "TypeScript SDK for Sentience AI Agent Browser Automation",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -18,6 +18,7 @@
"example:conversational-amazon": "ts-node examples/conversational-amazon-shopping.ts",
"example:tracing": "ts-node examples/agent-with-tracing.ts",
"example:trace-replay": "ts-node examples/trace-replay-demo.ts",
"example:cloud-tracing": "ts-node examples/cloud-tracing-agent.ts",
"cli": "ts-node src/cli.ts"
},
"bin": {
Expand Down
Loading