SDK
Use the Cohub TypeScript SDK for Spaces, Chats, Works, realtime updates, and Work runtime APIs.
The Cohub SDK is the TypeScript client for product APIs and realtime collaboration.
Package: @neta-art/cohub
Install
npm install @neta-art/cohubCreate a client
import { createCohubClient } from "@neta-art/cohub";
const client = createCohubClient({
getAccessToken: async () => localStorage.getItem("token"),
});Defaults:
| Env | API | WebSocket |
|---|---|---|
| production | https://api.cohub.run |
wss://gateway.cohub.run/ws |
| development | https://api-dev.cohub.run |
wss://gateway-dev.cohub.run/ws |
Select development:
const client = createCohubClient({
env: "dev",
getAccessToken: async () => token,
});Or with ENV=dev in Node.js.
Custom endpoints are supported when self-hosting or proxying.
Spaces and Chats
const created = await client.spaces.create({ name: "Demo" });
const space = client.space(created.space.id);
const sessionResult = await space.sessions.create({ title: "Planning" });
const session = space.session(sessionResult.session.id);
await session.messages.send({
content: [{ type: "text", text: "Help me plan the next steps" }],
});Product mapping:
- Space →
client.spaces/client.space(id) - Chat → session APIs under a Space
- Save → checkpoint APIs under a Space
- Work →
client.works
Session realtime
Subscribe to session events while an Agent is working:
const stop = session.subscribe({
progress(event) {
console.log("progress", event.payload);
},
finalized(event) {
console.log("done", event.payload);
},
});
stop();Works
Create and manage Works through client.works, including publish, update, versions, and lookups by slug.
For ordinary server/automation code, use normal user auth. For code inside a published Work, use the Work runtime APIs below.
Work runtime
Published Works can run with short-lived runtime auth provided by the Cohub shell.
const client = createCohubClient(); // token comes from Work runtime
const context = await client.context();
// Work identity, Space identity, viewer state
await client.auth.request({
scopes: ["session.prompt.readonly"],
reason: "Continue the demo chat",
});Important:
- Runtime APIs work inside a published Work
- They do not work from arbitrary static hosting or local file open
- Work scopes and viewer-consent scopes are enforced
Commerce helpers live under client.work.commerce.* when commerce is enabled and the Work is published.
Realtime rooms
Inside a published Work, client.work.realtime provides temporary rooms for
multiplayer state, presence, and generic JSON events. It uses the Work runtime
identity and needs no additional scope or consent dialog.
const room = await client.work.realtime.createRoom({
code: "TEAM-ALPHA", // optional
expiresInSeconds: 2 * 60 * 60,
});
const stop = room.subscribe("shared.state.updated", ({ data }) => {
console.log(data);
});
await room.publish("shared.state.updated", { value: 42 });
stop();
await room.leave();Events are ordered while connected but are not replayed. Use room.send() for
high-rate traffic, and resync authoritative state after reconnecting. Realtime
rooms are runtime-only; normal server auth and the CLI cannot create or join
them.
See the Work Runtime Guide for lifecycle, presence, membership, seat, and limit details.
Callable surface
A Work can expose named methods to the Cohub host embedding it, so an Agent can
call into the running Work with cohub ui preview <work> --call <method>.
client.work.surface.handle("image.open", async (input, { commandId }) => {
openImageStudio(input, commandId);
});
await client.ui.reportResult(commandId, {
status: "applied",
result: selectedImage,
error: null,
});Only registered methods are reachable. There is no DOM access or script
execution. A Surface response only acknowledges delivery; the Work reports the
final result through the same UI command with client.ui.reportResult().
Calls are delivered at-least-once, so prefer methods that are safe to repeat.
Composer context
A Work running in the workspace preview or as the New Chat background can attach one compact context chip to the Cohub composer. The label stays short while the full content is available to the user and sent with each message while attached:
client.work.composer.setChip({
key: "selection",
label: "3 selected",
content: "Selected records:\n- customer_123\n- customer_456\n- customer_789",
});
client.work.composer.clearChip("selection");Calling setChip() again with the same key updates the existing chip. Cohub owns
the chip's appearance and treats its content as plain text. Labels are limited to
120 characters and content to 32KB. A preview chip is attached while that Work is
active. A background chip is attached while the New Chat background is visible
and no preview is active. Closing or reloading either surface clears its chip.
New Chat backgrounds expose composer context but are not callable through UI commands.
Because a published Work is publicly embeddable, surface calls and composer
context are accepted only from an
explicit list of Cohub app origins (or the Work's own origin), and replies go to
that origin rather than being broadcast. A Work embedded by any other site
registers its methods but never answers. The list is deliberately not a
*.cohub.run suffix match, since Works themselves are served from a Cohub
subdomain. Self-hosted deployments and local development widen it explicitly:
client.work.surface.allowHostOrigins(["https://cohub.internal"]);Main client surfaces
The client groups product APIs intentionally:
| Area | Client surface |
|---|---|
| Spaces / sessions / files | client.spaces, client.space(id) |
| Works | client.works |
| Generations | client.generations |
| Models | client.models |
| Search | client.search |
| Tasks / cron | client.tasks, client.cronJobs |
| Channels | client.channels |
| Billing / commerce | client.billing, client.workCommerce |
| Work runtime | client.context(), client.auth, client.work |
| Cohub UI commands | client.ui |
Use only the surfaces you need. Start with Spaces, sessions, and Works.
Auth model
Outside Work runtime:
- Provide
getAccessToken - Optionally handle token storage helpers if you integrate login yourself
Inside Work runtime:
- The host can provide short-lived tokens
- Request additional viewer scopes only when required
Prefer least privilege for any Work that runs in other people’s browsers.
Practical tips
- Reuse one client instance per app shell
- Prefer Space-scoped helpers (
client.space(id)) for readable code - Use realtime subscriptions for streaming UX, not tight polling loops
- Keep product terms aligned in UI copy: Chat/Save, not session/checkpoint