| import { BashClient } from "@openhands/typescript-client/clients"; |
| import type { |
| BashEvent, |
| BashEventPage, |
| BashOutput, |
| } from "@openhands/typescript-client"; |
| import { buildHttpBaseUrl } from "#/utils/websocket-url"; |
| import { getActiveBackend } from "../backend-registry/active-store"; |
| import { callCloudProxy } from "../cloud/proxy"; |
| import { getAgentServerClientOptions } from "../agent-server-client-options"; |
|
|
| interface SearchOptions { |
| kind__eq?: "BashCommand" | "BashOutput"; |
| command_id__eq?: string; |
| sort_order?: "TIMESTAMP" | "TIMESTAMP_DESC"; |
| page_id?: string; |
| limit?: number; |
| } |
|
|
| const MAX_OUTPUT_PAGES = 20; |
|
|
| function isBashOutput(event: BashEvent): event is BashOutput { |
| return event.kind === "BashOutput"; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class BashService { |
| |
| |
| |
| |
| |
| static async listOutputs( |
| conversationUrl: string | null, |
| sessionApiKey: string | null | undefined, |
| bashCommandId: string, |
| ): Promise<BashOutput[]> { |
| const outputs: BashOutput[] = []; |
| let pageId: string | undefined; |
| for (let i = 0; i < MAX_OUTPUT_PAGES; i += 1) { |
| const page = await BashService.searchEvents( |
| conversationUrl, |
| sessionApiKey, |
| { |
| kind__eq: "BashOutput", |
| command_id__eq: bashCommandId, |
| sort_order: "TIMESTAMP", |
| ...(pageId ? { page_id: pageId } : {}), |
| }, |
| ); |
| page.items.forEach((event) => { |
| if (isBashOutput(event)) outputs.push(event); |
| }); |
| if (!page.next_page_id) break; |
| pageId = page.next_page_id; |
| } |
| return outputs; |
| } |
|
|
| private static async searchEvents( |
| conversationUrl: string | null, |
| sessionApiKey: string | null | undefined, |
| options: SearchOptions, |
| ): Promise<BashEventPage> { |
| const active = getActiveBackend().backend; |
|
|
| if (active.kind === "cloud") { |
| |
| |
| |
| |
| if (!conversationUrl) { |
| throw new Error( |
| "BashService.listOutputs requires a conversation URL on cloud backends", |
| ); |
| } |
| const params = new URLSearchParams(); |
| Object.entries(options).forEach(([k, v]) => { |
| if (v !== undefined && v !== null) params.set(k, String(v)); |
| }); |
| return callCloudProxy<BashEventPage>({ |
| backend: active, |
| method: "GET", |
| hostOverride: buildHttpBaseUrl(conversationUrl), |
| path: `/api/bash/bash_events/search?${params.toString()}`, |
| authMode: "session-api-key", |
| sessionApiKey, |
| }); |
| } |
|
|
| |
| |
| |
| |
| return new BashClient( |
| getAgentServerClientOptions({ |
| ...(conversationUrl ? { conversationUrl } : {}), |
| sessionApiKey, |
| }), |
| ).searchEvents(options); |
| } |
| } |
|
|
| export default BashService; |
|
|