Skip to content

Commit 03b7479

Browse files
authored
fix(tunnels): extract tunnel domain from base url (#755)
1 parent b5d13dc commit 03b7479

3 files changed

Lines changed: 24 additions & 3 deletions

File tree

src/sdk/devbox.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,9 @@ export class Devbox {
838838
if (!tunnel) {
839839
throw new RunloopError('No tunnel has been enabled for this devbox. Call net.enableTunnel() first.');
840840
}
841-
return `https://${port}-${tunnel.tunnel_key}.tunnel.runloop.ai`;
841+
const apiHost = new URL(this.client.baseURL).hostname;
842+
const baseDomain = apiHost.startsWith('api.') ? apiHost.slice(4) : apiHost;
843+
return `https://${port}-${tunnel.tunnel_key}.tunnel.${baseDomain}`;
842844
}
843845

844846
/**

tests/objects/devbox.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe('Devbox (New API)', () => {
1111
beforeEach(() => {
1212
// Create mock client instance with proper structure
1313
mockClient = {
14+
baseURL: 'https://api.runloop.ai',
1415
devboxes: {
1516
createAndAwaitRunning: jest.fn(),
1617
retrieve: jest.fn(),
@@ -303,6 +304,24 @@ describe('Devbox (New API)', () => {
303304
expect(url).toBe('https://3000-mykey456.tunnel.runloop.ai');
304305
});
305306

307+
it('should derive tunnel domain from client baseURL', async () => {
308+
const mockTunnel = {
309+
tunnel_key: 'abc123xyz',
310+
auth_mode: 'open' as const,
311+
create_time_ms: Date.now(),
312+
};
313+
const dataWithTunnel = { ...mockDevboxData, tunnel: mockTunnel };
314+
mockClient.devboxes.retrieve.mockResolvedValue(dataWithTunnel);
315+
316+
mockClient.baseURL = 'https://api.runloop.pro';
317+
expect(await devbox.getTunnelUrl(8080)).toBe('https://8080-abc123xyz.tunnel.runloop.pro');
318+
319+
mockClient.baseURL = 'http://127.0.0.1:8080';
320+
expect(await devbox.getTunnelUrl(8080)).toBe('https://8080-abc123xyz.tunnel.127.0.0.1');
321+
322+
mockClient.baseURL = 'https://api.runloop.ai';
323+
});
324+
306325
it('should throw RunloopError when no tunnel has been enabled', async () => {
307326
const dataWithoutTunnel = { ...mockDevboxData, tunnel: null };
308327
mockClient.devboxes.retrieve.mockResolvedValue(dataWithoutTunnel);

tests/smoketests/object-oriented/devbox.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,10 +413,10 @@ describe('smoketest: object-oriented devbox', () => {
413413
expect(tunnel.tunnel_key).toBeTruthy();
414414

415415
const url = await devbox.getTunnelUrl(8080);
416-
expect(url).toBe(`https://8080-${tunnel.tunnel_key}.tunnel.runloop.ai`);
416+
expect(url).toContain(`https://8080-${tunnel.tunnel_key}.tunnel.runloop.`);
417417

418418
const url3000 = await devbox.getTunnelUrl(3000);
419-
expect(url3000).toBe(`https://3000-${tunnel.tunnel_key}.tunnel.runloop.ai`);
419+
expect(url3000).toContain(`https://3000-${tunnel.tunnel_key}.tunnel.runloop.`);
420420
} finally {
421421
await devbox.shutdown();
422422
}

0 commit comments

Comments
 (0)