@neta-art/cohub 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # @neta-art/cohub
2
+
3
+ Cohub SDK for interacting with spaces, sessions, checkpoints, and realtime agent collaboration.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @neta-art/cohub @neta-art/cohub-protocol
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { createCohubClient } from "@neta-art/cohub";
15
+
16
+ const client = createCohubClient({
17
+ baseUrl: "https://api.example.com",
18
+ getAccessToken: async () => localStorage.getItem("token"),
19
+ websocket: {
20
+ url: "https://gateway.example.com",
21
+ },
22
+ });
23
+ ```
24
+
25
+ ## Spaces and sessions
26
+
27
+ A **Space** is a live, isolated working environment where users and agents create together.
28
+
29
+ ```ts
30
+ const created = await client.spaces.create({ name: "Demo" });
31
+ const space = client.space(created.space.id);
32
+
33
+ const sessionResult = await space.sessions.create({ title: "Planning" });
34
+ const session = space.session(sessionResult.session.id);
35
+
36
+ await session.messages.send({
37
+ content: [{ type: "text", text: "Help me plan the next steps" }],
38
+ });
39
+ ```
40
+
41
+ ## Session subscriptions
42
+
43
+ ```ts
44
+ const stop = session.subscribe({
45
+ progress(event) {
46
+ console.log("progress", event.payload);
47
+ },
48
+ final(event) {
49
+ console.log("final", event.payload);
50
+ },
51
+ error(event) {
52
+ console.error("error", event.payload);
53
+ },
54
+ persisted(event) {
55
+ console.log("persisted", event.payload);
56
+ },
57
+ });
58
+
59
+ // later
60
+ stop();
61
+ ```
62
+
63
+ You can also listen with business-oriented event names:
64
+
65
+ ```ts
66
+ session.on("turn.final", (event) => {
67
+ console.log(event.payload);
68
+ });
69
+
70
+ space.on("message.persisted", (event) => {
71
+ console.log(event.payload);
72
+ });
73
+ ```
74
+
75
+ Supported business event names:
76
+
77
+ - `turn.progress`
78
+ - `turn.final`
79
+ - `turn.error`
80
+ - `message.persisted`
81
+
82
+ ## HTTP-only usage
83
+
84
+ If you only want HTTP transport, use the dedicated entry:
85
+
86
+ ```ts
87
+ import { createHttpClient } from "@neta-art/cohub/http";
88
+
89
+ const http = createHttpClient({
90
+ baseUrl: "https://api.example.com",
91
+ getAccessToken: async () => localStorage.getItem("token"),
92
+ });
93
+
94
+ const spaces = await http.spaces.list();
95
+ ```
96
+
97
+ Note: realtime methods like `space.subscribe(...)` or `session.subscribe(...)` require the main client with websocket configuration.
98
+
99
+ ## Low-level websocket usage
100
+
101
+ If you need direct realtime transport access, use the websocket entry:
102
+
103
+ ```ts
104
+ import { createWebsocketClient } from "@neta-art/cohub/websocket";
105
+
106
+ const ws = createWebsocketClient({
107
+ url: "https://gateway.example.com",
108
+ getAccessToken: async () => localStorage.getItem("token"),
109
+ });
110
+
111
+ await ws.connect();
112
+ ```
113
+
114
+ ## Design principles
115
+
116
+ This SDK is intentionally built around Cohub's co-creation model:
117
+
118
+ - work with `space(...)` and `session(...)` as the primary creative surface
119
+ - send messages through `session.messages.send(...)`
120
+ - subscribe through `space.subscribe(...)` and `session.subscribe(...)`
121
+ - keep protocol details behind the SDK surface
122
+
123
+ ## Publish checklist
124
+
125
+ Before publishing:
126
+
127
+ 1. build the protocol package: `pnpm --filter @neta-art/cohub-protocol build`
128
+ 2. build this package: `pnpm --filter @neta-art/cohub build`
129
+ 3. typecheck the protocol package: `pnpm --filter @neta-art/cohub-protocol typecheck`
130
+ 4. typecheck this package: `pnpm --filter @neta-art/cohub typecheck`
131
+ 5. verify consuming apps still typecheck
132
+ 6. verify `dist/` contains `index`, `http`, and `websocket` outputs
@@ -0,0 +1,13 @@
1
+ import type { HttpTransport, Fetch } from "../transport.js";
2
+ import type { Channel } from "../types.js";
3
+ export declare class ChannelsApi {
4
+ private readonly transport;
5
+ constructor(transport: HttpTransport);
6
+ list(customFetch?: Fetch): Promise<Channel[]>;
7
+ create(data: {
8
+ provider: string;
9
+ name: string;
10
+ credentials: Record<string, unknown>;
11
+ }): Promise<unknown>;
12
+ delete(id: string): Promise<unknown>;
13
+ }
@@ -0,0 +1,24 @@
1
+ export class ChannelsApi {
2
+ transport;
3
+ constructor(transport) {
4
+ this.transport = transport;
5
+ }
6
+ list(customFetch) {
7
+ return this.transport.request("/api/channels", {
8
+ method: "GET",
9
+ fetch: customFetch,
10
+ });
11
+ }
12
+ create(data) {
13
+ return this.transport.request("/api/channels", {
14
+ method: "POST",
15
+ headers: {
16
+ "Content-Type": "application/json",
17
+ },
18
+ body: JSON.stringify(data),
19
+ });
20
+ }
21
+ delete(id) {
22
+ return this.transport.request(`/api/channels/${id}`, { method: "DELETE" });
23
+ }
24
+ }
@@ -0,0 +1,19 @@
1
+ import type { HttpTransport } from "../transport.js";
2
+ import type { CreateCronJobInput, CronJobRecord, TaskRunRecord } from "../types.js";
3
+ export declare class CronJobsApi {
4
+ private readonly transport;
5
+ constructor(transport: HttpTransport);
6
+ list(spaceId?: string): Promise<{
7
+ jobs: CronJobRecord[];
8
+ }>;
9
+ create(data: CreateCronJobInput): Promise<CronJobRecord>;
10
+ delete(id: string): Promise<{
11
+ ok: true;
12
+ }>;
13
+ toggle(id: string, enabled: boolean): Promise<{
14
+ ok: true;
15
+ }>;
16
+ runs(cronJobId: string): Promise<{
17
+ runs: TaskRunRecord[];
18
+ }>;
19
+ }
@@ -0,0 +1,32 @@
1
+ export class CronJobsApi {
2
+ transport;
3
+ constructor(transport) {
4
+ this.transport = transport;
5
+ }
6
+ list(spaceId) {
7
+ const query = spaceId ? `?spaceId=${encodeURIComponent(spaceId)}` : "";
8
+ return this.transport.request(`/api/cron-jobs${query}`);
9
+ }
10
+ create(data) {
11
+ return this.transport.request("/api/cron-jobs", {
12
+ method: "POST",
13
+ headers: { "Content-Type": "application/json" },
14
+ body: JSON.stringify(data),
15
+ });
16
+ }
17
+ delete(id) {
18
+ return this.transport.request(`/api/cron-jobs/${id}`, {
19
+ method: "DELETE",
20
+ });
21
+ }
22
+ toggle(id, enabled) {
23
+ return this.transport.request(`/api/cron-jobs/${id}`, {
24
+ method: "PATCH",
25
+ headers: { "Content-Type": "application/json" },
26
+ body: JSON.stringify({ enabled }),
27
+ });
28
+ }
29
+ runs(cronJobId) {
30
+ return this.transport.request(`/api/cron-jobs/${cronJobId}/runs`);
31
+ }
32
+ }
@@ -0,0 +1,8 @@
1
+ import type { Fetch } from "../transport.js";
2
+ import type { ModelCatalogEntry } from "../types.js";
3
+ export declare class ModelsApi {
4
+ private readonly fetcher;
5
+ private readonly baseUrl;
6
+ constructor(fetcher: Fetch, baseUrl: string);
7
+ list(customFetch?: Fetch): Promise<Record<string, ModelCatalogEntry[]>>;
8
+ }
@@ -0,0 +1,17 @@
1
+ export class ModelsApi {
2
+ fetcher;
3
+ baseUrl;
4
+ constructor(fetcher, baseUrl) {
5
+ this.fetcher = fetcher;
6
+ this.baseUrl = baseUrl;
7
+ }
8
+ async list(customFetch) {
9
+ const fetchImpl = customFetch ?? this.fetcher;
10
+ const url = this.baseUrl ? `${this.baseUrl}/api/models` : "/api/models";
11
+ const response = await fetchImpl(url);
12
+ if (!response.ok) {
13
+ throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`);
14
+ }
15
+ return response.json();
16
+ }
17
+ }
@@ -0,0 +1,13 @@
1
+ import type { HttpTransport } from "../transport.js";
2
+ import type { SpaceAccessPolicy, SpaceRole } from "../types.js";
3
+ export declare class SessionAccessApi {
4
+ private readonly transport;
5
+ constructor(transport: HttpTransport);
6
+ get(sessionId: string): Promise<SpaceAccessPolicy>;
7
+ set(sessionId: string, body: {
8
+ anonymous_user?: SpaceRole | null;
9
+ }): Promise<SpaceAccessPolicy>;
10
+ remove(sessionId: string): Promise<{
11
+ ok: true;
12
+ }>;
13
+ }
@@ -0,0 +1,19 @@
1
+ export class SessionAccessApi {
2
+ transport;
3
+ constructor(transport) {
4
+ this.transport = transport;
5
+ }
6
+ get(sessionId) {
7
+ return this.transport.request(`/api/sessions/${sessionId}/access`);
8
+ }
9
+ set(sessionId, body) {
10
+ return this.transport.request(`/api/sessions/${sessionId}/access`, {
11
+ method: "PUT",
12
+ headers: { "Content-Type": "application/json" },
13
+ body: JSON.stringify(body),
14
+ });
15
+ }
16
+ remove(sessionId) {
17
+ return this.transport.request(`/api/sessions/${sessionId}/access`, { method: "DELETE" });
18
+ }
19
+ }
@@ -0,0 +1,177 @@
1
+ import type { WebsocketClient, WebsocketEventPayload } from "../websocket.js";
2
+ import type { HttpTransport, Fetch } from "../transport.js";
3
+ import type { CheckpointRecord, ContentBlock, SessionMessagesPaginatedResponse, SessionMessagesResponse, SessionRecord, SpaceAccessPolicy, SpaceBootstrapSource, SpaceChannelBindingInput, SpaceCheckpointDetailResponse, SpaceCreateResponse, SpaceEnvInput, SpaceFsFileResponse, SpaceFsMoveInput, SpaceFsTreeResponse, SpaceFsWriteFileInput, SpaceMember, SpaceRecord, SpaceRole, SpaceSessionsResponse } from "../types.js";
4
+ export type SessionSubscriptionHandlers = {
5
+ progress?: (event: WebsocketEventPayload) => void;
6
+ final?: (event: WebsocketEventPayload) => void;
7
+ error?: (event: WebsocketEventPayload) => void;
8
+ persisted?: (event: WebsocketEventPayload) => void;
9
+ event?: (event: WebsocketEventPayload) => void;
10
+ };
11
+ export type SessionEventName = "turn.progress" | "turn.final" | "turn.error" | "message.persisted";
12
+ export type SpaceEventName = SessionEventName | "event";
13
+ type SessionSendMessageInput = {
14
+ content: ContentBlock[];
15
+ model?: string;
16
+ provider?: string;
17
+ clientMessageId?: string;
18
+ };
19
+ export declare class SpacesApi {
20
+ private readonly transport;
21
+ constructor(transport: HttpTransport);
22
+ list(customFetch?: Fetch): Promise<SpaceRecord[]>;
23
+ get(spaceId: string, customFetch?: Fetch): Promise<SpaceRecord>;
24
+ create(input?: {
25
+ name?: string;
26
+ description?: string;
27
+ source?: string;
28
+ extraEnv?: SpaceEnvInput[];
29
+ channelBindings?: SpaceChannelBindingInput[];
30
+ bootstrapSource?: SpaceBootstrapSource;
31
+ }, headers?: Record<string, string>): Promise<SpaceCreateResponse>;
32
+ }
33
+ export declare class SpaceFilesApi {
34
+ private readonly transport;
35
+ private readonly spaceId;
36
+ constructor(transport: HttpTransport, spaceId: string);
37
+ list(path?: string, customFetch?: Fetch): Promise<SpaceFsTreeResponse>;
38
+ read(path: string, customFetch?: Fetch): Promise<SpaceFsFileResponse>;
39
+ getDownloadUrl(path: string): string;
40
+ write(input: SpaceFsWriteFileInput): Promise<{
41
+ ok: true;
42
+ path: string;
43
+ size: number;
44
+ mtimeMs: number;
45
+ }>;
46
+ createDir(path: string): Promise<{
47
+ ok: true;
48
+ path: string;
49
+ size: number;
50
+ mtimeMs: number;
51
+ }>;
52
+ delete(path: string, recursive?: boolean): Promise<{
53
+ ok: true;
54
+ path: string;
55
+ }>;
56
+ move(input: SpaceFsMoveInput): Promise<{
57
+ ok: true;
58
+ fromPath: string;
59
+ toPath: string;
60
+ }>;
61
+ }
62
+ declare class SessionMessagesClient {
63
+ private readonly transport;
64
+ private readonly sessionId;
65
+ private lastSentSignature;
66
+ private lastSentSessionId;
67
+ private lastSentAt;
68
+ constructor(transport: HttpTransport, sessionId: string);
69
+ list(customFetch?: Fetch): Promise<SessionMessagesResponse>;
70
+ listPaginated(options?: {
71
+ cursor?: number;
72
+ limit?: number;
73
+ direction?: "older" | "newer";
74
+ }, customFetch?: Fetch): Promise<SessionMessagesPaginatedResponse>;
75
+ send(input: SessionSendMessageInput): Promise<{
76
+ ok: true;
77
+ userMessageId: string;
78
+ }>;
79
+ }
80
+ declare class SessionRealtimeClient {
81
+ private readonly websocketClient;
82
+ private readonly spaceId;
83
+ private readonly sessionId;
84
+ constructor(websocketClient: WebsocketClient | null, spaceId: string, sessionId: string);
85
+ subscribe(handlers: SessionSubscriptionHandlers): () => void;
86
+ on(type: SessionEventName, handler: (event: WebsocketEventPayload) => void): () => void;
87
+ }
88
+ export declare class SessionClient {
89
+ readonly spaceId: string;
90
+ readonly id: string;
91
+ private readonly transport;
92
+ readonly messages: SessionMessagesClient;
93
+ readonly realtime: SessionRealtimeClient;
94
+ constructor(spaceId: string, id: string, transport: HttpTransport, websocketClient: WebsocketClient | null);
95
+ get(customFetch?: Fetch): Promise<{
96
+ space: SpaceRecord;
97
+ session: SessionRecord;
98
+ }>;
99
+ subscribe(handlers: SessionSubscriptionHandlers): () => void;
100
+ on(type: SessionEventName, handler: (event: WebsocketEventPayload) => void): () => void;
101
+ }
102
+ export declare class SpaceSessionsApi {
103
+ private readonly transport;
104
+ private readonly spaceId;
105
+ private readonly websocketClient;
106
+ constructor(transport: HttpTransport, spaceId: string, websocketClient: WebsocketClient | null);
107
+ create(input?: {
108
+ title?: string;
109
+ source?: string;
110
+ }): Promise<{
111
+ ok: true;
112
+ session: SessionRecord;
113
+ }>;
114
+ list(customFetch?: Fetch): Promise<SpaceSessionsResponse>;
115
+ byId(sessionId: string): SessionClient;
116
+ }
117
+ export declare class SpaceEventsApi {
118
+ private readonly websocketClient;
119
+ private readonly spaceId;
120
+ constructor(websocketClient: WebsocketClient | null, spaceId: string);
121
+ subscribe(handler: (event: WebsocketEventPayload) => void): () => void;
122
+ on(type: SpaceEventName, handler: (event: WebsocketEventPayload) => void): () => void;
123
+ }
124
+ export declare class SpaceMembersApi {
125
+ private readonly transport;
126
+ private readonly spaceId;
127
+ constructor(transport: HttpTransport, spaceId: string);
128
+ list(): Promise<{
129
+ items: SpaceMember[];
130
+ }>;
131
+ update(userId: string, role: SpaceRole): Promise<SpaceMember>;
132
+ remove(userId: string): Promise<{
133
+ ok: true;
134
+ }>;
135
+ }
136
+ export declare class SpaceAccessApi {
137
+ private readonly transport;
138
+ private readonly spaceId;
139
+ constructor(transport: HttpTransport, spaceId: string);
140
+ get(): Promise<SpaceAccessPolicy>;
141
+ set(body: {
142
+ signed_in_user?: SpaceRole | null;
143
+ anonymous_user?: SpaceRole | null;
144
+ }): Promise<SpaceAccessPolicy>;
145
+ }
146
+ export declare class SpaceCheckpointsApi {
147
+ private readonly transport;
148
+ private readonly spaceId;
149
+ constructor(transport: HttpTransport, spaceId: string);
150
+ create(description?: string | null): Promise<{
151
+ ok: true;
152
+ taskRunId: string;
153
+ }>;
154
+ list(): Promise<{
155
+ checkpoints: CheckpointRecord[];
156
+ }>;
157
+ get(checkpointId: string, customFetch?: Fetch): Promise<SpaceCheckpointDetailResponse>;
158
+ }
159
+ export declare class SpaceClient {
160
+ readonly id: string;
161
+ private readonly transport;
162
+ private readonly websocketClient;
163
+ readonly files: SpaceFilesApi;
164
+ readonly sessions: SpaceSessionsApi;
165
+ readonly members: SpaceMembersApi;
166
+ readonly access: SpaceAccessApi;
167
+ readonly checkpoints: SpaceCheckpointsApi;
168
+ constructor(id: string, transport: HttpTransport, websocketClient: WebsocketClient | null);
169
+ get(customFetch?: Fetch): Promise<SpaceRecord>;
170
+ rename(name: string): Promise<{
171
+ space: SpaceRecord;
172
+ }>;
173
+ session(sessionId: string): SessionClient;
174
+ subscribe(handler: (event: WebsocketEventPayload) => void): () => void;
175
+ on(type: SpaceEventName, handler: (event: WebsocketEventPayload) => void): () => void;
176
+ }
177
+ export {};