Skip to content
Open
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
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ import {
BlueprintListView,
BlueprintPreviewParams,
BlueprintPreviewView,
BlueprintRegisterParams,
BlueprintUploadParameters,
BlueprintUploadView,
BlueprintView,
BlueprintViewsBlueprintsCursorIDPage,
Blueprints,
Expand Down Expand Up @@ -662,13 +665,16 @@ export declare namespace Runloop {
type BlueprintBuildParameters as BlueprintBuildParameters,
type BlueprintListView as BlueprintListView,
type BlueprintPreviewView as BlueprintPreviewView,
type BlueprintUploadParameters as BlueprintUploadParameters,
type BlueprintUploadView as BlueprintUploadView,
type BlueprintView as BlueprintView,
type BlueprintDeleteResponse as BlueprintDeleteResponse,
BlueprintViewsBlueprintsCursorIDPage as BlueprintViewsBlueprintsCursorIDPage,
type BlueprintCreateParams as BlueprintCreateParams,
type BlueprintListParams as BlueprintListParams,
type BlueprintListPublicParams as BlueprintListPublicParams,
type BlueprintPreviewParams as BlueprintPreviewParams,
type BlueprintRegisterParams as BlueprintRegisterParams,
};

export {
Expand Down
70 changes: 66 additions & 4 deletions src/resources/blueprints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ export class Blueprints extends APIResource {
return this._client.post('/v1/blueprints', { body, ...options });
}

/**
* Create a private Blueprint awaiting its image to be pushed out-of-band via
* docker push. Bypasses the build pipeline entirely: no Dockerfile is composed
* and no image is built. The Blueprint stays in the 'awaiting_upload' step until
* the push completes.
*/
register(
body: BlueprintRegisterParams,
options?: Core.RequestOptions,
): Core.APIPromise<BlueprintUploadView> {
return this._client.post('/v1/blueprints/register', { body, ...options });
}

/**
* Get the details of a previously created Blueprint including the build status.
*/
Expand All @@ -113,7 +126,7 @@ export class Blueprints extends APIResource {
signal,
...(pollTimeoutMs !== undefined ? { timeoutMs: pollTimeoutMs } : {}),
shouldStop: (result) => {
return !['queued', 'provisioning', 'building'].includes(result.status);
return !['queued', 'provisioning', 'building', 'awaiting_upload'].includes(result.status);
},
},
);
Expand Down Expand Up @@ -413,6 +426,35 @@ export interface BlueprintPreviewView {
dockerfile: string;
}

export interface BlueprintUploadParameters {
/**
* Name of the Blueprint.
*/
name: string;

/**
* Parameters to configure your Devbox at launch time.
*/
launch_parameters?: Shared.LaunchParameters | null;

/**
* (Optional) User defined metadata for the Blueprint.
*/
metadata?: { [key: string]: string } | null;
}

export interface BlueprintUploadView {
/**
* The created Blueprint, awaiting its image upload.
*/
blueprint: BlueprintView;

/**
* The reference to push the image to, e.g. via docker push.
*/
push_reference: string;
}

/**
* Blueprints are ways to create customized starting points for Devboxes. They
* allow you to define custom starting points for Devboxes such that environment
Expand Down Expand Up @@ -449,7 +491,7 @@ export interface BlueprintView {
/**
* The status of the Blueprint build.
*/
status: 'queued' | 'provisioning' | 'building' | 'failed' | 'build_complete';
status: 'queued' | 'provisioning' | 'building' | 'awaiting_upload' | 'failed' | 'build_complete';

/**
* The ID of the base Blueprint.
Expand Down Expand Up @@ -716,7 +758,7 @@ export interface BlueprintListParams extends BlueprintsCursorIDPageParams {
name?: string;

/**
* Filter by build status (queued, provisioning, building, failed, build_complete)
* Filter by build status (queued, provisioning, building, awaiting_upload, failed, build_complete)
*/
status?: string;
}
Expand All @@ -734,7 +776,7 @@ export interface BlueprintListPublicParams extends BlueprintsCursorIDPageParams
name?: string;

/**
* Filter by build status (queued, provisioning, building, failed, build_complete)
* Filter by build status (queued, provisioning, building, awaiting_upload, failed, build_complete)
*/
status?: string;
}
Expand Down Expand Up @@ -826,6 +868,23 @@ export interface BlueprintPreviewParams {
system_setup_commands?: Array<string> | null;
}

export interface BlueprintRegisterParams {
/**
* Name of the Blueprint.
*/
name: string;

/**
* Parameters to configure your Devbox at launch time.
*/
launch_parameters?: Shared.LaunchParameters | null;

/**
* (Optional) User defined metadata for the Blueprint.
*/
metadata?: { [key: string]: string } | null;
}

export namespace BlueprintPreviewParams {
/**
* A build context backed by an Object.
Expand Down Expand Up @@ -899,12 +958,15 @@ export declare namespace Blueprints {
type BlueprintBuildParameters as BlueprintBuildParameters,
type BlueprintListView as BlueprintListView,
type BlueprintPreviewView as BlueprintPreviewView,
type BlueprintUploadParameters as BlueprintUploadParameters,
type BlueprintUploadView as BlueprintUploadView,
type BlueprintView as BlueprintView,
type BlueprintDeleteResponse as BlueprintDeleteResponse,
BlueprintViewsBlueprintsCursorIDPage as BlueprintViewsBlueprintsCursorIDPage,
type BlueprintCreateParams as BlueprintCreateParams,
type BlueprintListParams as BlueprintListParams,
type BlueprintListPublicParams as BlueprintListPublicParams,
type BlueprintPreviewParams as BlueprintPreviewParams,
type BlueprintRegisterParams as BlueprintRegisterParams,
};
}
3 changes: 3 additions & 0 deletions src/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,15 @@ export {
type BlueprintBuildParameters,
type BlueprintListView,
type BlueprintPreviewView,
type BlueprintUploadParameters,
type BlueprintUploadView,
type BlueprintView,
type BlueprintDeleteResponse,
type BlueprintCreateParams,
type BlueprintListParams,
type BlueprintListPublicParams,
type BlueprintPreviewParams,
type BlueprintRegisterParams,
} from './blueprints';
export {
DevboxSnapshotViewsDiskSnapshotsCursorIDPage,
Expand Down
64 changes: 63 additions & 1 deletion tests/api-resources/blueprints.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,76 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { Runloop } from '@runloop/api-client';
import { Response } from 'node-fetch';
import { Response, type RequestInfo, type RequestInit } from 'node-fetch';

const client = new Runloop({
bearerToken: 'My Bearer Token',
baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
});

const createRegisterClient = () => {
const fetch = jest.fn(
async (_url: RequestInfo, _init?: RequestInit) =>
new Response(
JSON.stringify({
blueprint: {
id: 'bpt_123',
name: 'name',
status: 'awaiting_upload',
state: 'created',
create_time_ms: 0,
parameters: { name: 'name' },
},
push_reference: 'converter.runloop.ai/blueprints:bpt_123',
}),
{ headers: { 'Content-Type': 'application/json' } },
),
);

return {
client: new Runloop({
bearerToken: 'My Bearer Token',
baseURL: 'http://localhost:5000',
fetch,
}),
fetch,
};
};

describe('resource blueprints', () => {
test('register: only required params', async () => {
const { client, fetch } = createRegisterClient();
const responsePromise = client.blueprints.register({ name: 'name' });
const rawResponse = await responsePromise.asResponse();
expect(rawResponse).toBeInstanceOf(Response);
const response = await responsePromise;
expect(response).not.toBeInstanceOf(Response);
expect(response.push_reference).toBe('converter.runloop.ai/blueprints:bpt_123');
expect(fetch).toHaveBeenCalledWith(
'http://localhost:5000/v1/blueprints/register',
expect.objectContaining({ method: 'POST' }),
);
expect(JSON.parse(fetch.mock.calls[0]![1]!.body as string)).toEqual({ name: 'name' });
});

test('register: required and optional params', async () => {
const { client, fetch } = createRegisterClient();
await client.blueprints.register({
name: 'name',
launch_parameters: { architecture: 'x86_64' },
metadata: { source: 'upload' },
});
expect(fetch).toHaveBeenCalledWith(
'http://localhost:5000/v1/blueprints/register',
expect.objectContaining({ method: 'POST' }),
);
expect(JSON.parse(fetch.mock.calls[0]![1]!.body as string)).toEqual({
name: 'name',
launch_parameters: { architecture: 'x86_64' },
metadata: { source: 'upload' },
});
});

test('create: only required params', async () => {
const responsePromise = client.blueprints.create({ name: 'name' });
const rawResponse = await responsePromise.asResponse();
Expand Down