Skip to content

Commit 13ed89a

Browse files
author
Sentience Dev
committed
Merge pull request #56 from SentienceAPI/cloud_sync
Cloud Tracer Sink
2 parents d1a5d3d + 89c9caf commit 13ed89a

31 files changed

+2123
-66
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
*.yaml text eol=lf
1313

1414

15+

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Thumbs.db
4040
# Project specific
4141
snapshot_*.json
4242
*.test.js.map
43+
traces/
44+
tests/tracing/test-traces/
4345

4446
# Temporary directories from sync workflows
4547
extension-temp/

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ docs/
4444
*.temp
4545

4646

47+

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,64 @@ const browser = new SentienceBrowser(undefined, undefined, true);
720720
const browser = new SentienceBrowser(); // headless=true if CI=true, else false
721721
```
722722

723+
### Residential Proxy Support
724+
725+
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.
726+
727+
**Supported Formats:**
728+
- HTTP: `http://username:password@host:port`
729+
- HTTPS: `https://username:password@host:port`
730+
- SOCKS5: `socks5://username:password@host:port`
731+
732+
**Usage:**
733+
734+
```typescript
735+
// Via constructor parameter
736+
const browser = new SentienceBrowser(
737+
undefined,
738+
undefined,
739+
false,
740+
'http://username:password@residential-proxy.com:8000'
741+
);
742+
await browser.start();
743+
744+
// Via environment variable
745+
process.env.SENTIENCE_PROXY = 'http://username:password@proxy.com:8000';
746+
const browser = new SentienceBrowser();
747+
await browser.start();
748+
749+
// With agent
750+
import { SentienceAgent, OpenAIProvider } from 'sentience-ts';
751+
752+
const browser = new SentienceBrowser(
753+
'your-api-key',
754+
undefined,
755+
false,
756+
'http://user:pass@proxy.com:8000'
757+
);
758+
await browser.start();
759+
760+
const agent = new SentienceAgent(browser, new OpenAIProvider('openai-key'));
761+
await agent.act('Navigate to example.com');
762+
```
763+
764+
**WebRTC Protection:**
765+
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.
766+
767+
**HTTPS Certificate Handling:**
768+
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.
769+
770+
**Example:**
771+
```bash
772+
# Run with proxy via environment variable
773+
SENTIENCE_PROXY=http://user:pass@proxy.com:8000 npm run example:proxy
774+
775+
# Or via command line argument
776+
ts-node examples/proxy-example.ts --proxy=http://user:pass@proxy.com:8000
777+
```
778+
779+
**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.
780+
723781
## Best Practices
724782

725783
### 1. Wait for Dynamic Content

docs/QUERY_DSL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,4 @@ const center = query(snap, 'bbox.x>400 bbox.x<600 bbox.y>300 bbox.y<500');
507507
- [Snapshot Schema](../../spec/snapshot.schema.json)
508508

509509

510+

examples/click-rect-demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,4 @@ async function main() {
9898
main().catch(console.error);
9999

100100

101+

examples/cloud-tracing-agent.ts

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* Example: Agent with Cloud Tracing
3+
*
4+
* Demonstrates how to use cloud tracing with SentienceAgent to upload traces
5+
* and screenshots to cloud storage for remote viewing and analysis.
6+
*
7+
* Requirements:
8+
* - Pro or Enterprise tier API key (SENTIENCE_API_KEY)
9+
* - OpenAI API key (OPENAI_API_KEY) for LLM
10+
*
11+
* Usage:
12+
* ts-node examples/cloud-tracing-agent.ts
13+
* or
14+
* npm run example:cloud-tracing
15+
*/
16+
17+
import { SentienceBrowser } from '../src/browser';
18+
import { SentienceAgent } from '../src/agent';
19+
import { OpenAIProvider } from '../src/llm-provider';
20+
import { createTracer } from '../src/tracing/tracer-factory';
21+
22+
async function main() {
23+
// Get API keys from environment
24+
const sentienceKey = process.env.SENTIENCE_API_KEY;
25+
const openaiKey = process.env.OPENAI_API_KEY;
26+
27+
if (!sentienceKey) {
28+
console.error('❌ Error: SENTIENCE_API_KEY not set');
29+
console.error(' Cloud tracing requires Pro or Enterprise tier');
30+
console.error(' Get your API key at: https://sentience.studio');
31+
process.exit(1);
32+
}
33+
34+
if (!openaiKey) {
35+
console.error('❌ Error: OPENAI_API_KEY not set');
36+
process.exit(1);
37+
}
38+
39+
console.log('🚀 Starting Agent with Cloud Tracing Demo\n');
40+
41+
// 1. Create tracer with automatic tier detection
42+
// If apiKey is Pro/Enterprise, uses CloudTraceSink
43+
// If apiKey is missing/invalid, falls back to local JsonlTraceSink
44+
const runId = 'cloud-tracing-demo';
45+
const tracer = await createTracer({
46+
apiKey: sentienceKey,
47+
runId: runId
48+
});
49+
50+
console.log(`🆔 Run ID: ${runId}\n`);
51+
52+
// 2. Create browser and LLM
53+
console.log('🌐 Starting browser...');
54+
const browser = new SentienceBrowser(sentienceKey, undefined, false);
55+
56+
try {
57+
await browser.start();
58+
console.log('✅ Browser started successfully');
59+
} catch (error: any) {
60+
console.error(`❌ Failed to start browser: ${error.message}`);
61+
throw error;
62+
}
63+
64+
const llm = new OpenAIProvider(openaiKey, 'gpt-4o-mini');
65+
66+
// 3. Create agent with tracer
67+
// Note: Screenshot capture is handled automatically by the tracer
68+
// The agent will capture screenshots for each step when tracer is provided
69+
const agent = new SentienceAgent(browser, llm, 50, true, tracer);
70+
71+
try {
72+
// 5. Navigate and execute agent actions
73+
console.log('🌐 Navigating to Google...\n');
74+
const page = browser.getPage();
75+
console.log(' Getting page...');
76+
77+
try {
78+
console.log(' Navigating to Google...');
79+
await page.goto('https://www.google.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
80+
console.log(' Page loaded!');
81+
82+
// Wait a bit for page to stabilize (instead of networkidle which can hang)
83+
console.log(' Waiting for page to stabilize...');
84+
await new Promise(resolve => setTimeout(resolve, 2000));
85+
86+
// Wait for extension to inject (required for snapshot)
87+
console.log(' Waiting for Sentience extension to inject...');
88+
try {
89+
await page.waitForFunction(
90+
() => typeof (window as any).sentience !== 'undefined',
91+
{ timeout: 10000 }
92+
);
93+
console.log(' ✅ Extension ready!\n');
94+
} catch (error: any) {
95+
console.error(` ⚠️ Extension not ready after 10s: ${error.message}`);
96+
console.error(' Continuing anyway - snapshot may fail if extension not loaded');
97+
}
98+
} catch (error: any) {
99+
console.error(` ❌ Navigation/extension error: ${error.message}`);
100+
throw error;
101+
}
102+
103+
// All actions are automatically traced!
104+
console.log('📝 Executing agent actions (all automatically traced)...\n');
105+
console.log(' Action 1: Click the search box...');
106+
await agent.act('Click the search box');
107+
console.log(' ✅ Action 1 complete');
108+
console.log(' Action 2: Type into search field...');
109+
await agent.act("Type 'Sentience AI agent SDK' into the search field");
110+
console.log(' ✅ Action 2 complete');
111+
112+
console.log(' Action 3: Press Enter...');
113+
await agent.act('Press Enter key');
114+
console.log(' ✅ Action 3 complete');
115+
116+
// Wait for results
117+
console.log(' Waiting for search results...');
118+
await new Promise(resolve => setTimeout(resolve, 2000));
119+
120+
console.log(' Action 4: Click first result...');
121+
await agent.act('Click the first non-ad search result');
122+
console.log(' ✅ Action 4 complete');
123+
124+
console.log('\n✅ Agent execution complete!');
125+
126+
// 6. Get token usage stats
127+
const stats = agent.getTokenStats();
128+
console.log('\n📊 Token Usage:');
129+
console.log(` Total tokens: ${stats.totalTokens}`);
130+
console.log(` Prompt tokens: ${stats.totalPromptTokens}`);
131+
console.log(` Completion tokens: ${stats.totalCompletionTokens}`);
132+
133+
} catch (error: any) {
134+
console.error(`\n❌ Error during execution: ${error.message}`);
135+
throw error;
136+
} finally {
137+
// 7. Close tracer (uploads to cloud)
138+
console.log('\n📤 Uploading trace to cloud...');
139+
try {
140+
await tracer.close(true); // Wait for upload to complete
141+
console.log('✅ Trace uploaded successfully!');
142+
console.log(` View at: https://studio.sentienceapi.com (run_id: ${runId})`);
143+
} catch (error: any) {
144+
console.error(`⚠️ Upload failed: ${error.message}`);
145+
console.error(` Trace preserved locally at: ~/.sentience/traces/pending/${runId}.jsonl`);
146+
}
147+
148+
await browser.close();
149+
}
150+
}
151+
152+
main().catch(console.error);
153+

examples/proxy-example.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Example: Using Residential Proxy with Sentience SDK
3+
*
4+
* This example demonstrates how to configure a residential proxy
5+
* for use with the Sentience SDK when running from datacenters.
6+
*
7+
* Requirements:
8+
* - Residential proxy connection string (e.g., from Bright Data, Oxylabs, etc.)
9+
* - Sentience API key (optional, for server-side snapshots)
10+
*
11+
* Usage:
12+
* SENTIENCE_PROXY=http://user:pass@proxy.com:8000 ts-node examples/proxy-example.ts
13+
* or
14+
* ts-node examples/proxy-example.ts --proxy http://user:pass@proxy.com:8000
15+
*/
16+
17+
import { SentienceBrowser } from '../src/browser';
18+
import { snapshot } from '../src/snapshot';
19+
20+
async function main() {
21+
// Get proxy from command line argument or environment variable
22+
const proxyArg = process.argv.find(arg => arg.startsWith('--proxy='));
23+
const proxy = proxyArg
24+
? proxyArg.split('=')[1]
25+
: process.env.SENTIENCE_PROXY;
26+
27+
if (!proxy) {
28+
console.error('❌ Error: Proxy not provided');
29+
console.error(' Usage: ts-node examples/proxy-example.ts --proxy=http://user:pass@proxy.com:8000');
30+
console.error(' Or set SENTIENCE_PROXY environment variable');
31+
process.exit(1);
32+
}
33+
34+
console.log('🌐 Starting browser with residential proxy...\n');
35+
console.log(` Proxy: ${proxy.replace(/:[^:@]+@/, ':****@')}\n`); // Mask password in logs
36+
37+
// Create browser with proxy
38+
const browser = new SentienceBrowser(undefined, undefined, false, proxy);
39+
40+
try {
41+
await browser.start();
42+
console.log('✅ Browser started with proxy\n');
43+
44+
// Navigate to a page that shows your IP
45+
console.log('📍 Navigating to IP check service...');
46+
try {
47+
await browser.getPage().goto('https://api.ipify.org?format=json', {
48+
waitUntil: 'domcontentloaded',
49+
timeout: 30000
50+
});
51+
52+
const ipInfo = await browser.getPage().evaluate(() => document.body.textContent);
53+
console.log(` Your IP (via proxy): ${ipInfo}\n`);
54+
} catch (error: any) {
55+
console.warn(` ⚠️ Could not check IP: ${error.message}`);
56+
console.warn(' This is normal if the proxy uses self-signed certificates.\n');
57+
}
58+
59+
// Take a snapshot
60+
console.log('📸 Taking snapshot...');
61+
const snap = await snapshot(browser);
62+
console.log(` ✅ Captured ${snap.elements.length} elements\n`);
63+
64+
// Navigate to another site
65+
console.log('📍 Navigating to example.com...');
66+
await browser.getPage().goto('https://example.com');
67+
await browser.getPage().waitForLoadState('domcontentloaded');
68+
69+
const snap2 = await snapshot(browser);
70+
console.log(` ✅ Captured ${snap2.elements.length} elements\n`);
71+
72+
console.log('✅ Proxy example complete!');
73+
console.log('\n💡 Note: WebRTC leak protection is automatically enabled when using proxies.');
74+
console.log(' This prevents your real IP from being exposed via WebRTC.');
75+
76+
} catch (error: any) {
77+
console.error(`\n❌ Error: ${error.message}`);
78+
throw error;
79+
} finally {
80+
await browser.close();
81+
}
82+
}
83+
84+
main().catch(console.error);
85+

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sentience-ts",
3-
"version": "0.14.0",
3+
"version": "0.90.0",
44
"description": "TypeScript SDK for Sentience AI Agent Browser Automation",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
@@ -18,6 +18,7 @@
1818
"example:conversational-amazon": "ts-node examples/conversational-amazon-shopping.ts",
1919
"example:tracing": "ts-node examples/agent-with-tracing.ts",
2020
"example:trace-replay": "ts-node examples/trace-replay-demo.ts",
21+
"example:cloud-tracing": "ts-node examples/cloud-tracing-agent.ts",
2122
"cli": "ts-node src/cli.ts"
2223
},
2324
"bin": {

0 commit comments

Comments
 (0)