TypeScript SDK
The official @tektona/sdk client for Node.js, Bun, Deno, and edge runtimes.
@tektona/sdk is the official TypeScript client for the Tektona API. It runs
on Node.js (≥ 22), Bun, Deno, and edge runtimes that expose fetch.
The SDK is alpha, and APIs may change before 1.0. It exposes one service per
API resource off a single client: sandbox, secret, project, org,
registry, repository, gitCredential, egressNetworkPolicy,
egressProxyProfile, and location. Each resource has its own reference page in
the sidebar.
Install
pnpm add @tektona/sdknpm install @tektona/sdkyarn add @tektona/sdkbun add @tektona/sdkConfiguration
The client reads credentials and default scope from environment variables:
TEKTONA_API_KEY=...
TEKTONA_API_URL=https://api-dev.tektona.ai # optional override
TEKTONA_ORG=my-org # optional default org
TEKTONA_PROJECT=my-project # optional default projectOr pass them explicitly:
import { Tektona } from '@tektona/sdk';
const tek = new Tektona({
apiKey: process.env.TEKTONA_API_KEY,
apiUrl: 'https://api-dev.tektona.ai', // optional
timeoutMs: 60_000, // optional, default 60s
headers: { 'X-My-Header': 'foo' }, // optional
org: 'my-org', // optional default scope
project: 'my-project',
});Scope: org and project
Most resources live under an org and project. Rather than repeating them on every
call, set a default org/project on the client; any call can still override
per-invocation:
const tek = new Tektona({ apiKey, org: 'acme', project: 'web' });
await tek.secret.list(); // uses acme/web
await tek.secret.list({ project: 'api' }); // override just this call
await tek.org.list(); // top-level, ignores scopeA scoped call with no org/project available (neither per-call nor default) throws
InvalidArgumentError naming exactly what is missing.
Quick start
import { Tektona } from '@tektona/sdk';
const tek = new Tektona({ org: 'my-org', project: 'my-project' });
const sandbox = await tek.sandbox.create({
image: 'ghcr.io/tektona-ai/desktop-x11:0.4.3',
egressNetworkPolicy: 'tektona/open',
});
console.log(sandbox.id, sandbox.state);tek.sandbox.create returns a Sandbox instance bound to the client. See the
Sandbox reference for the full lifecycle surface.
Pagination
Every list() returns a single Page<T> and accepts an optional cursor /
limit:
const page = await tek.sandbox.list({ limit: 50 });
console.log(page.items, page.hasMore, page.nextCursor, page.totalCount);
// next page
if (page.hasMore) {
await tek.sandbox.list({ cursor: page.nextCursor });
}For convenience, list-backed services also expose an auto-paging async iterator that walks every page for you:
for await (const sandbox of tek.sandbox.listAll()) {
console.log(sandbox.id);
}Errors
Errors are typed and instanceof-friendly. They form a hierarchy rooted at
TektonaError; HTTP responses map to ApiError subclasses carrying the
statusCode:
import {
TektonaError, // root
ApiError, // any HTTP-status error (has .statusCode)
AuthenticationError, // 401
AuthorizationError, // 403
InvalidArgumentError,// 400 / 422
NotFoundError, // 404 (→ SandboxNotFoundError, SecretNotFoundError, …)
ConflictError, // 409
QuotaExceededError, // 402
RateLimitError, // 429 (has .retryAfterMs, .throttler, .limit, .remaining, .resetSeconds)
isHttpStatus,
} from '@tektona/sdk';
try {
await tek.sandbox.create({ image: '…' });
} catch (err) {
if (isHttpStatus(err, 404)) {
// not found
} else if (err instanceof RateLimitError) {
// back off using err.retryAfterMs; err.throttler tells you which limit was
// hit ("general" or "sandbox-create"), and err.remaining / err.resetSeconds
// mirror the X-RateLimit-* response headers.
}
throw err;
}isHttpStatus(err, status) is the canonical way to branch on HTTP status across
every TektonaError subclass.
Tree-shaking
The ergonomic services are the recommended surface. For minimal bundles (browser
or edge), the generated, fully tree-shakeable transport functions are re-exported
under generated:
import { generated } from '@tektona/sdk';
// import { listSandboxes } from '@tektona/sdk/generated' — call a single operation