From 5076da21a6729b4d288b530f78cc0e16415175d7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:57:44 +0000 Subject: [PATCH 1/2] chore: remove create_tunnel endpoint --- .stats.yml | 8 +-- api.md | 2 - packages/mcp-server/manifest.json | 2 +- packages/mcp-server/package.json | 2 +- packages/mcp-server/src/code-tool-paths.cts | 4 +- packages/mcp-server/src/code-tool.ts | 51 +++++++------------ packages/mcp-server/src/http.ts | 46 +---------------- packages/mcp-server/src/instructions.ts | 25 +++++---- packages/mcp-server/src/server.ts | 4 +- packages/mcp-server/src/types.ts | 1 - src/index.ts | 4 -- src/resources/benchmark-jobs.ts | 2 +- src/resources/benchmark-runs.ts | 6 ++- src/resources/devboxes/devboxes.ts | 43 ---------------- src/resources/devboxes/index.ts | 2 - src/resources/devboxes/tools.ts | 10 +--- src/resources/index.ts | 2 - src/sdk/devbox.ts | 25 --------- tests/api-resources/devboxes/devboxes.test.ts | 15 ------ tests/objects/devbox.test.ts | 22 ++++---- 20 files changed, 62 insertions(+), 214 deletions(-) diff --git a/.stats.yml b/.stats.yml index a784436d5..ab8cc33c0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-132ed160716591bdcd9231c00da8c506d9451a5486b165fc27b2a01d93202082.yml -openapi_spec_hash: c2b44d9e9cda56e32141a7ea3794bbba -config_hash: 3bd89c812b96708c461fb98286ebf0b5 +configured_endpoints: 117 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-0568973e19e8af9fa953b2ded109ab2b69e76e90e2b74f33617dbf7092e26274.yml +openapi_spec_hash: 10ba804ce69510d7985e05c77d0ffcf6 +config_hash: de99cfce88e2d1f02246dc6c2f43bc6c diff --git a/api.md b/api.md index 18d40cbe0..e52f4a6e8 100644 --- a/api.md +++ b/api.md @@ -114,7 +114,6 @@ Types: - DevboxSendStdInResult - DevboxSnapshotListView - DevboxSnapshotView -- DevboxTunnelView - DevboxView - TunnelView - DevboxCreateSSHKeyResponse @@ -131,7 +130,6 @@ Methods: - client.devboxes.update(id, { ...params }) -> DevboxView - client.devboxes.list({ ...params }) -> DevboxViewsDevboxesCursorIDPage - client.devboxes.createSSHKey(id) -> DevboxCreateSSHKeyResponse -- client.devboxes.createTunnel(id, { ...params }) -> DevboxTunnelView - client.devboxes.deleteDiskSnapshot(id) -> unknown - client.devboxes.downloadFile(id, { ...params }) -> Response - client.devboxes.enableTunnel(id, { ...params }) -> TunnelView diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json index 5e6ef95fa..d795441d6 100644 --- a/packages/mcp-server/manifest.json +++ b/packages/mcp-server/manifest.json @@ -1,7 +1,7 @@ { "dxt_version": "0.2", "name": "@runloop/api-client-mcp", - "version": "1.11.1", + "version": "1.11.0", "description": "The official MCP Server for the Runloop API", "author": { "name": "Runloop", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 83f8ea6d2..33877432f 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "@runloop/api-client-mcp", - "version": "1.11.1", + "version": "1.11.0", "description": "The official MCP Server for the Runloop API", "author": "Runloop ", "types": "dist/index.d.ts", diff --git a/packages/mcp-server/src/code-tool-paths.cts b/packages/mcp-server/src/code-tool-paths.cts index 78263e45e..15ce7f555 100644 --- a/packages/mcp-server/src/code-tool-paths.cts +++ b/packages/mcp-server/src/code-tool-paths.cts @@ -1,5 +1,3 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -export function getWorkerPath(): string { - return require.resolve('./code-tool-worker.mjs'); -} +export const workerPath = require.resolve('./code-tool-worker.mjs'); diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 1529df944..2d81111d6 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -1,5 +1,10 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import fs from 'node:fs'; +import path from 'node:path'; +import url from 'node:url'; +import { newDenoHTTPWorker } from '@valtown/deno-http-worker'; +import { workerPath } from './code-tool-paths.cjs'; import { ContentBlock, McpRequestContext, @@ -144,23 +149,19 @@ const remoteStainlessHandler = async ({ const codeModeEndpoint = readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool'; - const localClientEnvs = { - RUNLOOP_API_KEY: requireValue( - readEnv('RUNLOOP_API_KEY') ?? client.bearerToken, - 'set RUNLOOP_API_KEY environment variable or provide bearerToken client option', - ), - RUNLOOP_BASE_URL: readEnv('RUNLOOP_BASE_URL') ?? client.baseURL ?? undefined, - }; - // Merge any upstream client envs from the request header, with upstream values taking precedence. - const mergedClientEnvs = { ...localClientEnvs, ...reqContext.upstreamClientEnvs }; - // Setting a Stainless API key authenticates requests to the code tool endpoint. const res = await fetch(codeModeEndpoint, { method: 'POST', headers: { ...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }), 'Content-Type': 'application/json', - 'x-stainless-mcp-client-envs': JSON.stringify(mergedClientEnvs), + 'x-stainless-mcp-client-envs': JSON.stringify({ + RUNLOOP_API_KEY: requireValue( + readEnv('RUNLOOP_API_KEY') ?? client.bearerToken, + 'set RUNLOOP_API_KEY environment variable or provide bearerToken client option', + ), + RUNLOOP_BASE_URL: readEnv('RUNLOOP_BASE_URL') ?? client.baseURL ?? undefined, + }), }, body: JSON.stringify({ project_name: 'runloop', @@ -203,13 +204,6 @@ const localDenoHandler = async ({ reqContext: McpRequestContext; args: unknown; }): Promise => { - const fs = await import('node:fs'); - const path = await import('node:path'); - const url = await import('node:url'); - const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker'); - const { getWorkerPath } = await import('./code-tool-paths.cjs'); - const workerPath = getWorkerPath(); - const client = reqContext.client; const baseURLHostname = new URL(client.baseURL).hostname; const { code } = args as { code: string }; @@ -271,9 +265,6 @@ const localDenoHandler = async ({ printOutput: true, spawnOptions: { cwd: path.dirname(workerPath), - // Merge any upstream client envs into the Deno subprocess environment, - // with the upstream env vars taking precedence. - env: { ...process.env, ...reqContext.upstreamClientEnvs }, }, }); @@ -283,17 +274,13 @@ const localDenoHandler = async ({ reject(new Error(`Worker exited with code ${exitCode}`)); }); - // Strip null/undefined values so that the worker SDK client can fall back to - // reading from environment variables (including any upstreamClientEnvs). - const opts: ClientOptions = Object.fromEntries( - Object.entries({ - baseURL: client.baseURL, - bearerToken: client.bearerToken, - defaultHeaders: { - 'X-Stainless-MCP': 'true', - }, - }).filter(([_, v]) => v != null), - ) as ClientOptions; + const opts: ClientOptions = { + baseURL: client.baseURL, + bearerToken: client.bearerToken, + defaultHeaders: { + 'X-Stainless-MCP': 'true', + }, + }; const req = worker.request( 'http://localhost', diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts index 0d9cd42ee..ab32531d0 100644 --- a/packages/mcp-server/src/http.ts +++ b/packages/mcp-server/src/http.ts @@ -27,56 +27,14 @@ const newServer = async ({ const authOptions = parseClientAuthHeaders(req, false); - let upstreamClientEnvs: Record | undefined; - const clientEnvsHeader = req.headers['x-stainless-mcp-client-envs']; - if (typeof clientEnvsHeader === 'string') { - try { - const parsed = JSON.parse(clientEnvsHeader); - if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { - upstreamClientEnvs = parsed; - } - } catch { - // Ignore malformed header - } - } - - // Parse x-stainless-mcp-client-permissions header to override permission options - // - // Note: Permissions are best-effort and intended to prevent clients from doing unexpected things; - // they're not a hard security boundary, so we allow arbitrary, client-driven overrides. - // - // See the Stainless MCP documentation for more details. - let effectiveMcpOptions = mcpOptions; - const clientPermissionsHeader = req.headers['x-stainless-mcp-client-permissions']; - if (typeof clientPermissionsHeader === 'string') { - try { - const parsed = JSON.parse(clientPermissionsHeader); - if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { - effectiveMcpOptions = { - ...mcpOptions, - ...(typeof parsed.allow_http_gets === 'boolean' && { codeAllowHttpGets: parsed.allow_http_gets }), - ...(Array.isArray(parsed.allowed_methods) && { codeAllowedMethods: parsed.allowed_methods }), - ...(Array.isArray(parsed.blocked_methods) && { codeBlockedMethods: parsed.blocked_methods }), - }; - getLogger().info( - { clientPermissions: parsed }, - 'Overriding code execution permissions from x-stainless-mcp-client-permissions header', - ); - } - } catch (error) { - getLogger().warn({ error }, 'Failed to parse x-stainless-mcp-client-permissions header'); - } - } - await initMcpServer({ server: server, - mcpOptions: effectiveMcpOptions, + mcpOptions: mcpOptions, clientOptions: { ...clientOptions, ...authOptions, }, stainlessApiKey: stainlessApiKey, - upstreamClientEnvs, }); return server; @@ -114,7 +72,7 @@ const del = async (req: express.Request, res: express.Response) => { }; const redactHeaders = (headers: Record) => { - const hiddenHeaders = /auth|cookie|key|token|x-stainless-mcp-client-envs/i; + const hiddenHeaders = /auth|cookie|key|token/i; const filtered = { ...headers }; Object.keys(filtered).forEach((key) => { if (hiddenHeaders.test(key)) { diff --git a/packages/mcp-server/src/instructions.ts b/packages/mcp-server/src/instructions.ts index cae36329b..973b43d79 100644 --- a/packages/mcp-server/src/instructions.ts +++ b/packages/mcp-server/src/instructions.ts @@ -12,24 +12,29 @@ interface InstructionsCacheEntry { const instructionsCache = new Map(); -export async function getInstructions(stainlessApiKey: string | undefined): Promise { +// Periodically evict stale entries so the cache doesn't grow unboundedly. +const _cacheCleanupInterval = setInterval(() => { const now = Date.now(); - const cacheKey = stainlessApiKey ?? ''; - const cached = instructionsCache.get(cacheKey); - - if (cached && now - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) { - return cached.fetchedInstructions; - } - - // Evict stale entries so the cache doesn't grow unboundedly. for (const [key, entry] of instructionsCache) { if (now - entry.fetchedAt > INSTRUCTIONS_CACHE_TTL_MS) { instructionsCache.delete(key); } } +}, INSTRUCTIONS_CACHE_TTL_MS); + +// Don't keep the process alive just for cleanup. +_cacheCleanupInterval.unref(); + +export async function getInstructions(stainlessApiKey: string | undefined): Promise { + const cacheKey = stainlessApiKey ?? ''; + const cached = instructionsCache.get(cacheKey); + + if (cached && Date.now() - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) { + return cached.fetchedInstructions; + } const fetchedInstructions = await fetchLatestInstructions(stainlessApiKey); - instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: now }); + instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: Date.now() }); return fetchedInstructions; } diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index f0741edfe..6df9c2620 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -16,7 +16,7 @@ export const newMcpServer = async (stainlessApiKey: string | undefined) => new McpServer( { name: 'runloop_api_client_api', - version: '1.11.1', + version: '1.11.0', }, { instructions: await getInstructions(stainlessApiKey), @@ -33,7 +33,6 @@ export async function initMcpServer(params: { clientOptions?: ClientOptions; mcpOptions?: McpOptions; stainlessApiKey?: string | undefined; - upstreamClientEnvs?: Record | undefined; }) { const server = params.server instanceof McpServer ? params.server.server : params.server; @@ -95,7 +94,6 @@ export async function initMcpServer(params: { reqContext: { client, stainlessApiKey: params.stainlessApiKey ?? params.mcpOptions?.stainlessApiKey, - upstreamClientEnvs: params.upstreamClientEnvs, }, args, }); diff --git a/packages/mcp-server/src/types.ts b/packages/mcp-server/src/types.ts index dfe0d4bf3..024ab7543 100644 --- a/packages/mcp-server/src/types.ts +++ b/packages/mcp-server/src/types.ts @@ -45,7 +45,6 @@ export type ToolCallResult = { export type McpRequestContext = { client: Runloop; stainlessApiKey?: string | undefined; - upstreamClientEnvs?: Record | undefined; }; export type HandlerFunction = ({ diff --git a/src/index.ts b/src/index.ts index 7140ff6d2..c9cb5f9e3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -177,7 +177,6 @@ import { DevboxAsyncExecutionDetailView, DevboxCreateParams, DevboxCreateSSHKeyResponse, - DevboxCreateTunnelParams, DevboxDeleteDiskSnapshotResponse, DevboxDownloadFileParams, DevboxEnableTunnelParams, @@ -203,7 +202,6 @@ import { DevboxSnapshotListView, DevboxSnapshotView, DevboxSnapshotViewsDiskSnapshotsCursorIDPage, - DevboxTunnelView, DevboxUpdateParams, DevboxUploadFileParams, DevboxUploadFileResponse, @@ -608,7 +606,6 @@ export declare namespace Runloop { type DevboxSendStdInResult as DevboxSendStdInResult, type DevboxSnapshotListView as DevboxSnapshotListView, type DevboxSnapshotView as DevboxSnapshotView, - type DevboxTunnelView as DevboxTunnelView, type DevboxView as DevboxView, type TunnelView as TunnelView, type DevboxCreateSSHKeyResponse as DevboxCreateSSHKeyResponse, @@ -622,7 +619,6 @@ export declare namespace Runloop { type DevboxCreateParams as DevboxCreateParams, type DevboxUpdateParams as DevboxUpdateParams, type DevboxListParams as DevboxListParams, - type DevboxCreateTunnelParams as DevboxCreateTunnelParams, type DevboxDownloadFileParams as DevboxDownloadFileParams, type DevboxEnableTunnelParams as DevboxEnableTunnelParams, type DevboxExecuteParams as DevboxExecuteParams, diff --git a/src/resources/benchmark-jobs.ts b/src/resources/benchmark-jobs.ts index 0ec85c78c..ddd532047 100644 --- a/src/resources/benchmark-jobs.ts +++ b/src/resources/benchmark-jobs.ts @@ -505,7 +505,7 @@ export namespace BenchmarkJobView { /** * The current state of the run. */ - state: 'running' | 'canceled' | 'completed'; + state: 'running' | 'canceled' | 'completed' | 'failed'; /** * Agent configuration used for this run. Specifies whether the run was driven by diff --git a/src/resources/benchmark-runs.ts b/src/resources/benchmark-runs.ts index 8173f5863..0584557bf 100644 --- a/src/resources/benchmark-runs.ts +++ b/src/resources/benchmark-runs.ts @@ -39,7 +39,9 @@ export class BenchmarkRuns extends APIResource { } /** - * Cancel a currently running Benchmark run. + * Cancel a Benchmark run. This will do the following: 1. Cancel all running + * scenarios and shutdown the underlying Devbox resources 2. Update the benchmark + * state to CANCELED 3. Calculate final score from completed scenarios */ cancel(id: string, options?: Core.RequestOptions): Core.APIPromise { return this._client.post(`/v1/benchmark_runs/${id}/cancel`, options); @@ -118,7 +120,7 @@ export interface BenchmarkRunView { /** * The state of the BenchmarkRun. */ - state: 'running' | 'canceled' | 'completed'; + state: 'running' | 'canceled' | 'completed' | 'failed'; /** * The ID of the Benchmark definition. Present if run was created from a benchmark diff --git a/src/resources/devboxes/devboxes.ts b/src/resources/devboxes/devboxes.ts index 78a2698d7..13d5af600 100644 --- a/src/resources/devboxes/devboxes.ts +++ b/src/resources/devboxes/devboxes.ts @@ -189,23 +189,6 @@ export class Devboxes extends APIResource { return this._client.post(`/v1/devboxes/${id}/create_ssh_key`, options); } - /** - * @deprecated Use {@link enableTunnel} instead for V2 tunnels with better URL format. - * - * Creates a legacy tunnel to expose a specific port on the devbox. - * The legacy tunnel URL format is: `https://{devbox_id}-{port}.tunnel.runloop.ai` - * - * V2 tunnels (via enableTunnel) provide encrypted URL-based access with the format: - * `https://{port}-{tunnel_key}.tunnel.runloop.ai` - */ - createTunnel( - id: string, - body: DevboxCreateTunnelParams, - options?: Core.RequestOptions, - ): Core.APIPromise { - return this._client.post(`/v1/devboxes/${id}/create_tunnel`, { body, ...options }); - } - /** * Delete a previously taken disk snapshot of a Devbox. */ @@ -837,23 +820,6 @@ export interface DevboxSnapshotView { source_blueprint_id?: string | null; } -export interface DevboxTunnelView { - /** - * ID of the Devbox the tunnel routes to. - */ - devbox_id: string; - - /** - * Port of the Devbox the tunnel routes to. - */ - port: number; - - /** - * Public url used to access Devbox. - */ - url: string; -} - /** * A Devbox represents a virtual development environment. It is an isolated sandbox * that can be given to agents and used to run arbitrary code such as AI generated @@ -1285,13 +1251,6 @@ export interface DevboxListParams extends DevboxesCursorIDPageParams { | 'shutdown'; } -export interface DevboxCreateTunnelParams { - /** - * Devbox port that tunnel will expose. - */ - port: number; -} - export interface DevboxDownloadFileParams { /** * The path on the Devbox filesystem to read the file from. Path is relative to @@ -1540,7 +1499,6 @@ export declare namespace Devboxes { type DevboxSendStdInResult as DevboxSendStdInResult, type DevboxSnapshotListView as DevboxSnapshotListView, type DevboxSnapshotView as DevboxSnapshotView, - type DevboxTunnelView as DevboxTunnelView, type DevboxView as DevboxView, type TunnelView as TunnelView, type DevboxCreateSSHKeyResponse as DevboxCreateSSHKeyResponse, @@ -1554,7 +1512,6 @@ export declare namespace Devboxes { type DevboxCreateParams as DevboxCreateParams, type DevboxUpdateParams as DevboxUpdateParams, type DevboxListParams as DevboxListParams, - type DevboxCreateTunnelParams as DevboxCreateTunnelParams, type DevboxDownloadFileParams as DevboxDownloadFileParams, type DevboxEnableTunnelParams as DevboxEnableTunnelParams, type DevboxExecuteParams as DevboxExecuteParams, diff --git a/src/resources/devboxes/index.ts b/src/resources/devboxes/index.ts index 3cf02ca1b..1f1168349 100644 --- a/src/resources/devboxes/index.ts +++ b/src/resources/devboxes/index.ts @@ -25,7 +25,6 @@ export { type DevboxSendStdInResult, type DevboxSnapshotListView, type DevboxSnapshotView, - type DevboxTunnelView, type DevboxView, type TunnelView, type DevboxCreateSSHKeyResponse, @@ -37,7 +36,6 @@ export { type DevboxCreateParams, type DevboxUpdateParams, type DevboxListParams, - type DevboxCreateTunnelParams, type DevboxDownloadFileParams, type DevboxEnableTunnelParams, type DevboxExecuteParams, diff --git a/src/resources/devboxes/tools.ts b/src/resources/devboxes/tools.ts index 37683e669..36d41a4e0 100644 --- a/src/resources/devboxes/tools.ts +++ b/src/resources/devboxes/tools.ts @@ -145,7 +145,7 @@ export class DevboxTools { */ tunnelTools(devboxId: string, options?: Core.RequestOptions) { const tools = { - create_tunnel: createTool({ + enable_tunnel: createTool({ name: 'create_tunnel', description: 'Create a new tunnel to expose a port on the devbox', parameters: z.object({ @@ -154,13 +154,7 @@ export class DevboxTools { }), execute: async (params) => { try { - const result = await this.devboxes.createTunnel( - devboxId, - { - port: params.port, - }, - options, - ); + const result = await this.devboxes.enableTunnel(devboxId, options); return `Created tunnel: ${JSON.stringify(result, null, 2)}`; } catch (error) { return `Failed to create tunnel: ${error}`; diff --git a/src/resources/index.ts b/src/resources/index.ts index 9adfbced4..c669111e2 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -74,7 +74,6 @@ export { type DevboxSendStdInResult, type DevboxSnapshotListView, type DevboxSnapshotView, - type DevboxTunnelView, type DevboxView, type TunnelView, type DevboxCreateSSHKeyResponse, @@ -86,7 +85,6 @@ export { type DevboxCreateParams, type DevboxUpdateParams, type DevboxListParams, - type DevboxCreateTunnelParams, type DevboxDownloadFileParams, type DevboxEnableTunnelParams, type DevboxExecuteParams, diff --git a/src/sdk/devbox.ts b/src/sdk/devbox.ts index 9a81ea9bb..b09776e90 100644 --- a/src/sdk/devbox.ts +++ b/src/sdk/devbox.ts @@ -6,7 +6,6 @@ import type { DevboxCreateParams, DevboxAsyncExecutionDetailView, DevboxSnapshotDiskParams, - DevboxCreateTunnelParams, DevboxEnableTunnelParams, DevboxRemoveTunnelParams, DevboxReadFileContentsParams, @@ -102,30 +101,6 @@ export class DevboxNetOps { return this.client.devboxes.createSSHKey(this.devboxId, options); } - /** - * @deprecated Use {@link enableTunnel} instead for V2 tunnels with better URL format. - * - * Creates a legacy tunnel to expose a specific port on the devbox. - * The legacy tunnel URL format is: `https://{devbox_id}-{port}.tunnel.runloop.ai` - * - * V2 tunnels (via enableTunnel) provide encrypted URL-based access with the format: - * `https://{port}-{tunnel_key}.tunnel.runloop.ai` - * - * @example - * ```typescript - * // Deprecated - use enableTunnel instead - * const tunnel = await devbox.net.createTunnel({ port: 8080 }); - * console.log(tunnel.url); // Legacy URL format - * ``` - * - * @param {DevboxCreateTunnelParams} params - Tunnel creation parameters including port - * @param {Core.RequestOptions} [options] - Request options - * @returns {Promise} Legacy tunnel view with devbox_id, port, and url - */ - async createTunnel(params: DevboxCreateTunnelParams, options?: Core.RequestOptions) { - return this.client.devboxes.createTunnel(this.devboxId, params, options); - } - /** * Enable a V2 tunnel for the devbox. V2 tunnels provide encrypted URL-based access * to the devbox without exposing internal IDs. The tunnel URL format is: diff --git a/tests/api-resources/devboxes/devboxes.test.ts b/tests/api-resources/devboxes/devboxes.test.ts index 06683077d..a169ed73e 100644 --- a/tests/api-resources/devboxes/devboxes.test.ts +++ b/tests/api-resources/devboxes/devboxes.test.ts @@ -181,21 +181,6 @@ describe('resource devboxes', () => { ); }); - test('createTunnel: only required params', async () => { - const responsePromise = client.devboxes.createTunnel('id', { port: 0 }); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - test('createTunnel: required and optional params', async () => { - const response = await client.devboxes.createTunnel('id', { port: 0 }); - }); - test('deleteDiskSnapshot', async () => { const responsePromise = client.devboxes.deleteDiskSnapshot('id'); const rawResponse = await responsePromise.asResponse(); diff --git a/tests/objects/devbox.test.ts b/tests/objects/devbox.test.ts index 9c3b5714d..8ca1d8fa9 100644 --- a/tests/objects/devbox.test.ts +++ b/tests/objects/devbox.test.ts @@ -333,25 +333,25 @@ describe('Devbox (New API)', () => { it('should create legacy tunnel (deprecated) and return old URL format', async () => { // Legacy tunnel response includes the old URL format: https://{devbox_id}-{port}.tunnel.runloop.ai + // along with the V2 properties for backward compatibility const mockTunnel = { - devbox_id: 'devbox-123', - port: 8080, - url: 'https://devbox-123-8080.tunnel.runloop.ai', + tunnel_key: 'legacy-key', + auth_mode: 'open', + create_time_ms: Date.now(), + http_keep_alive: false, }; - mockClient.devboxes.createTunnel.mockResolvedValue(mockTunnel); + mockClient.devboxes.enableTunnel.mockResolvedValue(mockTunnel); - const result = await devbox.net.createTunnel({ port: 8080 }); + const result = await devbox.net.enableTunnel({ auth_mode: 'open' }); - expect(mockClient.devboxes.createTunnel).toHaveBeenCalledWith( + expect(mockClient.devboxes.enableTunnel).toHaveBeenCalledWith( 'devbox-123', - { port: 8080 }, + { auth_mode: 'open' }, undefined, ); expect(result).toEqual(mockTunnel); - expect(result.devbox_id).toBe('devbox-123'); - expect(result.port).toBe(8080); - // Verify the legacy URL format contains devbox_id and port - expect(result.url).toBe('https://devbox-123-8080.tunnel.runloop.ai'); + expect(result.tunnel_key).toBeTruthy(); + expect(result.auth_mode).toBe('open'); }); it('should enable V2 tunnel with params', async () => { From 75c8f6a7c29ffd8031770e0c3da67c13ea927cdb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:58:11 +0000 Subject: [PATCH 2/2] release: 1.11.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ package.json | 2 +- packages/mcp-server/manifest.json | 2 +- packages/mcp-server/package.json | 2 +- packages/mcp-server/src/server.ts | 2 +- src/version.ts | 2 +- 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1b796cf0d..33f2a7378 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.11.1" + ".": "1.11.2" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc3e486b..c3d20ab51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.11.2 (2026-03-19) + +Full Changelog: [v1.11.1...v1.11.2](https://github.com/runloopai/api-client-ts/compare/v1.11.1...v1.11.2) + +### Chores + +* remove create_tunnel endpoint ([5076da2](https://github.com/runloopai/api-client-ts/commit/5076da21a6729b4d288b530f78cc0e16415175d7)) + ## 1.11.1 (2026-03-18) Full Changelog: [v1.11.0...v1.11.1](https://github.com/runloopai/api-client-ts/compare/v1.11.0...v1.11.1) diff --git a/package.json b/package.json index 322b79cd5..f9a06d6df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@runloop/api-client", - "version": "1.11.1", + "version": "1.11.2", "description": "The official TypeScript library for the Runloop API", "author": "Runloop ", "types": "dist/sdk.d.ts", diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json index d795441d6..70704ce07 100644 --- a/packages/mcp-server/manifest.json +++ b/packages/mcp-server/manifest.json @@ -1,7 +1,7 @@ { "dxt_version": "0.2", "name": "@runloop/api-client-mcp", - "version": "1.11.0", + "version": "1.11.2", "description": "The official MCP Server for the Runloop API", "author": { "name": "Runloop", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 33877432f..8ef61f27a 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "@runloop/api-client-mcp", - "version": "1.11.0", + "version": "1.11.2", "description": "The official MCP Server for the Runloop API", "author": "Runloop ", "types": "dist/index.d.ts", diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 6df9c2620..0d686b2ec 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -16,7 +16,7 @@ export const newMcpServer = async (stainlessApiKey: string | undefined) => new McpServer( { name: 'runloop_api_client_api', - version: '1.11.0', + version: '1.11.2', }, { instructions: await getInstructions(stainlessApiKey), diff --git a/src/version.ts b/src/version.ts index ba06c0e6e..5cab60c7c 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '1.11.1'; // x-release-please-version +export const VERSION = '1.11.2'; // x-release-please-version