← Public packages
@cameronpak/zo-computer
Typed Zo Computer API client with Result-based error handling and SSE streaming.
src/call.ts
75 lines · 2.0 KB · TypeScriptimport { Result } from 'better-result'
import ask from './ask'
import listModels from './list-models'
import listPersonas from './list-personas'
import type { ZoAskInput, ZoClientOptions } from './types'
/**
* A `Result` flattened into plain JSON.
*/
export type ZoWireResult =
| { status: 'ok'; value: unknown }
| { status: 'error'; error: Record<string, unknown> }
type CallParams = ZoClientOptions &
Partial<ZoAskInput> & {
/** Which operation to run. */
operation: 'ask' | 'listModels' | 'listPersonas'
}
function toWire(result: Result<unknown, { _tag: string }>): ZoWireResult {
if (Result.isOk(result)) return { status: 'ok', value: result.value }
const error = result.error as {
_tag: string
message?: string
toJSON?: () => Record<string, unknown>
}
const payload =
typeof error.toJSON === 'function'
? error.toJSON()
: { _tag: error._tag, message: error.message ?? '' }
const { stack: _stack, name: _name, ...rest } = payload
return { status: 'error', error: { ...rest, _tag: error._tag } }
}
/**
* Run one operation and return plain JSON.
*
* A `Result` is a class instance, so it cannot cross a `packages.invoke`
* boundary. Use this export from `packages.invoke`. Use the direct exports
* when you import the package.
*
* @example
* await packages.invoke({
* kodyId: 'zo-computer',
* exportName: './call',
* params: { operation: 'ask', input: 'Hello' },
* })
* // => { status: 'ok', value: { output: '...', conversationId: '...' } }
*/
export default async function call(params: CallParams): Promise<ZoWireResult> {
const operation = params?.operation
if (operation === 'listModels') {
return toWire(await listModels(params))
}
if (operation === 'listPersonas') {
return toWire(await listPersonas(params))
}
if (operation === 'ask') {
return toWire(await ask(params as ZoAskInput & ZoClientOptions))
}
return {
status: 'error',
error: {
_tag: 'ZoConfigError',
field: 'operation',
message: 'operation must be ask, listModels, or listPersonas',
},
}
}