@liveport/agent-sdk
v0.1.1
Published
TypeScript SDK for AI agents to access LivePort tunnels
Maintainers
Readme
@liveport/agent-sdk
TypeScript SDK for AI agents to discover and access LivePort tunnels
Enable AI agents and automated testing frameworks to seamlessly access localhost applications through secure LivePort tunnels. Perfect for E2E testing, AI-powered development tools, and automated workflows.
Features
- 🔍 Auto-Discovery - Wait for tunnels to become available automatically
- 🤖 AI-First Design - Built specifically for AI agents and automation
- ⏱️ Configurable Timeouts - Fine-tune waiting and polling behavior
- 🔐 Secure Authentication - Bridge key-based authentication
- 📊 Multiple Tunnels - List and manage multiple active tunnels
- 🧪 Testing Ready - Perfect for CI/CD and automated testing
- TypeScript Native - Full type safety and IntelliSense support
Installation
npm install @liveport/agent-sdk
# or
pnpm add @liveport/agent-sdk
# or
yarn add @liveport/agent-sdkQuick Start
import { LivePortAgent } from '@liveport/agent-sdk';
// Create agent instance
const agent = new LivePortAgent({
key: process.env.LIVEPORT_KEY!
});
// Wait for tunnel to become available
const tunnel = await agent.waitForTunnel({ timeout: 30000 });
console.log(`Testing at: ${tunnel.url}`);
// Run your tests against tunnel.url
const response = await fetch(`${tunnel.url}/api/health`);
console.log(await response.json());
// Clean up when done
await agent.disconnect();API Reference
LivePortAgent
Main SDK class for interacting with LivePort tunnels.
Constructor
new LivePortAgent(config: LivePortAgentConfig)Config options:
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| key | string | ✅ | - | Bridge key for authentication |
| apiUrl | string | ❌ | https://app.liveport.dev | API base URL |
| timeout | number | ❌ | 30000 | Default timeout in milliseconds |
Example:
const agent = new LivePortAgent({
key: 'lpk_abc123...',
apiUrl: 'https://app.liveport.dev',
timeout: 60000
});waitForTunnel(options?)
Wait for a tunnel to become available. Long-polls the API until a tunnel is ready or timeout is reached.
async waitForTunnel(options?: WaitForTunnelOptions): Promise<AgentTunnel>Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| timeout | number | 30000 | Maximum wait time in milliseconds |
| pollInterval | number | 1000 | Poll interval in milliseconds |
Returns: Promise<AgentTunnel>
Throws:
TunnelTimeoutError- If no tunnel becomes available within timeoutApiError- If the API request fails
Example:
try {
const tunnel = await agent.waitForTunnel({
timeout: 60000, // Wait up to 60 seconds
pollInterval: 2000 // Check every 2 seconds
});
console.log(`Tunnel available at: ${tunnel.url}`);
} catch (error) {
if (error instanceof TunnelTimeoutError) {
console.log('No tunnel available yet');
}
}listTunnels()
List all active tunnels for this bridge key.
async listTunnels(): Promise<AgentTunnel[]>Returns: Promise<AgentTunnel[]> - Array of active tunnels
Throws: ApiError - If the API request fails
Example:
const tunnels = await agent.listTunnels();
for (const tunnel of tunnels) {
console.log(`${tunnel.subdomain}: ${tunnel.url} (port ${tunnel.localPort})`);
}
// Find specific tunnel by port
const apiTunnel = tunnels.find(t => t.localPort === 3000);
if (apiTunnel) {
console.log(`API server at: ${apiTunnel.url}`);
}disconnect()
Disconnect and clean up. Cancels any pending waitForTunnel calls.
async disconnect(): Promise<void>Example:
// Always clean up in a finally block
try {
const tunnel = await agent.waitForTunnel();
// Use tunnel...
} finally {
await agent.disconnect();
}Types
AgentTunnel
Represents a tunnel connection.
interface AgentTunnel {
tunnelId: string; // Unique tunnel identifier
subdomain: string; // Subdomain (e.g., "swift-fox-a7x2")
url: string; // Full public URL
localPort: number; // Local port being tunneled
createdAt: Date; // When tunnel was created
expiresAt: Date; // When tunnel will expire
}TunnelTimeoutError
Thrown when waitForTunnel times out.
class TunnelTimeoutError extends Error {
constructor(timeout: number);
}ApiError
Thrown when an API request fails.
class ApiError extends Error {
statusCode: number; // HTTP status code
code: string; // Error code (e.g., "INVALID_KEY")
constructor(statusCode: number, code: string, message: string);
}Error Codes:
INVALID_KEY- Bridge key is invalidEXPIRED_KEY- Bridge key has expiredREVOKED_KEY- Bridge key was revokedUSAGE_LIMIT_EXCEEDED- Key usage limit reachedRATE_LIMIT_EXCEEDED- Too many requestsTIMEOUT- Server timeoutTUNNEL_SERVER_ERROR- Tunnel server errorINTERNAL_ERROR- Internal server error
Use Cases
1. Automated Testing
import { LivePortAgent } from '@liveport/agent-sdk';
import { test, expect } from 'vitest';
let tunnelUrl: string;
beforeAll(async () => {
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
const tunnel = await agent.waitForTunnel({ timeout: 60000 });
tunnelUrl = tunnel.url;
});
test('API returns healthy status', async () => {
const response = await fetch(`${tunnelUrl}/api/health`);
expect(response.ok).toBe(true);
const data = await response.json();
expect(data.status).toBe('healthy');
});2. AI Agent Workflow
import { LivePortAgent } from '@liveport/agent-sdk';
async function runAITests(bridgeKey: string) {
const agent = new LivePortAgent({ key: bridgeKey, timeout: 120000 });
console.log('Waiting for developer to start tunnel...');
const tunnel = await agent.waitForTunnel();
console.log(`Connected to ${tunnel.url}`);
// AI agent can now interact with the local application
await discoverEndpoints(tunnel.url);
await runPerformanceTests(tunnel.url);
await analyzeAPIs(tunnel.url);
await generateDocumentation(tunnel.url);
await agent.disconnect();
}3. CI/CD Integration
// tests/setup.ts
import { LivePortAgent } from '@liveport/agent-sdk';
export async function setupTunnel() {
const agent = new LivePortAgent({
key: process.env.CI_LIVEPORT_KEY!,
timeout: 60000
});
// Wait for tunnel (developer must start it before running tests)
const tunnel = await agent.waitForTunnel();
// Make tunnel URL available to tests
process.env.TEST_BASE_URL = tunnel.url;
return { agent, tunnel };
}
export async function teardownTunnel(agent: LivePortAgent) {
await agent.disconnect();
}4. Multi-Service Testing
import { LivePortAgent } from '@liveport/agent-sdk';
async function testMicroservices() {
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
// List all available tunnels
const tunnels = await agent.listTunnels();
// Find specific services by port
const api = tunnels.find(t => t.localPort === 3000);
const web = tunnels.find(t => t.localPort === 3001);
const db = tunnels.find(t => t.localPort === 5432);
if (!api || !web) {
throw new Error('Required services not available');
}
// Test API
const apiHealth = await fetch(`${api.url}/health`);
console.log(`API: ${apiHealth.status}`);
// Test Web
const webHome = await fetch(web.url);
console.log(`Web: ${webHome.status}`);
await agent.disconnect();
}5. Webhook Testing
import { LivePortAgent } from '@liveport/agent-sdk';
async function testWebhooks() {
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
const tunnel = await agent.waitForTunnel();
// Register webhook with external service
await registerWebhook(`${tunnel.url}/webhooks/stripe`);
// Trigger webhook event
await triggerTestEvent();
// Verify webhook was received
const response = await fetch(`${tunnel.url}/webhooks/verify`);
const result = await response.json();
expect(result.received).toBe(true);
await agent.disconnect();
}Error Handling
Handling Timeouts
import { LivePortAgent, TunnelTimeoutError } from '@liveport/agent-sdk';
try {
const tunnel = await agent.waitForTunnel({ timeout: 30000 });
} catch (error) {
if (error instanceof TunnelTimeoutError) {
console.error('No tunnel available. Did you forget to run:');
console.error(' liveport connect <port>');
process.exit(1);
}
throw error;
}Handling API Errors
import { ApiError } from '@liveport/agent-sdk';
try {
const tunnels = await agent.listTunnels();
} catch (error) {
if (error instanceof ApiError) {
console.error(`API Error [${error.code}]: ${error.message}`);
console.error(`Status: ${error.statusCode}`);
if (error.code === 'INVALID_KEY') {
console.error('Get a new key at: https://liveport.dev/keys');
}
}
throw error;
}Robust Error Handling Pattern
async function robustTunnelAccess() {
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
try {
const tunnel = await agent.waitForTunnel({ timeout: 60000 });
// Your logic here
await runTests(tunnel.url);
} catch (error) {
if (error instanceof TunnelTimeoutError) {
console.error('Timeout waiting for tunnel');
process.exit(1);
} else if (error instanceof ApiError) {
console.error(`API Error: ${error.message}`);
process.exit(1);
} else {
console.error('Unexpected error:', error);
process.exit(1);
}
} finally {
await agent.disconnect();
}
}Examples
Comprehensive examples are available in the examples/ directory:
- 01-basic-usage.ts - Simple tunnel connection
- 02-testing-integration.ts - Vitest integration
- 03-ai-agent-workflow.ts - AI agent implementation
- 04-multiple-tunnels.ts - Multi-service testing
- 05-error-handling.ts - Robust error handling
Run examples:
# Set your bridge key
export LIVEPORT_KEY=lpk_your_key_here
# Start a tunnel in another terminal
liveport connect 3000
# Run an example
npx tsx examples/01-basic-usage.tsBest Practices
✅ Do's
Set reasonable timeouts
// Good: 60 second timeout for CI const tunnel = await agent.waitForTunnel({ timeout: 60000 });Always clean up
try { const tunnel = await agent.waitForTunnel(); // Use tunnel... } finally { await agent.disconnect(); // Always cleanup }Handle specific errors
try { const tunnel = await agent.waitForTunnel(); } catch (error) { if (error instanceof TunnelTimeoutError) { // Provide helpful message } }Use environment variables
// Good const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! }); // Bad - never hardcode keys const agent = new LivePortAgent({ key: 'lpk_...' });Configure appropriate timeouts
// Local development: short timeout const tunnel = await agent.waitForTunnel({ timeout: 10000 }); // CI/CD: longer timeout const tunnel = await agent.waitForTunnel({ timeout: 120000 });
❌ Don'ts
Don't commit bridge keys
# Use .env.local (git-ignored) LIVEPORT_KEY=lpk_your_key_hereDon't forget to disconnect
// Bad - missing cleanup const tunnel = await agent.waitForTunnel(); // ... tests ... // Missing: await agent.disconnect()Don't ignore errors
// Bad - swallowing errors try { const tunnel = await agent.waitForTunnel(); } catch (error) { // Silent failure }Don't use production keys in tests
# Separate keys for different environments LIVEPORT_DEV_KEY=lpk_dev_... LIVEPORT_CI_KEY=lpk_ci_... LIVEPORT_PROD_KEY=lpk_prod_...
TypeScript Support
This package is written in TypeScript and includes full type definitions.
import type {
LivePortAgent,
LivePortAgentConfig,
AgentTunnel,
WaitForTunnelOptions,
TunnelTimeoutError,
ApiError
} from '@liveport/agent-sdk';
// Full IntelliSense support
const config: LivePortAgentConfig = {
key: 'lpk_...',
timeout: 30000
};
const agent: LivePortAgent = new LivePortAgent(config);
const tunnel: AgentTunnel = await agent.waitForTunnel();Testing
The SDK includes comprehensive tests. Run them with:
pnpm testRelated Packages
- @liveport/cli - CLI for creating tunnels
- Dashboard - Web interface for managing keys and tunnels
Resources
- Documentation: liveport.dev/docs
- API Reference: liveport.dev/docs
- Dashboard: liveport.dev/dashboard
- Examples: examples/
- Support: GitHub Issues
- Website: liveport.dev
Contributing
Contributions are welcome! Please read our Contributing Guide first.
License
MIT © LivePort
Built with ❤️ for AI agents and developers
