npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@liveport/agent-sdk

v0.1.1

Published

TypeScript SDK for AI agents to access LivePort tunnels

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.

npm version License: MIT

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-sdk

Quick 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 timeout
  • ApiError - 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 invalid
  • EXPIRED_KEY - Bridge key has expired
  • REVOKED_KEY - Bridge key was revoked
  • USAGE_LIMIT_EXCEEDED - Key usage limit reached
  • RATE_LIMIT_EXCEEDED - Too many requests
  • TIMEOUT - Server timeout
  • TUNNEL_SERVER_ERROR - Tunnel server error
  • INTERNAL_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:

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.ts

Best Practices

✅ Do's

  1. Set reasonable timeouts

    // Good: 60 second timeout for CI
    const tunnel = await agent.waitForTunnel({ timeout: 60000 });
  2. Always clean up

    try {
      const tunnel = await agent.waitForTunnel();
      // Use tunnel...
    } finally {
      await agent.disconnect(); // Always cleanup
    }
  3. Handle specific errors

    try {
      const tunnel = await agent.waitForTunnel();
    } catch (error) {
      if (error instanceof TunnelTimeoutError) {
        // Provide helpful message
      }
    }
  4. Use environment variables

    // Good
    const agent = new LivePortAgent({
      key: process.env.LIVEPORT_KEY!
    });
    
    // Bad - never hardcode keys
    const agent = new LivePortAgent({ key: 'lpk_...' });
  5. 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

  1. Don't commit bridge keys

    # Use .env.local (git-ignored)
    LIVEPORT_KEY=lpk_your_key_here
  2. Don't forget to disconnect

    // Bad - missing cleanup
    const tunnel = await agent.waitForTunnel();
    // ... tests ...
    // Missing: await agent.disconnect()
  3. Don't ignore errors

    // Bad - swallowing errors
    try {
      const tunnel = await agent.waitForTunnel();
    } catch (error) {
      // Silent failure
    }
  4. 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 test

Related Packages


Resources


Contributing

Contributions are welcome! Please read our Contributing Guide first.


License

MIT © LivePort


Built with ❤️ for AI agents and developers