Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.29.0"
".": "1.30.0"
}
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 1.30.0 (2026-08-23)

Full Changelog: [v1.29.0...v1.30.0](https://github.com/runloopai/api-client-ts/compare/v1.29.0...v1.30.0)

### Features

* **devboxes:** use optimistic create endpoint ([#825](https://github.com/runloopai/api-client-ts/issues/825)) ([07c0ca0](https://github.com/runloopai/api-client-ts/commit/07c0ca079a3ed09d2350d320323b616e471d0b03))


### Chores

* **internal:** allow the mock server port to be set with STAINLESS_MOCK_PORT ([627021b](https://github.com/runloopai/api-client-ts/commit/627021b4d35e9996fe00a7a263a8e8ab5a83cba5))

## 1.29.0 (2026-08-20)

Full Changelog: [v1.28.0...v1.29.0](https://github.com/runloopai/api-client-ts/compare/v1.28.0...v1.29.0)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@runloop/api-client",
"version": "1.29.0",
"version": "1.30.0",
"description": "The official TypeScript library for the Runloop API",
"author": "Runloop <support@runloop.ai>",
"types": "dist/sdk.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-server/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"dxt_version": "0.2",
"name": "@runloop/api-client-mcp",
"version": "1.29.0",
"version": "1.30.0",
"description": "The official MCP Server for the Runloop API",
"author": {
"name": "Runloop",
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@runloop/api-client-mcp",
"version": "1.29.0",
"version": "1.30.0",
"description": "The official MCP Server for the Runloop API",
"author": "Runloop <support@runloop.ai>",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const newMcpServer = async (stainlessApiKey: string | undefined) =>
new McpServer(
{
name: 'runloop_api_client_api',
version: '1.29.0',
version: '1.30.0',
},
{
instructions: await getInstructions(stainlessApiKey),
Expand Down
13 changes: 10 additions & 3 deletions scripts/mock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ set -e

cd "$(dirname "$0")/.."

if [ "$1" == "--install" ]; then
npm exec --package=@stdy/cli@0.22.1 -- steady --version
exit 0
fi

if [[ -n "$1" && "$1" != '--'* ]]; then
URL="$1"
shift
Expand All @@ -20,16 +25,18 @@ fi
echo "==> Starting mock server with URL ${URL}"

# Run steady mock on the given spec
MOCK_PORT="${STAINLESS_MOCK_PORT:-4010}"

if [ "$1" == "--daemon" ]; then
# Pre-install the package so the download doesn't eat into the startup timeout
npm exec --package=@stdy/cli@0.22.1 -- steady --version

npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log &
npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p "$MOCK_PORT" --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log &

# Wait for server to come online via health endpoint (max 30s)
echo -n "Waiting for server"
attempts=0
while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do
while ! curl --silent --fail "http://127.0.0.1:$MOCK_PORT/_x-steady/health" >/dev/null 2>&1; do
if ! kill -0 $! 2>/dev/null; then
echo
cat .stdy.log
Expand All @@ -48,5 +55,5 @@ if [ "$1" == "--daemon" ]; then

echo
else
npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL"
npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p "$MOCK_PORT" --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL"
fi
26 changes: 16 additions & 10 deletions scripts/test
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

MOCK_PORT="${STAINLESS_MOCK_PORT:-4010}"

function steady_is_running() {
curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1
curl --silent "http://127.0.0.1:$MOCK_PORT/_x-steady/health" >/dev/null 2>&1
}

# Parse command line arguments
Expand Down Expand Up @@ -44,7 +46,7 @@ function is_overriding_api_base_url() {

if ! is_overriding_api_base_url && ! steady_is_running ; then
# When we exit this script, make sure to kill the background mock server process
trap 'kill_server_on_port 4010' EXIT
trap 'kill_server_on_port "$MOCK_PORT"' EXIT

# Start the dev server
./scripts/mock --daemon
Expand All @@ -60,7 +62,7 @@ elif ! steady_is_running ; then
echo -e "To run the server, pass in the path or url of your OpenAPI"
echo -e "spec to the steady command:"
echo
echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}"
echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p $MOCK_PORT --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}"
echo

exit 1
Expand All @@ -69,10 +71,14 @@ else
echo
fi

if [ "$RUN_SMOKE_TESTS" = true ]; then
echo -e "${YELLOW}==> Running smoke tests${NC}"
RUN_SMOKETESTS=1 ./node_modules/.bin/jest "${JEST_ARGS[@]}"
else
echo "==> Running tests"
./node_modules/.bin/jest "${JEST_ARGS[@]}"
fi
if [ -n "${STAINLESS_MOCK_PORT:-}" ] && ! is_overriding_api_base_url ; then
export TEST_API_BASE_URL="http://127.0.0.1:$MOCK_PORT"
fi

if [ "$RUN_SMOKE_TESTS" = true ]; then
echo -e "${YELLOW}==> Running smoke tests${NC}"
RUN_SMOKETESTS=1 ./node_modules/.bin/jest "${JEST_ARGS[@]}"
else
echo "==> Running tests"
./node_modules/.bin/jest "${JEST_ARGS[@]}"
fi
8 changes: 7 additions & 1 deletion src/resources/devboxes/devboxes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ export class Devboxes extends APIResource {
options?: LongPollRequestOptions<DevboxView>,
): Promise<DevboxView> {
const { longPoll, polling, ...requestOptions } = options ?? {};
const devbox = await this.create(body, requestOptions);
const devbox = (await this._client.post('/v1/devboxes/create_and_await_running', {
body: body ?? {},
...requestOptions,
})) as DevboxView;
if (devbox.status === 'running') {
return devbox;
}
return this.awaitRunning(devbox.id, { ...requestOptions, longPoll, polling });
}
/**
Expand Down
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const VERSION = '1.29.0'; // x-release-please-version
export const VERSION = '1.30.0'; // x-release-please-version
22 changes: 17 additions & 5 deletions tests/api-resources/devboxes/devboxes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ describe('resource devboxes', () => {
expect(mockPost).toHaveBeenCalledTimes(2);

// Check create call
expect(mockPost).toHaveBeenNthCalledWith(1, '/v1/devboxes', {
expect(mockPost).toHaveBeenNthCalledWith(1, '/v1/devboxes/create_and_await_running', {
body: { name: 'test-devbox' },
});

Expand All @@ -731,16 +731,28 @@ describe('resource devboxes', () => {
mockPost.mockRestore();
});

test('createAndAwaitRunning: returns an optimistic running response without polling', async () => {
const mockPost = jest.spyOn(client.devboxes['_client'], 'post');
mockPost.mockResolvedValueOnce({ id: 'new-devbox-id', status: 'running' });

const result = await client.devboxes.createAndAwaitRunning({ name: 'test-devbox' });

expect(result).toEqual({ id: 'new-devbox-id', status: 'running' });
expect(mockPost).toHaveBeenCalledTimes(1);
expect(mockPost).toHaveBeenCalledWith('/v1/devboxes/create_and_await_running', {
body: { name: 'test-devbox' },
});
});

test('createAndAwaitRunning: handles creation failure', async () => {
const mockPost = jest.spyOn(client.devboxes['_client'], 'post');

const createError = new Error('Creation failed');
mockPost.mockRejectedValueOnce(createError);
const createError = new APIError(400, undefined, 'Creation failed', {});
mockPost.mockRejectedValue(createError);

await expect(client.devboxes.createAndAwaitRunning()).rejects.toThrow('Creation failed');

expect(mockPost).toHaveBeenCalledTimes(1);
expect(mockPost).toHaveBeenCalledWith('/v1/devboxes', { body: {} });
expect(mockPost).toHaveBeenCalledWith('/v1/devboxes/create_and_await_running', { body: {} });

mockPost.mockRestore();
});
Expand Down