Skip to content
← Public packages

@cameronpak/box

Full Box Public API v1 client: lifecycle, prompts, files, commands, snapshots, environments, webhooks, desktop, hosting, and account.

src/run-command.ts

51 lines · 1.6 KB · TypeScript
import { assertBoxId, boxRequest, failure, type Result } from './client.ts'
import type { CommandResult, CommandStarted } from './types.ts'

export type RunCommandInput = {
	boxId: string
	/** Shell command to run inside the box. */
	command: string
	/** Working directory relative to the box work directory. */
	cwd?: string
	/** 1 to 600 seconds. Defaults to 30. */
	timeoutSeconds?: number
	/**
	 * Start in the background and return a process id immediately.
	 * Poll getCommandStatus with that process id.
	 */
	detached?: boolean
}

export type RunCommandOutput = CommandResult | CommandStarted

/**
 * Run one shell command in a box and return its exit code, stdout, and stderr.
 * This is the main surface for building and testing code.
 * With detached: true the command starts in the background and a process id
 * is returned immediately; poll getCommandStatus for status and logs.
 */
export default async function runCommand(
	input: RunCommandInput,
): Promise<Result<RunCommandOutput>> {
	const invalid = assertBoxId(input?.boxId)
	if (invalid) return failure<RunCommandOutput>(invalid)

	if (!input?.command) {
		return failure<RunCommandOutput>({
			code: 'command_required',
			message: 'command is required.',
			status: 400,
		})
	}

	const body: Record<string, unknown> = { command: input.command }
	if (input.cwd !== undefined) body.cwd = input.cwd
	if (input.timeoutSeconds !== undefined) body.timeoutSeconds = input.timeoutSeconds
	if (input.detached !== undefined) body.detached = input.detached

	return boxRequest<RunCommandOutput>({
		path: '/boxes/' + input.boxId + '/commands',
		method: 'POST',
		body,
	})
}