Files
orca/src/cli/command-body-input.ts
T
Jinwoo Hong 47b7af097c Add --spec-file and one keep-open name to orchestration worker commands (#36545)
worker-start and task-create take --spec-file <path> (or - for stdin). worker-start --keep-open keeps a worker open after it finishes in one step, and worker-keep-open replaces worker-retain (kept as a hidden alias). Concise text output escapes terminal control characters in worker-written values.
2026-10-31 01:23:25 -04:00

78 lines
2.4 KiB
TypeScript

import { readFile } from 'node:fs/promises'
import { isAbsolute, join } from 'node:path'
import { getRequiredStringFlag, getRequiredStringFlagAllowingEmpty } from './flags'
import { RuntimeClientError } from './runtime-client'
type BodyOptions = {
required: boolean
maximumCharacters: number
tooLargeCode: string
label: string
}
export function readCommandBody(
flags: Map<string, string | boolean>,
cwd: string,
options: BodyOptions & { required: true }
): Promise<string>
export function readCommandBody(
flags: Map<string, string | boolean>,
cwd: string,
options: BodyOptions
): Promise<string | undefined>
export async function readCommandBody(
flags: Map<string, string | boolean>,
cwd: string,
options: BodyOptions
): Promise<string | undefined> {
const body = await readTextOrFileFlag(flags, cwd, 'body', options.required)
if (body !== undefined && body.length > options.maximumCharacters) {
throw new RuntimeClientError(
options.tooLargeCode,
`${options.label} must be at most ${options.maximumCharacters} characters`
)
}
return body
}
/** Reads `--<flag> <text>` or `--<flag>-file <path|->`, refusing both. */
export async function readTextOrFileFlag(
flags: Map<string, string | boolean>,
cwd: string,
flag: string,
required: boolean
): Promise<string | undefined> {
const fileFlag = `${flag}-file`
const hasText = flags.has(flag)
const hasFile = flags.has(fileFlag)
if (hasText && hasFile) {
throw new RuntimeClientError(
'invalid_argument',
`Use either --${flag} or --${fileFlag}, not both`
)
}
if (!hasText && !hasFile) {
if (required) {
throw new RuntimeClientError('invalid_argument', `Missing --${flag} or --${fileFlag}`)
}
return undefined
}
return hasText
? getRequiredStringFlagAllowingEmpty(flags, flag)
: readBodyFile(getRequiredStringFlag(flags, fileFlag), cwd)
}
async function readBodyFile(path: string, cwd: string): Promise<string> {
if (path !== '-') {
return readFile(isAbsolute(path) ? path : join(cwd, path), 'utf8')
}
if (process.stdin.isTTY) {
throw new RuntimeClientError('invalid_argument', 'stdin body requested but stdin is a TTY')
}
const chunks: Buffer[] = []
for await (const chunk of process.stdin) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)))
}
return Buffer.concat(chunks).toString('utf8')
}