mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 00:03:47 +00:00
154 lines
5.9 KiB
TypeScript
154 lines
5.9 KiB
TypeScript
import type {
|
|
RuntimeTerminalClose,
|
|
RuntimeTerminalCreate,
|
|
RuntimeTerminalFocus,
|
|
RuntimeTerminalListResult,
|
|
RuntimeTerminalRead,
|
|
RuntimeTerminalRename,
|
|
RuntimeTerminalSend,
|
|
RuntimeTerminalShow,
|
|
RuntimeTerminalSplit,
|
|
RuntimeTerminalWait
|
|
} from '../../shared/runtime-types'
|
|
import type { CommandHandler } from '../dispatch'
|
|
import {
|
|
formatTerminalClose,
|
|
formatTerminalCreate,
|
|
formatTerminalFocus,
|
|
formatTerminalList,
|
|
formatTerminalRead,
|
|
formatTerminalRename,
|
|
formatTerminalSend,
|
|
formatTerminalShow,
|
|
formatTerminalSplit,
|
|
formatTerminalWait,
|
|
printResult
|
|
} from '../format'
|
|
import {
|
|
getOptionalPositiveIntegerFlag,
|
|
getOptionalStringFlag,
|
|
getRequiredStringFlag
|
|
} from '../flags'
|
|
import { RuntimeClientError } from '../runtime-client'
|
|
import {
|
|
getBrowserWorktreeSelector,
|
|
getOptionalWorktreeSelector,
|
|
getRequiredWorktreeSelector,
|
|
getTerminalHandle
|
|
} from '../selectors'
|
|
|
|
// Why: terminal wait legitimately needs to outlive the CLI's default RPC
|
|
// timeout. Even without an explicit server timeout, the client must allow
|
|
// long waits instead of failing at the generic 15s transport cap.
|
|
const DEFAULT_TERMINAL_WAIT_RPC_TIMEOUT_MS = 5 * 60 * 1000
|
|
|
|
const terminalFocusHandler: CommandHandler = async ({ flags, client, cwd, json }) => {
|
|
const result = await client.call<{ focus: RuntimeTerminalFocus }>('terminal.focus', {
|
|
terminal: await getTerminalHandle(flags, cwd, client)
|
|
})
|
|
printResult(result, json, formatTerminalFocus)
|
|
}
|
|
|
|
export const TERMINAL_HANDLERS: Record<string, CommandHandler> = {
|
|
'terminal list': async ({ flags, client, cwd, json }) => {
|
|
const result = await client.call<RuntimeTerminalListResult>('terminal.list', {
|
|
worktree: await getOptionalWorktreeSelector(flags, 'worktree', cwd, client),
|
|
limit: getOptionalPositiveIntegerFlag(flags, 'limit')
|
|
})
|
|
printResult(result, json, formatTerminalList)
|
|
},
|
|
'terminal show': async ({ flags, client, cwd, json }) => {
|
|
const result = await client.call<{ terminal: RuntimeTerminalShow }>('terminal.show', {
|
|
terminal: await getTerminalHandle(flags, cwd, client)
|
|
})
|
|
printResult(result, json, formatTerminalShow)
|
|
},
|
|
'terminal read': async ({ flags, client, cwd, json }) => {
|
|
const cursorFlag = getOptionalStringFlag(flags, 'cursor')
|
|
const cursor =
|
|
cursorFlag !== undefined && /^\d+$/.test(cursorFlag)
|
|
? Number.parseInt(cursorFlag, 10)
|
|
: undefined
|
|
if (cursorFlag !== undefined && cursor === undefined) {
|
|
throw new RuntimeClientError('invalid_argument', '--cursor must be a non-negative integer')
|
|
}
|
|
const result = await client.call<{ terminal: RuntimeTerminalRead }>('terminal.read', {
|
|
terminal: await getTerminalHandle(flags, cwd, client),
|
|
...(cursor !== undefined ? { cursor } : {})
|
|
})
|
|
printResult(result, json, formatTerminalRead)
|
|
},
|
|
'terminal send': async ({ flags, client, cwd, json }) => {
|
|
const result = await client.call<{ send: RuntimeTerminalSend }>('terminal.send', {
|
|
terminal: await getTerminalHandle(flags, cwd, client),
|
|
text: getOptionalStringFlag(flags, 'text'),
|
|
enter: flags.get('enter') === true,
|
|
interrupt: flags.get('interrupt') === true
|
|
})
|
|
printResult(result, json, formatTerminalSend)
|
|
},
|
|
'terminal wait': async ({ flags, client, cwd, json }) => {
|
|
const timeoutMs = getOptionalPositiveIntegerFlag(flags, 'timeout-ms')
|
|
const result = await client.call<{ wait: RuntimeTerminalWait }>(
|
|
'terminal.wait',
|
|
{
|
|
terminal: await getTerminalHandle(flags, cwd, client),
|
|
for: getRequiredStringFlag(flags, 'for'),
|
|
timeoutMs
|
|
},
|
|
{
|
|
timeoutMs: timeoutMs ? timeoutMs + 5000 : DEFAULT_TERMINAL_WAIT_RPC_TIMEOUT_MS
|
|
}
|
|
)
|
|
printResult(result, json, formatTerminalWait)
|
|
},
|
|
'terminal stop': async ({ flags, client, cwd, json }) => {
|
|
const result = await client.call<{ stopped: number }>('terminal.stop', {
|
|
worktree: await getRequiredWorktreeSelector(flags, 'worktree', cwd, client)
|
|
})
|
|
printResult(result, json, (value) => `Stopped ${value.stopped} terminals.`)
|
|
},
|
|
'terminal rename': async ({ flags, client, cwd, json }) => {
|
|
const result = await client.call<{ rename: RuntimeTerminalRename }>('terminal.rename', {
|
|
terminal: await getTerminalHandle(flags, cwd, client),
|
|
title: getOptionalStringFlag(flags, 'title') ?? null
|
|
})
|
|
printResult(result, json, formatTerminalRename)
|
|
},
|
|
'terminal create': async ({ flags, client, cwd, json }) => {
|
|
const result = await client.call<{ terminal: RuntimeTerminalCreate }>('terminal.create', {
|
|
worktree: await getBrowserWorktreeSelector(flags, cwd, client),
|
|
command: getOptionalStringFlag(flags, 'command'),
|
|
title: getOptionalStringFlag(flags, 'title'),
|
|
focus: flags.get('focus') === true
|
|
})
|
|
printResult(result, json, formatTerminalCreate)
|
|
},
|
|
// Why: `focus` and `switch` are aliases — register the same handler under
|
|
// both keys so the dispatch table stays a plain command-path lookup.
|
|
'terminal focus': terminalFocusHandler,
|
|
'terminal switch': terminalFocusHandler,
|
|
'terminal close': async ({ flags, client, cwd, json }) => {
|
|
const result = await client.call<{ close: RuntimeTerminalClose }>('terminal.close', {
|
|
terminal: await getTerminalHandle(flags, cwd, client)
|
|
})
|
|
printResult(result, json, formatTerminalClose)
|
|
},
|
|
'terminal split': async ({ flags, client, cwd, json }) => {
|
|
const directionFlag = getOptionalStringFlag(flags, 'direction')
|
|
if (
|
|
directionFlag !== undefined &&
|
|
directionFlag !== 'horizontal' &&
|
|
directionFlag !== 'vertical'
|
|
) {
|
|
throw new RuntimeClientError('invalid_argument', '--direction must be horizontal or vertical')
|
|
}
|
|
const result = await client.call<{ split: RuntimeTerminalSplit }>('terminal.split', {
|
|
terminal: await getTerminalHandle(flags, cwd, client),
|
|
direction: directionFlag,
|
|
command: getOptionalStringFlag(flags, 'command')
|
|
})
|
|
printResult(result, json, formatTerminalSplit)
|
|
}
|
|
}
|