Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kentcdodds/cursor

src/workers.ts

130 lines · 4.0 KB · TypeScript
import { clampInt, cursorApi, trimString } from './lib/client.ts'

export type ListWorkersParams = {
	status?: 'all' | 'in_use' | 'idle'
	limit?: number
	nextPageToken?: string
}

/**
 * List self-hosted pool workers (requires pool service account key).
 *
 * @param params.status - Filter by `all`, `in_use`, or `idle`.
 * @param params.limit - Page size (1–100); default 50.
 * @returns Worker list and optional pagination token.
 * @example
 * import { listWorkers } from 'kody:@kentcdodds/cursor/workers'
 * const workers = await listWorkers({ status: 'idle', limit: 50 })
 * // => { workers: [...], nextPageToken: '...' }
 */
export async function listWorkers(params: ListWorkersParams = {}) {
	const query: Record<string, string | number> = {}
	if (params.status) query.status = params.status
	if (params.limit !== undefined) {
		query.limit = clampInt(params.limit, 1, 100, 50)
	}
	if (params.nextPageToken) query.nextPageToken = params.nextPageToken

	return cursorApi({
		path: '/v0/private-workers',
		query,
	})
}

/**
 * Return connected and in-use worker counts for fleet autoscaling.
 *
 * @returns User and team worker utilization summary.
 * @example
 * import { getFleetSummary } from 'kody:@kentcdodds/cursor/workers'
 * const summary = await getFleetSummary()
 * // => { teamSummary: { totalConnected: 10, inUse: 8 } }
 */
export async function getFleetSummary() {
	return cursorApi({ path: '/v0/private-workers/summary' })
}

/**
 * Retrieve a single self-hosted pool worker by id.
 *
 * @param params.id - Worker id (e.g. `pw_123`).
 * @returns Worker details.
 * @example
 * import { getWorker } from 'kody:@kentcdodds/cursor/workers'
 * const worker = await getWorker({ id: 'pw_123' })
 * // => { id: 'pw_123', status: 'idle' }
 */
export async function getWorker(params: { id: string }) {
	const id = trimString(params.id)
	if (!id) throw new Error('id is required')
	return cursorApi({ path: `/v0/private-workers/${id}` })
}

export type ListPendingPoolRequestsParams = {
	limit?: number
	pageToken?: string
	repository?: string
}

/**
 * List pending pool requests not yet assigned to a worker.
 *
 * @param params.limit - Page size; default 50.
 * @param params.repository - Required for repo-scoped service account keys.
 * @returns Pending requests and pagination token.
 * @example
 * import { listPendingPoolRequests } from 'kody:@kentcdodds/cursor/workers'
 * const pending = await listPendingPoolRequests({ limit: 50 })
 * // => { requests: [...], nextPageToken: '...' }
 */
export async function listPendingPoolRequests(
	params: ListPendingPoolRequestsParams = {},
) {
	const query: Record<string, string | number> = {}
	if (params.limit !== undefined) {
		query.limit = clampInt(params.limit, 1, 100, 50)
	}
	if (params.pageToken) query.pageToken = params.pageToken
	if (params.repository) query.repository = params.repository

	return cursorApi({
		path: '/v0/private-workers/pending-requests',
		query,
	})
}

export type CreateWorkerTokenParams = {
	forUserEmail?: string
	forUserId?: number
}

/**
 * Create a one-hour user-scoped worker token (service account key required).
 *
 * @param params.forUserEmail - Target team member email.
 * @param params.forUserId - Target team member user id (alternative to email).
 * @returns Short-lived access token and expiry.
 * @example
 * import { createWorkerToken } from 'kody:@kentcdodds/cursor/workers'
 * const token = await createWorkerToken({ forUserEmail: 'alice@company.com' })
 * // => { accessToken: 'eyJ...', expiresAt: '...' }
 */
export async function createWorkerToken(params: CreateWorkerTokenParams) {
	const email = trimString(params.forUserEmail)
	const userId = params.forUserId
	if (!email && userId === undefined) {
		throw new Error('Provide forUserEmail or forUserId')
	}
	const body: Record<string, unknown> = {}
	if (email) body.forUserEmail = email
	if (userId !== undefined) body.forUserId = userId

	return cursorApi({
		method: 'POST',
		path: '/v1/sub-tokens',
		body,
	})
}

/** Default export: fleet utilization summary. */
export default getFleetSummary