@liveport/agent-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +609 -0
- package/dist/index.d.mts +105 -0
- package/dist/index.d.ts +105 -0
- package/dist/index.js +164 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +137 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
# @liveport/agent-sdk
|
|
2
|
+
|
|
3
|
+
> TypeScript SDK for AI agents to discover and access LivePort tunnels
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@liveport/agent-sdk)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **๐ Auto-Discovery** - Wait for tunnels to become available automatically
|
|
13
|
+
- **๐ค AI-First Design** - Built specifically for AI agents and automation
|
|
14
|
+
- **โฑ๏ธ Configurable Timeouts** - Fine-tune waiting and polling behavior
|
|
15
|
+
- **๐ Secure Authentication** - Bridge key-based authentication
|
|
16
|
+
- **๐ Multiple Tunnels** - List and manage multiple active tunnels
|
|
17
|
+
- **๐งช Testing Ready** - Perfect for CI/CD and automated testing
|
|
18
|
+
- **TypeScript Native** - Full type safety and IntelliSense support
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @liveport/agent-sdk
|
|
24
|
+
# or
|
|
25
|
+
pnpm add @liveport/agent-sdk
|
|
26
|
+
# or
|
|
27
|
+
yarn add @liveport/agent-sdk
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { LivePortAgent } from '@liveport/agent-sdk';
|
|
34
|
+
|
|
35
|
+
// Create agent instance
|
|
36
|
+
const agent = new LivePortAgent({
|
|
37
|
+
key: process.env.LIVEPORT_KEY!
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Wait for tunnel to become available
|
|
41
|
+
const tunnel = await agent.waitForTunnel({ timeout: 30000 });
|
|
42
|
+
console.log(`Testing at: ${tunnel.url}`);
|
|
43
|
+
|
|
44
|
+
// Run your tests against tunnel.url
|
|
45
|
+
const response = await fetch(`${tunnel.url}/api/health`);
|
|
46
|
+
console.log(await response.json());
|
|
47
|
+
|
|
48
|
+
// Clean up when done
|
|
49
|
+
await agent.disconnect();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API Reference
|
|
53
|
+
|
|
54
|
+
### `LivePortAgent`
|
|
55
|
+
|
|
56
|
+
Main SDK class for interacting with LivePort tunnels.
|
|
57
|
+
|
|
58
|
+
#### Constructor
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
new LivePortAgent(config: LivePortAgentConfig)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Config options:**
|
|
65
|
+
|
|
66
|
+
| Option | Type | Required | Default | Description |
|
|
67
|
+
|--------|------|----------|---------|-------------|
|
|
68
|
+
| `key` | string | โ
| - | Bridge key for authentication |
|
|
69
|
+
| `apiUrl` | string | โ | `https://app.liveport.dev` | API base URL |
|
|
70
|
+
| `timeout` | number | โ | `30000` | Default timeout in milliseconds |
|
|
71
|
+
|
|
72
|
+
**Example:**
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const agent = new LivePortAgent({
|
|
76
|
+
key: 'lpk_abc123...',
|
|
77
|
+
apiUrl: 'https://app.liveport.dev',
|
|
78
|
+
timeout: 60000
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
#### `waitForTunnel(options?)`
|
|
85
|
+
|
|
86
|
+
Wait for a tunnel to become available. Long-polls the API until a tunnel is ready or timeout is reached.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
async waitForTunnel(options?: WaitForTunnelOptions): Promise<AgentTunnel>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Options:**
|
|
93
|
+
|
|
94
|
+
| Option | Type | Default | Description |
|
|
95
|
+
|--------|------|---------|-------------|
|
|
96
|
+
| `timeout` | number | `30000` | Maximum wait time in milliseconds |
|
|
97
|
+
| `pollInterval` | number | `1000` | Poll interval in milliseconds |
|
|
98
|
+
|
|
99
|
+
**Returns:** `Promise<AgentTunnel>`
|
|
100
|
+
|
|
101
|
+
**Throws:**
|
|
102
|
+
- `TunnelTimeoutError` - If no tunnel becomes available within timeout
|
|
103
|
+
- `ApiError` - If the API request fails
|
|
104
|
+
|
|
105
|
+
**Example:**
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
try {
|
|
109
|
+
const tunnel = await agent.waitForTunnel({
|
|
110
|
+
timeout: 60000, // Wait up to 60 seconds
|
|
111
|
+
pollInterval: 2000 // Check every 2 seconds
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
console.log(`Tunnel available at: ${tunnel.url}`);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error instanceof TunnelTimeoutError) {
|
|
117
|
+
console.log('No tunnel available yet');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
#### `listTunnels()`
|
|
125
|
+
|
|
126
|
+
List all active tunnels for this bridge key.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
async listTunnels(): Promise<AgentTunnel[]>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Returns:** `Promise<AgentTunnel[]>` - Array of active tunnels
|
|
133
|
+
|
|
134
|
+
**Throws:** `ApiError` - If the API request fails
|
|
135
|
+
|
|
136
|
+
**Example:**
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const tunnels = await agent.listTunnels();
|
|
140
|
+
|
|
141
|
+
for (const tunnel of tunnels) {
|
|
142
|
+
console.log(`${tunnel.subdomain}: ${tunnel.url} (port ${tunnel.localPort})`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Find specific tunnel by port
|
|
146
|
+
const apiTunnel = tunnels.find(t => t.localPort === 3000);
|
|
147
|
+
if (apiTunnel) {
|
|
148
|
+
console.log(`API server at: ${apiTunnel.url}`);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
#### `disconnect()`
|
|
155
|
+
|
|
156
|
+
Disconnect and clean up. Cancels any pending `waitForTunnel` calls.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
async disconnect(): Promise<void>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Example:**
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Always clean up in a finally block
|
|
166
|
+
try {
|
|
167
|
+
const tunnel = await agent.waitForTunnel();
|
|
168
|
+
// Use tunnel...
|
|
169
|
+
} finally {
|
|
170
|
+
await agent.disconnect();
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### Types
|
|
177
|
+
|
|
178
|
+
#### `AgentTunnel`
|
|
179
|
+
|
|
180
|
+
Represents a tunnel connection.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
interface AgentTunnel {
|
|
184
|
+
tunnelId: string; // Unique tunnel identifier
|
|
185
|
+
subdomain: string; // Subdomain (e.g., "swift-fox-a7x2")
|
|
186
|
+
url: string; // Full public URL
|
|
187
|
+
localPort: number; // Local port being tunneled
|
|
188
|
+
createdAt: Date; // When tunnel was created
|
|
189
|
+
expiresAt: Date; // When tunnel will expire
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### `TunnelTimeoutError`
|
|
194
|
+
|
|
195
|
+
Thrown when `waitForTunnel` times out.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
class TunnelTimeoutError extends Error {
|
|
199
|
+
constructor(timeout: number);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### `ApiError`
|
|
204
|
+
|
|
205
|
+
Thrown when an API request fails.
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
class ApiError extends Error {
|
|
209
|
+
statusCode: number; // HTTP status code
|
|
210
|
+
code: string; // Error code (e.g., "INVALID_KEY")
|
|
211
|
+
|
|
212
|
+
constructor(statusCode: number, code: string, message: string);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Error Codes:**
|
|
217
|
+
- `INVALID_KEY` - Bridge key is invalid
|
|
218
|
+
- `EXPIRED_KEY` - Bridge key has expired
|
|
219
|
+
- `REVOKED_KEY` - Bridge key was revoked
|
|
220
|
+
- `USAGE_LIMIT_EXCEEDED` - Key usage limit reached
|
|
221
|
+
- `RATE_LIMIT_EXCEEDED` - Too many requests
|
|
222
|
+
- `TIMEOUT` - Server timeout
|
|
223
|
+
- `TUNNEL_SERVER_ERROR` - Tunnel server error
|
|
224
|
+
- `INTERNAL_ERROR` - Internal server error
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Use Cases
|
|
229
|
+
|
|
230
|
+
### 1. Automated Testing
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { LivePortAgent } from '@liveport/agent-sdk';
|
|
234
|
+
import { test, expect } from 'vitest';
|
|
235
|
+
|
|
236
|
+
let tunnelUrl: string;
|
|
237
|
+
|
|
238
|
+
beforeAll(async () => {
|
|
239
|
+
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
|
|
240
|
+
const tunnel = await agent.waitForTunnel({ timeout: 60000 });
|
|
241
|
+
tunnelUrl = tunnel.url;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('API returns healthy status', async () => {
|
|
245
|
+
const response = await fetch(`${tunnelUrl}/api/health`);
|
|
246
|
+
expect(response.ok).toBe(true);
|
|
247
|
+
|
|
248
|
+
const data = await response.json();
|
|
249
|
+
expect(data.status).toBe('healthy');
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 2. AI Agent Workflow
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { LivePortAgent } from '@liveport/agent-sdk';
|
|
257
|
+
|
|
258
|
+
async function runAITests(bridgeKey: string) {
|
|
259
|
+
const agent = new LivePortAgent({ key: bridgeKey, timeout: 120000 });
|
|
260
|
+
|
|
261
|
+
console.log('Waiting for developer to start tunnel...');
|
|
262
|
+
const tunnel = await agent.waitForTunnel();
|
|
263
|
+
|
|
264
|
+
console.log(`Connected to ${tunnel.url}`);
|
|
265
|
+
|
|
266
|
+
// AI agent can now interact with the local application
|
|
267
|
+
await discoverEndpoints(tunnel.url);
|
|
268
|
+
await runPerformanceTests(tunnel.url);
|
|
269
|
+
await analyzeAPIs(tunnel.url);
|
|
270
|
+
await generateDocumentation(tunnel.url);
|
|
271
|
+
|
|
272
|
+
await agent.disconnect();
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 3. CI/CD Integration
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// tests/setup.ts
|
|
280
|
+
import { LivePortAgent } from '@liveport/agent-sdk';
|
|
281
|
+
|
|
282
|
+
export async function setupTunnel() {
|
|
283
|
+
const agent = new LivePortAgent({
|
|
284
|
+
key: process.env.CI_LIVEPORT_KEY!,
|
|
285
|
+
timeout: 60000
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Wait for tunnel (developer must start it before running tests)
|
|
289
|
+
const tunnel = await agent.waitForTunnel();
|
|
290
|
+
|
|
291
|
+
// Make tunnel URL available to tests
|
|
292
|
+
process.env.TEST_BASE_URL = tunnel.url;
|
|
293
|
+
|
|
294
|
+
return { agent, tunnel };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function teardownTunnel(agent: LivePortAgent) {
|
|
298
|
+
await agent.disconnect();
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### 4. Multi-Service Testing
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { LivePortAgent } from '@liveport/agent-sdk';
|
|
306
|
+
|
|
307
|
+
async function testMicroservices() {
|
|
308
|
+
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
|
|
309
|
+
|
|
310
|
+
// List all available tunnels
|
|
311
|
+
const tunnels = await agent.listTunnels();
|
|
312
|
+
|
|
313
|
+
// Find specific services by port
|
|
314
|
+
const api = tunnels.find(t => t.localPort === 3000);
|
|
315
|
+
const web = tunnels.find(t => t.localPort === 3001);
|
|
316
|
+
const db = tunnels.find(t => t.localPort === 5432);
|
|
317
|
+
|
|
318
|
+
if (!api || !web) {
|
|
319
|
+
throw new Error('Required services not available');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Test API
|
|
323
|
+
const apiHealth = await fetch(`${api.url}/health`);
|
|
324
|
+
console.log(`API: ${apiHealth.status}`);
|
|
325
|
+
|
|
326
|
+
// Test Web
|
|
327
|
+
const webHome = await fetch(web.url);
|
|
328
|
+
console.log(`Web: ${webHome.status}`);
|
|
329
|
+
|
|
330
|
+
await agent.disconnect();
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 5. Webhook Testing
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { LivePortAgent } from '@liveport/agent-sdk';
|
|
338
|
+
|
|
339
|
+
async function testWebhooks() {
|
|
340
|
+
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
|
|
341
|
+
const tunnel = await agent.waitForTunnel();
|
|
342
|
+
|
|
343
|
+
// Register webhook with external service
|
|
344
|
+
await registerWebhook(`${tunnel.url}/webhooks/stripe`);
|
|
345
|
+
|
|
346
|
+
// Trigger webhook event
|
|
347
|
+
await triggerTestEvent();
|
|
348
|
+
|
|
349
|
+
// Verify webhook was received
|
|
350
|
+
const response = await fetch(`${tunnel.url}/webhooks/verify`);
|
|
351
|
+
const result = await response.json();
|
|
352
|
+
|
|
353
|
+
expect(result.received).toBe(true);
|
|
354
|
+
|
|
355
|
+
await agent.disconnect();
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Error Handling
|
|
362
|
+
|
|
363
|
+
### Handling Timeouts
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import { LivePortAgent, TunnelTimeoutError } from '@liveport/agent-sdk';
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
const tunnel = await agent.waitForTunnel({ timeout: 30000 });
|
|
370
|
+
} catch (error) {
|
|
371
|
+
if (error instanceof TunnelTimeoutError) {
|
|
372
|
+
console.error('No tunnel available. Did you forget to run:');
|
|
373
|
+
console.error(' liveport connect <port>');
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
throw error;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Handling API Errors
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import { ApiError } from '@liveport/agent-sdk';
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
const tunnels = await agent.listTunnels();
|
|
387
|
+
} catch (error) {
|
|
388
|
+
if (error instanceof ApiError) {
|
|
389
|
+
console.error(`API Error [${error.code}]: ${error.message}`);
|
|
390
|
+
console.error(`Status: ${error.statusCode}`);
|
|
391
|
+
|
|
392
|
+
if (error.code === 'INVALID_KEY') {
|
|
393
|
+
console.error('Get a new key at: https://liveport.dev/keys');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Robust Error Handling Pattern
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
async function robustTunnelAccess() {
|
|
404
|
+
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
const tunnel = await agent.waitForTunnel({ timeout: 60000 });
|
|
408
|
+
|
|
409
|
+
// Your logic here
|
|
410
|
+
await runTests(tunnel.url);
|
|
411
|
+
|
|
412
|
+
} catch (error) {
|
|
413
|
+
if (error instanceof TunnelTimeoutError) {
|
|
414
|
+
console.error('Timeout waiting for tunnel');
|
|
415
|
+
process.exit(1);
|
|
416
|
+
} else if (error instanceof ApiError) {
|
|
417
|
+
console.error(`API Error: ${error.message}`);
|
|
418
|
+
process.exit(1);
|
|
419
|
+
} else {
|
|
420
|
+
console.error('Unexpected error:', error);
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
} finally {
|
|
424
|
+
await agent.disconnect();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Examples
|
|
432
|
+
|
|
433
|
+
Comprehensive examples are available in the [`examples/`](./examples/) directory:
|
|
434
|
+
|
|
435
|
+
- **[01-basic-usage.ts](./examples/01-basic-usage.ts)** - Simple tunnel connection
|
|
436
|
+
- **[02-testing-integration.ts](./examples/02-testing-integration.ts)** - Vitest integration
|
|
437
|
+
- **[03-ai-agent-workflow.ts](./examples/03-ai-agent-workflow.ts)** - AI agent implementation
|
|
438
|
+
- **[04-multiple-tunnels.ts](./examples/04-multiple-tunnels.ts)** - Multi-service testing
|
|
439
|
+
- **[05-error-handling.ts](./examples/05-error-handling.ts)** - Robust error handling
|
|
440
|
+
|
|
441
|
+
Run examples:
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
# Set your bridge key
|
|
445
|
+
export LIVEPORT_KEY=lpk_your_key_here
|
|
446
|
+
|
|
447
|
+
# Start a tunnel in another terminal
|
|
448
|
+
liveport connect 3000
|
|
449
|
+
|
|
450
|
+
# Run an example
|
|
451
|
+
npx tsx examples/01-basic-usage.ts
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Best Practices
|
|
457
|
+
|
|
458
|
+
### โ
Do's
|
|
459
|
+
|
|
460
|
+
1. **Set reasonable timeouts**
|
|
461
|
+
```typescript
|
|
462
|
+
// Good: 60 second timeout for CI
|
|
463
|
+
const tunnel = await agent.waitForTunnel({ timeout: 60000 });
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
2. **Always clean up**
|
|
467
|
+
```typescript
|
|
468
|
+
try {
|
|
469
|
+
const tunnel = await agent.waitForTunnel();
|
|
470
|
+
// Use tunnel...
|
|
471
|
+
} finally {
|
|
472
|
+
await agent.disconnect(); // Always cleanup
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
3. **Handle specific errors**
|
|
477
|
+
```typescript
|
|
478
|
+
try {
|
|
479
|
+
const tunnel = await agent.waitForTunnel();
|
|
480
|
+
} catch (error) {
|
|
481
|
+
if (error instanceof TunnelTimeoutError) {
|
|
482
|
+
// Provide helpful message
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
4. **Use environment variables**
|
|
488
|
+
```typescript
|
|
489
|
+
// Good
|
|
490
|
+
const agent = new LivePortAgent({
|
|
491
|
+
key: process.env.LIVEPORT_KEY!
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// Bad - never hardcode keys
|
|
495
|
+
const agent = new LivePortAgent({ key: 'lpk_...' });
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
5. **Configure appropriate timeouts**
|
|
499
|
+
```typescript
|
|
500
|
+
// Local development: short timeout
|
|
501
|
+
const tunnel = await agent.waitForTunnel({ timeout: 10000 });
|
|
502
|
+
|
|
503
|
+
// CI/CD: longer timeout
|
|
504
|
+
const tunnel = await agent.waitForTunnel({ timeout: 120000 });
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### โ Don'ts
|
|
508
|
+
|
|
509
|
+
1. **Don't commit bridge keys**
|
|
510
|
+
```bash
|
|
511
|
+
# Use .env.local (git-ignored)
|
|
512
|
+
LIVEPORT_KEY=lpk_your_key_here
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
2. **Don't forget to disconnect**
|
|
516
|
+
```typescript
|
|
517
|
+
// Bad - missing cleanup
|
|
518
|
+
const tunnel = await agent.waitForTunnel();
|
|
519
|
+
// ... tests ...
|
|
520
|
+
// Missing: await agent.disconnect()
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
3. **Don't ignore errors**
|
|
524
|
+
```typescript
|
|
525
|
+
// Bad - swallowing errors
|
|
526
|
+
try {
|
|
527
|
+
const tunnel = await agent.waitForTunnel();
|
|
528
|
+
} catch (error) {
|
|
529
|
+
// Silent failure
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
4. **Don't use production keys in tests**
|
|
534
|
+
```bash
|
|
535
|
+
# Separate keys for different environments
|
|
536
|
+
LIVEPORT_DEV_KEY=lpk_dev_...
|
|
537
|
+
LIVEPORT_CI_KEY=lpk_ci_...
|
|
538
|
+
LIVEPORT_PROD_KEY=lpk_prod_...
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## TypeScript Support
|
|
544
|
+
|
|
545
|
+
This package is written in TypeScript and includes full type definitions.
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
import type {
|
|
549
|
+
LivePortAgent,
|
|
550
|
+
LivePortAgentConfig,
|
|
551
|
+
AgentTunnel,
|
|
552
|
+
WaitForTunnelOptions,
|
|
553
|
+
TunnelTimeoutError,
|
|
554
|
+
ApiError
|
|
555
|
+
} from '@liveport/agent-sdk';
|
|
556
|
+
|
|
557
|
+
// Full IntelliSense support
|
|
558
|
+
const config: LivePortAgentConfig = {
|
|
559
|
+
key: 'lpk_...',
|
|
560
|
+
timeout: 30000
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const agent: LivePortAgent = new LivePortAgent(config);
|
|
564
|
+
const tunnel: AgentTunnel = await agent.waitForTunnel();
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Testing
|
|
570
|
+
|
|
571
|
+
The SDK includes comprehensive tests. Run them with:
|
|
572
|
+
|
|
573
|
+
```bash
|
|
574
|
+
pnpm test
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## Related Packages
|
|
580
|
+
|
|
581
|
+
- **[@liveport/cli](https://www.npmjs.com/package/@liveport/cli)** - CLI for creating tunnels
|
|
582
|
+
- **[Dashboard](https://liveport.dev/dashboard)** - Web interface for managing keys and tunnels
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## Resources
|
|
587
|
+
|
|
588
|
+
- **Documentation**: [liveport.dev/docs](https://liveport.dev/docs)
|
|
589
|
+
- **API Reference**: [liveport.dev/docs](https://liveport.dev/docs)
|
|
590
|
+
- **Dashboard**: [liveport.dev/dashboard](https://liveport.dev/dashboard)
|
|
591
|
+
- **Examples**: [examples/](./examples/)
|
|
592
|
+
- **Support**: [GitHub Issues](https://github.com/dundas/liveport/issues)
|
|
593
|
+
- **Website**: [liveport.dev](https://liveport.dev)
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## Contributing
|
|
598
|
+
|
|
599
|
+
Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) first.
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## License
|
|
604
|
+
|
|
605
|
+
MIT ยฉ LivePort
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
**Built with โค๏ธ for AI agents and developers**
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export { Tunnel } from '@liveport/shared';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LivePort Agent SDK
|
|
5
|
+
*
|
|
6
|
+
* TypeScript SDK for AI agents to wait for and access localhost tunnels.
|
|
7
|
+
*/
|
|
8
|
+
interface LivePortAgentConfig {
|
|
9
|
+
/** Bridge key for authentication */
|
|
10
|
+
key: string;
|
|
11
|
+
/** API base URL (default: https://app.liveport.dev) */
|
|
12
|
+
apiUrl?: string;
|
|
13
|
+
/** Default timeout in milliseconds (default: 30000) */
|
|
14
|
+
timeout?: number;
|
|
15
|
+
}
|
|
16
|
+
interface WaitForTunnelOptions {
|
|
17
|
+
/** Timeout in milliseconds (default: 30000) */
|
|
18
|
+
timeout?: number;
|
|
19
|
+
/** Poll interval in milliseconds (default: 1000) */
|
|
20
|
+
pollInterval?: number;
|
|
21
|
+
}
|
|
22
|
+
interface AgentTunnel {
|
|
23
|
+
tunnelId: string;
|
|
24
|
+
subdomain: string;
|
|
25
|
+
url: string;
|
|
26
|
+
localPort: number;
|
|
27
|
+
createdAt: Date;
|
|
28
|
+
expiresAt: Date;
|
|
29
|
+
}
|
|
30
|
+
/** Error thrown when tunnel wait times out */
|
|
31
|
+
declare class TunnelTimeoutError extends Error {
|
|
32
|
+
constructor(timeout: number);
|
|
33
|
+
}
|
|
34
|
+
/** Error thrown when API request fails */
|
|
35
|
+
declare class ApiError extends Error {
|
|
36
|
+
readonly statusCode: number;
|
|
37
|
+
readonly code: string;
|
|
38
|
+
constructor(statusCode: number, code: string, message: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* LivePort Agent SDK
|
|
42
|
+
*
|
|
43
|
+
* Allows AI agents to wait for and access localhost tunnels.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import { LivePortAgent } from '@liveport/agent-sdk';
|
|
48
|
+
*
|
|
49
|
+
* const agent = new LivePortAgent({
|
|
50
|
+
* key: process.env.LIVEPORT_BRIDGE_KEY!
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // Wait for a tunnel to become available
|
|
54
|
+
* const tunnel = await agent.waitForTunnel({ timeout: 30000 });
|
|
55
|
+
* console.log(`Testing at: ${tunnel.url}`);
|
|
56
|
+
*
|
|
57
|
+
* // Run your tests against tunnel.url
|
|
58
|
+
*
|
|
59
|
+
* // Clean up when done
|
|
60
|
+
* await agent.disconnect();
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
declare class LivePortAgent {
|
|
64
|
+
private config;
|
|
65
|
+
private abortController;
|
|
66
|
+
constructor(config: LivePortAgentConfig);
|
|
67
|
+
/**
|
|
68
|
+
* Wait for a tunnel to become available
|
|
69
|
+
*
|
|
70
|
+
* Long-polls the API until a tunnel is ready or timeout is reached.
|
|
71
|
+
*
|
|
72
|
+
* @param options - Wait options
|
|
73
|
+
* @returns The tunnel info once available
|
|
74
|
+
* @throws TunnelTimeoutError if no tunnel becomes available within timeout
|
|
75
|
+
* @throws ApiError if the API request fails
|
|
76
|
+
*/
|
|
77
|
+
waitForTunnel(options?: WaitForTunnelOptions): Promise<AgentTunnel>;
|
|
78
|
+
/**
|
|
79
|
+
* List all active tunnels for this bridge key
|
|
80
|
+
*
|
|
81
|
+
* @returns Array of active tunnels
|
|
82
|
+
* @throws ApiError if the API request fails
|
|
83
|
+
*/
|
|
84
|
+
listTunnels(): Promise<AgentTunnel[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Disconnect and clean up
|
|
87
|
+
*
|
|
88
|
+
* Cancels any pending waitForTunnel calls.
|
|
89
|
+
*/
|
|
90
|
+
disconnect(): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Make an authenticated API request
|
|
93
|
+
*/
|
|
94
|
+
private makeRequest;
|
|
95
|
+
/**
|
|
96
|
+
* Parse tunnel response into AgentTunnel
|
|
97
|
+
*/
|
|
98
|
+
private parseTunnel;
|
|
99
|
+
/**
|
|
100
|
+
* Sleep helper
|
|
101
|
+
*/
|
|
102
|
+
private sleep;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { type AgentTunnel, ApiError, LivePortAgent, type LivePortAgentConfig, TunnelTimeoutError, type WaitForTunnelOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export { Tunnel } from '@liveport/shared';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LivePort Agent SDK
|
|
5
|
+
*
|
|
6
|
+
* TypeScript SDK for AI agents to wait for and access localhost tunnels.
|
|
7
|
+
*/
|
|
8
|
+
interface LivePortAgentConfig {
|
|
9
|
+
/** Bridge key for authentication */
|
|
10
|
+
key: string;
|
|
11
|
+
/** API base URL (default: https://app.liveport.dev) */
|
|
12
|
+
apiUrl?: string;
|
|
13
|
+
/** Default timeout in milliseconds (default: 30000) */
|
|
14
|
+
timeout?: number;
|
|
15
|
+
}
|
|
16
|
+
interface WaitForTunnelOptions {
|
|
17
|
+
/** Timeout in milliseconds (default: 30000) */
|
|
18
|
+
timeout?: number;
|
|
19
|
+
/** Poll interval in milliseconds (default: 1000) */
|
|
20
|
+
pollInterval?: number;
|
|
21
|
+
}
|
|
22
|
+
interface AgentTunnel {
|
|
23
|
+
tunnelId: string;
|
|
24
|
+
subdomain: string;
|
|
25
|
+
url: string;
|
|
26
|
+
localPort: number;
|
|
27
|
+
createdAt: Date;
|
|
28
|
+
expiresAt: Date;
|
|
29
|
+
}
|
|
30
|
+
/** Error thrown when tunnel wait times out */
|
|
31
|
+
declare class TunnelTimeoutError extends Error {
|
|
32
|
+
constructor(timeout: number);
|
|
33
|
+
}
|
|
34
|
+
/** Error thrown when API request fails */
|
|
35
|
+
declare class ApiError extends Error {
|
|
36
|
+
readonly statusCode: number;
|
|
37
|
+
readonly code: string;
|
|
38
|
+
constructor(statusCode: number, code: string, message: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* LivePort Agent SDK
|
|
42
|
+
*
|
|
43
|
+
* Allows AI agents to wait for and access localhost tunnels.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import { LivePortAgent } from '@liveport/agent-sdk';
|
|
48
|
+
*
|
|
49
|
+
* const agent = new LivePortAgent({
|
|
50
|
+
* key: process.env.LIVEPORT_BRIDGE_KEY!
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // Wait for a tunnel to become available
|
|
54
|
+
* const tunnel = await agent.waitForTunnel({ timeout: 30000 });
|
|
55
|
+
* console.log(`Testing at: ${tunnel.url}`);
|
|
56
|
+
*
|
|
57
|
+
* // Run your tests against tunnel.url
|
|
58
|
+
*
|
|
59
|
+
* // Clean up when done
|
|
60
|
+
* await agent.disconnect();
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
declare class LivePortAgent {
|
|
64
|
+
private config;
|
|
65
|
+
private abortController;
|
|
66
|
+
constructor(config: LivePortAgentConfig);
|
|
67
|
+
/**
|
|
68
|
+
* Wait for a tunnel to become available
|
|
69
|
+
*
|
|
70
|
+
* Long-polls the API until a tunnel is ready or timeout is reached.
|
|
71
|
+
*
|
|
72
|
+
* @param options - Wait options
|
|
73
|
+
* @returns The tunnel info once available
|
|
74
|
+
* @throws TunnelTimeoutError if no tunnel becomes available within timeout
|
|
75
|
+
* @throws ApiError if the API request fails
|
|
76
|
+
*/
|
|
77
|
+
waitForTunnel(options?: WaitForTunnelOptions): Promise<AgentTunnel>;
|
|
78
|
+
/**
|
|
79
|
+
* List all active tunnels for this bridge key
|
|
80
|
+
*
|
|
81
|
+
* @returns Array of active tunnels
|
|
82
|
+
* @throws ApiError if the API request fails
|
|
83
|
+
*/
|
|
84
|
+
listTunnels(): Promise<AgentTunnel[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Disconnect and clean up
|
|
87
|
+
*
|
|
88
|
+
* Cancels any pending waitForTunnel calls.
|
|
89
|
+
*/
|
|
90
|
+
disconnect(): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Make an authenticated API request
|
|
93
|
+
*/
|
|
94
|
+
private makeRequest;
|
|
95
|
+
/**
|
|
96
|
+
* Parse tunnel response into AgentTunnel
|
|
97
|
+
*/
|
|
98
|
+
private parseTunnel;
|
|
99
|
+
/**
|
|
100
|
+
* Sleep helper
|
|
101
|
+
*/
|
|
102
|
+
private sleep;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { type AgentTunnel, ApiError, LivePortAgent, type LivePortAgentConfig, TunnelTimeoutError, type WaitForTunnelOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ApiError: () => ApiError,
|
|
24
|
+
LivePortAgent: () => LivePortAgent,
|
|
25
|
+
TunnelTimeoutError: () => TunnelTimeoutError
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
var TunnelTimeoutError = class extends Error {
|
|
29
|
+
constructor(timeout) {
|
|
30
|
+
super(`Tunnel not available within ${timeout}ms timeout`);
|
|
31
|
+
this.name = "TunnelTimeoutError";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var ApiError = class extends Error {
|
|
35
|
+
statusCode;
|
|
36
|
+
code;
|
|
37
|
+
constructor(statusCode, code, message) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "ApiError";
|
|
40
|
+
this.statusCode = statusCode;
|
|
41
|
+
this.code = code;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var LivePortAgent = class {
|
|
45
|
+
config;
|
|
46
|
+
abortController = null;
|
|
47
|
+
constructor(config) {
|
|
48
|
+
if (!config.key) {
|
|
49
|
+
throw new Error("Bridge key is required");
|
|
50
|
+
}
|
|
51
|
+
this.config = {
|
|
52
|
+
key: config.key,
|
|
53
|
+
apiUrl: config.apiUrl || "https://app.liveport.dev",
|
|
54
|
+
timeout: config.timeout || 3e4
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Wait for a tunnel to become available
|
|
59
|
+
*
|
|
60
|
+
* Long-polls the API until a tunnel is ready or timeout is reached.
|
|
61
|
+
*
|
|
62
|
+
* @param options - Wait options
|
|
63
|
+
* @returns The tunnel info once available
|
|
64
|
+
* @throws TunnelTimeoutError if no tunnel becomes available within timeout
|
|
65
|
+
* @throws ApiError if the API request fails
|
|
66
|
+
*/
|
|
67
|
+
async waitForTunnel(options) {
|
|
68
|
+
const timeout = options?.timeout ?? this.config.timeout;
|
|
69
|
+
const pollInterval = options?.pollInterval ?? 1e3;
|
|
70
|
+
this.abortController = new AbortController();
|
|
71
|
+
const startTime = Date.now();
|
|
72
|
+
while (Date.now() - startTime < timeout) {
|
|
73
|
+
try {
|
|
74
|
+
const response = await this.makeRequest(
|
|
75
|
+
`/api/agent/tunnels/wait?timeout=${Math.min(pollInterval * 5, timeout - (Date.now() - startTime))}`,
|
|
76
|
+
{ signal: this.abortController.signal }
|
|
77
|
+
);
|
|
78
|
+
if (response.ok) {
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
if (data.tunnel) {
|
|
81
|
+
return this.parseTunnel(data.tunnel);
|
|
82
|
+
}
|
|
83
|
+
} else if (response.status === 408) {
|
|
84
|
+
} else {
|
|
85
|
+
const error = await response.json().catch(() => ({ code: "UNKNOWN", message: "Request failed" }));
|
|
86
|
+
throw new ApiError(response.status, error.code, error.message);
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
if (err instanceof ApiError) throw err;
|
|
90
|
+
if (err.name === "AbortError") {
|
|
91
|
+
throw new Error("Wait cancelled");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
await this.sleep(pollInterval);
|
|
95
|
+
}
|
|
96
|
+
throw new TunnelTimeoutError(timeout);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* List all active tunnels for this bridge key
|
|
100
|
+
*
|
|
101
|
+
* @returns Array of active tunnels
|
|
102
|
+
* @throws ApiError if the API request fails
|
|
103
|
+
*/
|
|
104
|
+
async listTunnels() {
|
|
105
|
+
const response = await this.makeRequest("/api/agent/tunnels");
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
const error = await response.json().catch(() => ({ code: "UNKNOWN", message: "Request failed" }));
|
|
108
|
+
throw new ApiError(response.status, error.code, error.message);
|
|
109
|
+
}
|
|
110
|
+
const data = await response.json();
|
|
111
|
+
return (data.tunnels || []).map((t) => this.parseTunnel(t));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Disconnect and clean up
|
|
115
|
+
*
|
|
116
|
+
* Cancels any pending waitForTunnel calls.
|
|
117
|
+
*/
|
|
118
|
+
async disconnect() {
|
|
119
|
+
if (this.abortController) {
|
|
120
|
+
this.abortController.abort();
|
|
121
|
+
this.abortController = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Make an authenticated API request
|
|
126
|
+
*/
|
|
127
|
+
async makeRequest(path, options = {}) {
|
|
128
|
+
const url = `${this.config.apiUrl}${path}`;
|
|
129
|
+
return fetch(url, {
|
|
130
|
+
...options,
|
|
131
|
+
headers: {
|
|
132
|
+
"Authorization": `Bearer ${this.config.key}`,
|
|
133
|
+
"Content-Type": "application/json",
|
|
134
|
+
...options.headers
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Parse tunnel response into AgentTunnel
|
|
140
|
+
*/
|
|
141
|
+
parseTunnel(data) {
|
|
142
|
+
return {
|
|
143
|
+
tunnelId: data.tunnelId || data.id,
|
|
144
|
+
subdomain: data.subdomain,
|
|
145
|
+
url: data.url,
|
|
146
|
+
localPort: data.localPort,
|
|
147
|
+
createdAt: new Date(data.createdAt),
|
|
148
|
+
expiresAt: new Date(data.expiresAt)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Sleep helper
|
|
153
|
+
*/
|
|
154
|
+
sleep(ms) {
|
|
155
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
159
|
+
0 && (module.exports = {
|
|
160
|
+
ApiError,
|
|
161
|
+
LivePortAgent,
|
|
162
|
+
TunnelTimeoutError
|
|
163
|
+
});
|
|
164
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * LivePort Agent SDK\n *\n * TypeScript SDK for AI agents to wait for and access localhost tunnels.\n */\n\nimport type { Tunnel } from \"@liveport/shared\";\n\nexport interface LivePortAgentConfig {\n /** Bridge key for authentication */\n key: string;\n /** API base URL (default: https://app.liveport.dev) */\n apiUrl?: string;\n /** Default timeout in milliseconds (default: 30000) */\n timeout?: number;\n}\n\nexport interface WaitForTunnelOptions {\n /** Timeout in milliseconds (default: 30000) */\n timeout?: number;\n /** Poll interval in milliseconds (default: 1000) */\n pollInterval?: number;\n}\n\nexport interface AgentTunnel {\n tunnelId: string;\n subdomain: string;\n url: string;\n localPort: number;\n createdAt: Date;\n expiresAt: Date;\n}\n\n/** Error thrown when tunnel wait times out */\nexport class TunnelTimeoutError extends Error {\n constructor(timeout: number) {\n super(`Tunnel not available within ${timeout}ms timeout`);\n this.name = \"TunnelTimeoutError\";\n }\n}\n\n/** Error thrown when API request fails */\nexport class ApiError extends Error {\n public readonly statusCode: number;\n public readonly code: string;\n\n constructor(statusCode: number, code: string, message: string) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.code = code;\n }\n}\n\n/**\n * LivePort Agent SDK\n *\n * Allows AI agents to wait for and access localhost tunnels.\n *\n * @example\n * ```typescript\n * import { LivePortAgent } from '@liveport/agent-sdk';\n *\n * const agent = new LivePortAgent({\n * key: process.env.LIVEPORT_BRIDGE_KEY!\n * });\n *\n * // Wait for a tunnel to become available\n * const tunnel = await agent.waitForTunnel({ timeout: 30000 });\n * console.log(`Testing at: ${tunnel.url}`);\n *\n * // Run your tests against tunnel.url\n *\n * // Clean up when done\n * await agent.disconnect();\n * ```\n */\nexport class LivePortAgent {\n private config: Required<LivePortAgentConfig>;\n private abortController: AbortController | null = null;\n\n constructor(config: LivePortAgentConfig) {\n if (!config.key) {\n throw new Error(\"Bridge key is required\");\n }\n\n this.config = {\n key: config.key,\n apiUrl: config.apiUrl || \"https://app.liveport.dev\",\n timeout: config.timeout || 30000,\n };\n }\n\n /**\n * Wait for a tunnel to become available\n *\n * Long-polls the API until a tunnel is ready or timeout is reached.\n *\n * @param options - Wait options\n * @returns The tunnel info once available\n * @throws TunnelTimeoutError if no tunnel becomes available within timeout\n * @throws ApiError if the API request fails\n */\n async waitForTunnel(options?: WaitForTunnelOptions): Promise<AgentTunnel> {\n const timeout = options?.timeout ?? this.config.timeout;\n const pollInterval = options?.pollInterval ?? 1000;\n\n this.abortController = new AbortController();\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n try {\n const response = await this.makeRequest(\n `/api/agent/tunnels/wait?timeout=${Math.min(pollInterval * 5, timeout - (Date.now() - startTime))}`,\n { signal: this.abortController.signal }\n );\n\n if (response.ok) {\n const data = await response.json() as { tunnel?: Record<string, unknown> };\n if (data.tunnel) {\n return this.parseTunnel(data.tunnel);\n }\n } else if (response.status === 408) {\n // Timeout from server, continue polling\n } else {\n const error = await response.json().catch(() => ({ code: \"UNKNOWN\", message: \"Request failed\" })) as { code: string; message: string };\n throw new ApiError(response.status, error.code, error.message);\n }\n } catch (err) {\n if (err instanceof ApiError) throw err;\n if ((err as Error).name === \"AbortError\") {\n throw new Error(\"Wait cancelled\");\n }\n // Network error, wait and retry\n }\n\n // Wait before next poll\n await this.sleep(pollInterval);\n }\n\n throw new TunnelTimeoutError(timeout);\n }\n\n /**\n * List all active tunnels for this bridge key\n *\n * @returns Array of active tunnels\n * @throws ApiError if the API request fails\n */\n async listTunnels(): Promise<AgentTunnel[]> {\n const response = await this.makeRequest(\"/api/agent/tunnels\");\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ code: \"UNKNOWN\", message: \"Request failed\" })) as { code: string; message: string };\n throw new ApiError(response.status, error.code, error.message);\n }\n\n const data = await response.json() as { tunnels?: Record<string, unknown>[] };\n return (data.tunnels || []).map((t) => this.parseTunnel(t));\n }\n\n /**\n * Disconnect and clean up\n *\n * Cancels any pending waitForTunnel calls.\n */\n async disconnect(): Promise<void> {\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n /**\n * Make an authenticated API request\n */\n private async makeRequest(\n path: string,\n options: RequestInit = {}\n ): Promise<Response> {\n const url = `${this.config.apiUrl}${path}`;\n\n return fetch(url, {\n ...options,\n headers: {\n \"Authorization\": `Bearer ${this.config.key}`,\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n });\n }\n\n /**\n * Parse tunnel response into AgentTunnel\n */\n private parseTunnel(data: Record<string, unknown>): AgentTunnel {\n return {\n tunnelId: data.tunnelId as string || data.id as string,\n subdomain: data.subdomain as string,\n url: data.url as string,\n localPort: data.localPort as number,\n createdAt: new Date(data.createdAt as string),\n expiresAt: new Date(data.expiresAt as string),\n };\n }\n\n /**\n * Sleep helper\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n// Re-export types\nexport type { Tunnel } from \"@liveport/shared\";\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var TunnelTimeoutError = class extends Error {
|
|
3
|
+
constructor(timeout) {
|
|
4
|
+
super(`Tunnel not available within ${timeout}ms timeout`);
|
|
5
|
+
this.name = "TunnelTimeoutError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var ApiError = class extends Error {
|
|
9
|
+
statusCode;
|
|
10
|
+
code;
|
|
11
|
+
constructor(statusCode, code, message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "ApiError";
|
|
14
|
+
this.statusCode = statusCode;
|
|
15
|
+
this.code = code;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var LivePortAgent = class {
|
|
19
|
+
config;
|
|
20
|
+
abortController = null;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
if (!config.key) {
|
|
23
|
+
throw new Error("Bridge key is required");
|
|
24
|
+
}
|
|
25
|
+
this.config = {
|
|
26
|
+
key: config.key,
|
|
27
|
+
apiUrl: config.apiUrl || "https://app.liveport.dev",
|
|
28
|
+
timeout: config.timeout || 3e4
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Wait for a tunnel to become available
|
|
33
|
+
*
|
|
34
|
+
* Long-polls the API until a tunnel is ready or timeout is reached.
|
|
35
|
+
*
|
|
36
|
+
* @param options - Wait options
|
|
37
|
+
* @returns The tunnel info once available
|
|
38
|
+
* @throws TunnelTimeoutError if no tunnel becomes available within timeout
|
|
39
|
+
* @throws ApiError if the API request fails
|
|
40
|
+
*/
|
|
41
|
+
async waitForTunnel(options) {
|
|
42
|
+
const timeout = options?.timeout ?? this.config.timeout;
|
|
43
|
+
const pollInterval = options?.pollInterval ?? 1e3;
|
|
44
|
+
this.abortController = new AbortController();
|
|
45
|
+
const startTime = Date.now();
|
|
46
|
+
while (Date.now() - startTime < timeout) {
|
|
47
|
+
try {
|
|
48
|
+
const response = await this.makeRequest(
|
|
49
|
+
`/api/agent/tunnels/wait?timeout=${Math.min(pollInterval * 5, timeout - (Date.now() - startTime))}`,
|
|
50
|
+
{ signal: this.abortController.signal }
|
|
51
|
+
);
|
|
52
|
+
if (response.ok) {
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
if (data.tunnel) {
|
|
55
|
+
return this.parseTunnel(data.tunnel);
|
|
56
|
+
}
|
|
57
|
+
} else if (response.status === 408) {
|
|
58
|
+
} else {
|
|
59
|
+
const error = await response.json().catch(() => ({ code: "UNKNOWN", message: "Request failed" }));
|
|
60
|
+
throw new ApiError(response.status, error.code, error.message);
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if (err instanceof ApiError) throw err;
|
|
64
|
+
if (err.name === "AbortError") {
|
|
65
|
+
throw new Error("Wait cancelled");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
await this.sleep(pollInterval);
|
|
69
|
+
}
|
|
70
|
+
throw new TunnelTimeoutError(timeout);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* List all active tunnels for this bridge key
|
|
74
|
+
*
|
|
75
|
+
* @returns Array of active tunnels
|
|
76
|
+
* @throws ApiError if the API request fails
|
|
77
|
+
*/
|
|
78
|
+
async listTunnels() {
|
|
79
|
+
const response = await this.makeRequest("/api/agent/tunnels");
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
const error = await response.json().catch(() => ({ code: "UNKNOWN", message: "Request failed" }));
|
|
82
|
+
throw new ApiError(response.status, error.code, error.message);
|
|
83
|
+
}
|
|
84
|
+
const data = await response.json();
|
|
85
|
+
return (data.tunnels || []).map((t) => this.parseTunnel(t));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Disconnect and clean up
|
|
89
|
+
*
|
|
90
|
+
* Cancels any pending waitForTunnel calls.
|
|
91
|
+
*/
|
|
92
|
+
async disconnect() {
|
|
93
|
+
if (this.abortController) {
|
|
94
|
+
this.abortController.abort();
|
|
95
|
+
this.abortController = null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Make an authenticated API request
|
|
100
|
+
*/
|
|
101
|
+
async makeRequest(path, options = {}) {
|
|
102
|
+
const url = `${this.config.apiUrl}${path}`;
|
|
103
|
+
return fetch(url, {
|
|
104
|
+
...options,
|
|
105
|
+
headers: {
|
|
106
|
+
"Authorization": `Bearer ${this.config.key}`,
|
|
107
|
+
"Content-Type": "application/json",
|
|
108
|
+
...options.headers
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Parse tunnel response into AgentTunnel
|
|
114
|
+
*/
|
|
115
|
+
parseTunnel(data) {
|
|
116
|
+
return {
|
|
117
|
+
tunnelId: data.tunnelId || data.id,
|
|
118
|
+
subdomain: data.subdomain,
|
|
119
|
+
url: data.url,
|
|
120
|
+
localPort: data.localPort,
|
|
121
|
+
createdAt: new Date(data.createdAt),
|
|
122
|
+
expiresAt: new Date(data.expiresAt)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Sleep helper
|
|
127
|
+
*/
|
|
128
|
+
sleep(ms) {
|
|
129
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
export {
|
|
133
|
+
ApiError,
|
|
134
|
+
LivePortAgent,
|
|
135
|
+
TunnelTimeoutError
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * LivePort Agent SDK\n *\n * TypeScript SDK for AI agents to wait for and access localhost tunnels.\n */\n\nimport type { Tunnel } from \"@liveport/shared\";\n\nexport interface LivePortAgentConfig {\n /** Bridge key for authentication */\n key: string;\n /** API base URL (default: https://app.liveport.dev) */\n apiUrl?: string;\n /** Default timeout in milliseconds (default: 30000) */\n timeout?: number;\n}\n\nexport interface WaitForTunnelOptions {\n /** Timeout in milliseconds (default: 30000) */\n timeout?: number;\n /** Poll interval in milliseconds (default: 1000) */\n pollInterval?: number;\n}\n\nexport interface AgentTunnel {\n tunnelId: string;\n subdomain: string;\n url: string;\n localPort: number;\n createdAt: Date;\n expiresAt: Date;\n}\n\n/** Error thrown when tunnel wait times out */\nexport class TunnelTimeoutError extends Error {\n constructor(timeout: number) {\n super(`Tunnel not available within ${timeout}ms timeout`);\n this.name = \"TunnelTimeoutError\";\n }\n}\n\n/** Error thrown when API request fails */\nexport class ApiError extends Error {\n public readonly statusCode: number;\n public readonly code: string;\n\n constructor(statusCode: number, code: string, message: string) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.code = code;\n }\n}\n\n/**\n * LivePort Agent SDK\n *\n * Allows AI agents to wait for and access localhost tunnels.\n *\n * @example\n * ```typescript\n * import { LivePortAgent } from '@liveport/agent-sdk';\n *\n * const agent = new LivePortAgent({\n * key: process.env.LIVEPORT_BRIDGE_KEY!\n * });\n *\n * // Wait for a tunnel to become available\n * const tunnel = await agent.waitForTunnel({ timeout: 30000 });\n * console.log(`Testing at: ${tunnel.url}`);\n *\n * // Run your tests against tunnel.url\n *\n * // Clean up when done\n * await agent.disconnect();\n * ```\n */\nexport class LivePortAgent {\n private config: Required<LivePortAgentConfig>;\n private abortController: AbortController | null = null;\n\n constructor(config: LivePortAgentConfig) {\n if (!config.key) {\n throw new Error(\"Bridge key is required\");\n }\n\n this.config = {\n key: config.key,\n apiUrl: config.apiUrl || \"https://app.liveport.dev\",\n timeout: config.timeout || 30000,\n };\n }\n\n /**\n * Wait for a tunnel to become available\n *\n * Long-polls the API until a tunnel is ready or timeout is reached.\n *\n * @param options - Wait options\n * @returns The tunnel info once available\n * @throws TunnelTimeoutError if no tunnel becomes available within timeout\n * @throws ApiError if the API request fails\n */\n async waitForTunnel(options?: WaitForTunnelOptions): Promise<AgentTunnel> {\n const timeout = options?.timeout ?? this.config.timeout;\n const pollInterval = options?.pollInterval ?? 1000;\n\n this.abortController = new AbortController();\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n try {\n const response = await this.makeRequest(\n `/api/agent/tunnels/wait?timeout=${Math.min(pollInterval * 5, timeout - (Date.now() - startTime))}`,\n { signal: this.abortController.signal }\n );\n\n if (response.ok) {\n const data = await response.json() as { tunnel?: Record<string, unknown> };\n if (data.tunnel) {\n return this.parseTunnel(data.tunnel);\n }\n } else if (response.status === 408) {\n // Timeout from server, continue polling\n } else {\n const error = await response.json().catch(() => ({ code: \"UNKNOWN\", message: \"Request failed\" })) as { code: string; message: string };\n throw new ApiError(response.status, error.code, error.message);\n }\n } catch (err) {\n if (err instanceof ApiError) throw err;\n if ((err as Error).name === \"AbortError\") {\n throw new Error(\"Wait cancelled\");\n }\n // Network error, wait and retry\n }\n\n // Wait before next poll\n await this.sleep(pollInterval);\n }\n\n throw new TunnelTimeoutError(timeout);\n }\n\n /**\n * List all active tunnels for this bridge key\n *\n * @returns Array of active tunnels\n * @throws ApiError if the API request fails\n */\n async listTunnels(): Promise<AgentTunnel[]> {\n const response = await this.makeRequest(\"/api/agent/tunnels\");\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ code: \"UNKNOWN\", message: \"Request failed\" })) as { code: string; message: string };\n throw new ApiError(response.status, error.code, error.message);\n }\n\n const data = await response.json() as { tunnels?: Record<string, unknown>[] };\n return (data.tunnels || []).map((t) => this.parseTunnel(t));\n }\n\n /**\n * Disconnect and clean up\n *\n * Cancels any pending waitForTunnel calls.\n */\n async disconnect(): Promise<void> {\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n /**\n * Make an authenticated API request\n */\n private async makeRequest(\n path: string,\n options: RequestInit = {}\n ): Promise<Response> {\n const url = `${this.config.apiUrl}${path}`;\n\n return fetch(url, {\n ...options,\n headers: {\n \"Authorization\": `Bearer ${this.config.key}`,\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n });\n }\n\n /**\n * Parse tunnel response into AgentTunnel\n */\n private parseTunnel(data: Record<string, unknown>): AgentTunnel {\n return {\n tunnelId: data.tunnelId as string || data.id as string,\n subdomain: data.subdomain as string,\n url: data.url as string,\n localPort: data.localPort as number,\n createdAt: new Date(data.createdAt as string),\n expiresAt: new Date(data.expiresAt as string),\n };\n }\n\n /**\n * Sleep helper\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n// Re-export types\nexport type { Tunnel } from \"@liveport/shared\";\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@liveport/agent-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for AI agents to access LivePort tunnels",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"lint": "eslint src/",
|
|
23
|
+
"test": "vitest run --passWithNoTests",
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"clean": "rm -rf dist",
|
|
26
|
+
"prepublishOnly": "pnpm build"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"sdk",
|
|
30
|
+
"ai-agents",
|
|
31
|
+
"tunnel",
|
|
32
|
+
"localhost",
|
|
33
|
+
"liveport",
|
|
34
|
+
"testing",
|
|
35
|
+
"claude",
|
|
36
|
+
"gpt",
|
|
37
|
+
"automation"
|
|
38
|
+
],
|
|
39
|
+
"author": "LivePort",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"homepage": "https://liveport.dev",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/dundas/liveport/issues"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/dundas/liveport.git",
|
|
48
|
+
"directory": "packages/agent-sdk"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^20.10.0",
|
|
58
|
+
"tsup": "^8.0.0",
|
|
59
|
+
"typescript": "^5.3.0",
|
|
60
|
+
"vitest": "^2.0.0"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@liveport/shared": "workspace:*"
|
|
64
|
+
}
|
|
65
|
+
}
|