Skip to content
← Community packages

@cameronpak/zo-computer

by @cameronpakFollow @cameronpak

Typed Zo Computer API client with Result-based error handling and SSE streaming.

  • zo
  • zo-computer
  • api
  • client
  • streaming
License
MIT
Published
August 8, 2026
Pinned commit
c276108
Rating
No ratings yet
Forks
0
Stars
0
Adaptation effort

One-click install

Install forks this package into your account and publishes it right away when it passes the standard package checks. This listing has not been reviewed by an admin.

Log in to install this package.

Fork with your agent

Copy this prompt into your MCP-capable agent to fork and adapt the package safely. Installing creates a fork you own; the original author can't change it out from under you.

Use Kody to fork the community package "@cameronpak/zo-computer" (listing id: 2823705b-1f6e-44a6-a2b1-2bf290d21f2f). Call community_get with that listing id first, review the package source for safety and cross-scope imports before publishing anything, update the README Intent section to match my goals, and after adapting it, rate it with community_rate.

README

@cameronpak/zo-computer

Typed client for the Zo Computer API. Every call returns a Result, so a failure is a value and not an exception.

Intent

This package exists so Cam can call his Zo Computer instance from any Kody context without rewriting fetch code. It covers the three documented endpoints: /zo/ask, /models/available, and /personas/available.

The error handling is the point. Each failure carries a tag, so the caller can match on the exact error instead of reading a message string. The package uses better-result for this.

Success means one thing. Cam can ask Zo a question, stream the answer, and know what went wrong when the call fails.

Install the API key

  1. Open your Zo instance. Go to Settings, then Advanced.
  2. Copy the API key. The key starts with zo_sk_.
  3. Save the key in Kody as a secret named ZO_API_KEY.
  4. Approve the host api.zo.computer for that secret.

Every export reads the ZO_API_KEY secret by default. Kody resolves the secret inside the outbound request, so package code never holds the plain key.

To override the secret, pass an apiKey argument to any export.

Exports

ExportWhat it does
.Returns a summary of this package surface.
./askSends one message and waits for the whole answer.
./ask-streamSends one message and reads the answer as it arrives.
./list-modelsLists the models you can pass as modelName.
./list-personasLists the personas you can pass as personaId.
./callRuns one operation and returns plain JSON.

The root export re-exports every type and every error class.

import { ZoApiError, type ZoAskResult } from 'kody:@cameronpak/zo-computer'

Ask a question

import ask from 'kody:@cameronpak/zo-computer/ask'

const answer = await ask({ input: 'What is on my calendar today?' })

if (answer.status === 'ok') {
	console.log(answer.value.output)
	console.log(answer.value.conversationId)
}

Pass the returned conversationId back in to continue the same thread.

const reply = await ask({
	input: 'Now summarize that in one line.',
	conversationId: answer.value.conversationId,
})

Get structured output

Set outputFormat to a JSON Schema. Zo then returns output as an object.

const answer = await ask({
	input: 'List my three top priorities.',
	outputFormat: {
		type: 'object',
		properties: { priorities: { type: 'array', items: { type: 'string' } } },
		required: ['priorities'],
	},
})

Handle errors by tag

Result.match handles success against failure. error.match handles each error tag. The compiler rejects the handler when a tag is missing.

import ask from 'kody:@cameronpak/zo-computer/ask'

const message = (await ask({ input: 'Hello' })).match({
	ok: (value) => String(value.output),
	err: (error) =>
		error.match({
			ZoConfigError: (e) => `Bad input: ${e.field}`,
			ZoNetworkError: () => 'Zo was not reachable.',
			ZoApiError: (e) => `Zo returned HTTP ${e.status}.`,
			ZoParseError: () => 'Zo returned an unexpected body.',
		}),
})

Error tags

TagCause
ZoConfigErrorThe caller passed bad input. No request was sent.
ZoNetworkErrorThe request never produced a response.
ZoApiErrorZo answered with a non-2xx status.
ZoParseErrorThe body did not match the documented shape.
ZoStreamErrorThe event stream broke after it opened.

Stream an answer

askStream returns an async iterable, so import it. This export cannot cross a packages.invoke boundary.

import askStream from 'kody:@cameronpak/zo-computer/ask-stream'

const opened = await askStream({ input: 'Write a haiku about coffee.' })

if (opened.status === 'ok') {
	for await (const event of opened.value.events) {
		if (event.event === 'End') break
		console.log(event.event, event.data)
	}
}

The Err case covers failures before the stream opens. After the stream opens, a failure arrives as an event named Error. The data field of that event holds a ZoStreamError.

Zo documents three event names: FrontendModelResponse, End, and Error.

Retry network failures

Retries apply to network failures only. A 4xx or 5xx response is never retried.

const answer = await ask({
	input: 'Hello',
	retry: { times: 3, delayMs: 250, backoff: 'exponential' },
})

Options

OptionTypeDefault
apiKeystringThe ZO_API_KEY secret.
baseUrlstringhttps://api.zo.computer
signalAbortSignalNone.
retryobjectNo retries.

Notes

The client maps between two naming styles. It accepts camelCase input such as conversationId. It sends the snake_case field names the API documents, such as conversation_id. It returns camelCase again.

A Result is a class instance, so packages.invoke cannot serialize it. The direct exports are for import. For packages.invoke, use ./call.

await packages.invoke({
	kodyId: 'zo-computer',
	exportName: './call',
	params: { operation: 'ask', input: 'Hello' },
})
// => { status: 'ok', value: { output: '...', conversationId: '...' } }
// => { status: 'error', error: { _tag: 'ZoApiError', status: 401, ... } }

The operation field accepts ask, listModels, and listPersonas. Every other option is the same. askStream has no ./call form, because an async iterable cannot cross that boundary either.

Source

The Zo Computer API reference is at zo.computer/docs/api.

License

MIT

Stars

0 stars

Log in to star this package.

Report this listing

Log in to report this listing.