cccc-sdk 0.1.0 → 0.1.1

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.
Files changed (50) hide show
  1. package/README.md +68 -186
  2. package/dist/client.d.ts +101 -186
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +373 -410
  5. package/dist/client.js.map +1 -0
  6. package/dist/errors.d.ts +28 -10
  7. package/dist/errors.d.ts.map +1 -0
  8. package/dist/errors.js +42 -16
  9. package/dist/errors.js.map +1 -0
  10. package/dist/index.d.ts +7 -7
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +11 -10
  13. package/dist/index.js.map +1 -0
  14. package/dist/transport.d.ts +19 -24
  15. package/dist/transport.d.ts.map +1 -0
  16. package/dist/transport.js +202 -92
  17. package/dist/transport.js.map +1 -0
  18. package/dist/types.d.ts +273 -0
  19. package/dist/types.d.ts.map +1 -0
  20. package/dist/types.js +5 -0
  21. package/dist/types.js.map +1 -0
  22. package/package.json +24 -25
  23. package/dist/types/actor.d.ts +0 -42
  24. package/dist/types/actor.d.ts.map +0 -1
  25. package/dist/types/actor.js +0 -5
  26. package/dist/types/actor.js.map +0 -1
  27. package/dist/types/context.d.ts +0 -89
  28. package/dist/types/context.d.ts.map +0 -1
  29. package/dist/types/context.js +0 -5
  30. package/dist/types/context.js.map +0 -1
  31. package/dist/types/event.d.ts +0 -15
  32. package/dist/types/event.d.ts.map +0 -1
  33. package/dist/types/event.js +0 -5
  34. package/dist/types/event.js.map +0 -1
  35. package/dist/types/group.d.ts +0 -33
  36. package/dist/types/group.d.ts.map +0 -1
  37. package/dist/types/group.js +0 -5
  38. package/dist/types/group.js.map +0 -1
  39. package/dist/types/index.d.ts +0 -10
  40. package/dist/types/index.d.ts.map +0 -1
  41. package/dist/types/index.js +0 -10
  42. package/dist/types/index.js.map +0 -1
  43. package/dist/types/ipc.d.ts +0 -24
  44. package/dist/types/ipc.d.ts.map +0 -1
  45. package/dist/types/ipc.js +0 -10
  46. package/dist/types/ipc.js.map +0 -1
  47. package/dist/types/message.d.ts +0 -53
  48. package/dist/types/message.d.ts.map +0 -1
  49. package/dist/types/message.js +0 -5
  50. package/dist/types/message.js.map +0 -1
package/dist/client.js CHANGED
@@ -1,536 +1,499 @@
1
1
  /**
2
- * CCCC Client SDK
3
- *
4
- * High-level API for interacting with CCCC daemon
2
+ * CCCC SDK client
5
3
  */
6
- import { callDaemon, pingDaemon } from './transport.js';
7
- import { createRequest } from './types/ipc.js';
8
- import { CCCCError } from './errors.js';
4
+ import { DaemonAPIError, IncompatibleDaemonError, } from './errors.js';
5
+ import { discoverEndpoint, callDaemon, openEventsStream, readLines, } from './transport.js';
9
6
  /**
10
- * Handle daemon response, throwing on error
7
+ * CCCC client
11
8
  */
12
- function handleResponse(response) {
13
- if (!response.ok) {
14
- if (response.error) {
15
- throw CCCCError.fromDaemonError(response.error);
16
- }
17
- throw new CCCCError('DAEMON_ERROR', 'Unknown daemon error');
18
- }
19
- return response.result;
20
- }
21
- /**
22
- * Groups API
23
- */
24
- class GroupsAPI {
25
- client;
26
- constructor(client) {
27
- this.client = client;
9
+ export class CCCCClient {
10
+ constructor(endpoint, timeoutMs) {
11
+ this._endpoint = endpoint;
12
+ this._timeoutMs = timeoutMs;
28
13
  }
29
14
  /**
30
- * List all groups
15
+ * Async factory method: create a client instance
31
16
  */
32
- async list() {
33
- const response = await this.client.call('groups', {});
34
- return (handleResponse(response)).groups;
17
+ static async create(options = {}) {
18
+ const endpoint = options.endpoint ?? await discoverEndpoint(options.ccccHome);
19
+ const timeoutMs = options.timeoutMs ?? 30000;
20
+ return new CCCCClient(endpoint, timeoutMs);
35
21
  }
36
22
  /**
37
- * Get group info
23
+ * Get the current endpoint
38
24
  */
39
- async get(groupId) {
40
- const response = await this.client.call('group_show', { group_id: groupId });
41
- return handleResponse(response);
25
+ get endpoint() {
26
+ return this._endpoint;
42
27
  }
28
+ // ============================================================
29
+ // Low-level API
30
+ // ============================================================
43
31
  /**
44
- * Create a new group
32
+ * Send raw IPC request and return the full response
45
33
  */
46
- async create(options) {
47
- const response = await this.client.call('group_create', {
48
- title: options.title,
49
- topic: options.topic ?? '',
50
- url: options.url ?? '',
51
- });
52
- return handleResponse(response);
53
- }
54
- /**
55
- * Set group state
56
- */
57
- async setState(groupId, state, by) {
58
- const response = await this.client.call('group_set_state', {
59
- group_id: groupId,
60
- state,
61
- by,
62
- });
63
- handleResponse(response);
34
+ async callRaw(op, args) {
35
+ const request = {
36
+ v: 1,
37
+ op,
38
+ args: args ?? {},
39
+ };
40
+ const response = await callDaemon(this._endpoint, request, this._timeoutMs);
41
+ if (!response.ok && response.error) {
42
+ throw new DaemonAPIError(response.error.code, response.error.message, response.error.details, response);
43
+ }
44
+ return response;
64
45
  }
65
46
  /**
66
- * Start group (activate all enabled actors)
47
+ * Send IPC request and return only result payload
67
48
  */
68
- async start(groupId) {
69
- const response = await this.client.call('group_start', { group_id: groupId });
70
- handleResponse(response);
49
+ async call(op, args) {
50
+ const response = await this.callRaw(op, args);
51
+ return response.result ?? {};
71
52
  }
72
53
  /**
73
- * Stop group (stop all actors)
54
+ * Compatibility check
74
55
  */
75
- async stop(groupId) {
76
- const response = await this.client.call('group_stop', { group_id: groupId });
77
- handleResponse(response);
56
+ async assertCompatible(options = {}) {
57
+ const pingResult = await this.ping();
58
+ const ipcV = pingResult['ipc_v'] ?? 0;
59
+ const capabilities = pingResult['capabilities'] ?? {};
60
+ // Check IPC version
61
+ const requiredV = options.requireIpcV ?? 1;
62
+ if (ipcV < requiredV) {
63
+ throw new IncompatibleDaemonError(`IPC version ${ipcV} < required ${requiredV}`);
64
+ }
65
+ // Check capabilities
66
+ for (const [cap, required] of Object.entries(options.requireCapabilities ?? {})) {
67
+ if (required && !capabilities[cap]) {
68
+ throw new IncompatibleDaemonError(`Missing capability: ${cap}`);
69
+ }
70
+ }
71
+ // Check operation support by probing
72
+ const reservedOps = new Set(['ping', 'shutdown', 'events_stream', 'term_attach']);
73
+ for (const op of options.requireOps ?? []) {
74
+ if (reservedOps.has(op))
75
+ continue;
76
+ try {
77
+ await this.callRaw(op, {});
78
+ }
79
+ catch (e) {
80
+ if (e instanceof DaemonAPIError && e.code === 'unknown_op') {
81
+ throw new IncompatibleDaemonError(`Operation not supported: ${op}`);
82
+ }
83
+ // Other errors (e.g. missing_group_id) imply the operation exists.
84
+ }
85
+ }
86
+ return pingResult;
78
87
  }
88
+ // ============================================================
89
+ // Convenience methods: diagnostics
90
+ // ============================================================
79
91
  /**
80
- * Update group
92
+ * Ping daemon
81
93
  */
82
- async update(groupId, patch) {
83
- const response = await this.client.call('group_update', {
84
- group_id: groupId,
85
- patch,
86
- });
87
- handleResponse(response);
94
+ async ping() {
95
+ return this.call('ping');
88
96
  }
97
+ // ============================================================
98
+ // Convenience methods: group operations
99
+ // ============================================================
89
100
  /**
90
- * Delete group
101
+ * List all groups
91
102
  */
92
- async delete(groupId) {
93
- const response = await this.client.call('group_delete', { group_id: groupId });
94
- handleResponse(response);
95
- }
96
- }
97
- /**
98
- * Actors API
99
- */
100
- class ActorsAPI {
101
- client;
102
- constructor(client) {
103
- this.client = client;
103
+ async groups() {
104
+ return this.call('groups');
104
105
  }
105
106
  /**
106
- * List actors in a group
107
+ * Show group details
107
108
  */
108
- async list(groupId) {
109
- const response = await this.client.call('actor_list', { group_id: groupId });
110
- return (handleResponse(response)).actors;
109
+ async groupShow(groupId) {
110
+ return this.call('group_show', { group_id: groupId });
111
111
  }
112
112
  /**
113
- * Add an actor to a group
113
+ * Create group
114
114
  */
115
- async add(groupId, by, options) {
116
- const response = await this.client.call('actor_add', {
117
- group_id: groupId,
118
- by,
119
- actor_id: options.actor_id,
120
- runtime: options.runtime ?? 'claude',
121
- runner: options.runner ?? 'pty',
115
+ async groupCreate(options = {}) {
116
+ return this.call('group_create', {
122
117
  title: options.title ?? '',
123
- command: options.command ?? [],
124
- env: options.env ?? {},
118
+ topic: options.topic ?? '',
119
+ by: options.by ?? 'user',
125
120
  });
126
- return handleResponse(response).actor;
127
121
  }
128
122
  /**
129
- * Start an actor
123
+ * Update group
130
124
  */
131
- async start(groupId, actorId, by) {
132
- const response = await this.client.call('actor_start', {
133
- group_id: groupId,
134
- actor_id: actorId,
135
- by,
125
+ async groupUpdate(options) {
126
+ return this.call('group_update', {
127
+ group_id: options.groupId,
128
+ patch: options.patch,
129
+ by: options.by ?? 'user',
136
130
  });
137
- handleResponse(response);
138
131
  }
139
132
  /**
140
- * Stop an actor
133
+ * Delete group
141
134
  */
142
- async stop(groupId, actorId, by) {
143
- const response = await this.client.call('actor_stop', {
144
- group_id: groupId,
145
- actor_id: actorId,
146
- by,
147
- });
148
- handleResponse(response);
135
+ async groupDelete(groupId, by = 'user') {
136
+ return this.call('group_delete', { group_id: groupId, by });
149
137
  }
150
138
  /**
151
- * Restart an actor
139
+ * Use group (set active scope)
152
140
  */
153
- async restart(groupId, actorId, by) {
154
- const response = await this.client.call('actor_restart', {
155
- group_id: groupId,
156
- actor_id: actorId,
157
- by,
158
- });
159
- handleResponse(response);
141
+ async groupUse(groupId, path, by = 'user') {
142
+ return this.call('group_use', { group_id: groupId, path, by });
160
143
  }
161
144
  /**
162
- * Remove an actor
145
+ * Set group state
163
146
  */
164
- async remove(groupId, actorId, by) {
165
- const response = await this.client.call('actor_remove', {
166
- group_id: groupId,
167
- actor_id: actorId,
168
- by,
169
- });
170
- handleResponse(response);
147
+ async groupSetState(groupId, state, by = 'user') {
148
+ return this.call('group_set_state', { group_id: groupId, state, by });
171
149
  }
172
- }
173
- /**
174
- * Messages API
175
- */
176
- class MessagesAPI {
177
- client;
178
- constructor(client) {
179
- this.client = client;
150
+ /**
151
+ * Update group settings
152
+ */
153
+ async groupSettingsUpdate(groupId, patch, by = 'user') {
154
+ return this.call('group_settings_update', { group_id: groupId, patch, by });
180
155
  }
181
156
  /**
182
- * Send a message
157
+ * Read group-level automation state (rules, snippets, next run, ...)
183
158
  */
184
- async send(groupId, by, options) {
185
- const response = await this.client.call('send', {
186
- group_id: groupId,
187
- by,
188
- text: options.text,
189
- to: options.to ?? [],
190
- format: options.format ?? 'plain',
191
- });
192
- return handleResponse(response).event;
159
+ async groupAutomationState(groupId, by = 'user') {
160
+ return this.call('group_automation_state', { group_id: groupId, by });
193
161
  }
194
162
  /**
195
- * Reply to a message
163
+ * Replace group-level automation (rules + snippets)
196
164
  */
197
- async reply(groupId, by, options) {
198
- const response = await this.client.call('reply', {
199
- group_id: groupId,
200
- by,
201
- event_id: options.event_id,
202
- text: options.text,
203
- to: options.to ?? [],
204
- });
205
- return handleResponse(response).event;
165
+ async groupAutomationUpdate(options) {
166
+ const args = {
167
+ group_id: options.groupId,
168
+ by: options.by ?? 'user',
169
+ ruleset: options.ruleset,
170
+ };
171
+ if (options.expectedVersion !== undefined) {
172
+ args['expected_version'] = options.expectedVersion;
173
+ }
174
+ return this.call('group_automation_update', args);
206
175
  }
207
- }
208
- /**
209
- * Inbox API
210
- */
211
- class InboxAPI {
212
- client;
213
- constructor(client) {
214
- this.client = client;
176
+ /**
177
+ * Incrementally manage group-level automation (actions[])
178
+ */
179
+ async groupAutomationManage(options) {
180
+ const actions = options.actions;
181
+ if (actions.length === 0) {
182
+ throw new Error('groupAutomationManage requires a non-empty actions array');
183
+ }
184
+ const args = {
185
+ group_id: options.groupId,
186
+ by: options.by ?? 'user',
187
+ actions,
188
+ };
189
+ if (options.expectedVersion !== undefined)
190
+ args['expected_version'] = options.expectedVersion;
191
+ return this.call('group_automation_manage', args);
192
+ }
193
+ /**
194
+ * Reset group-level automation to baseline
195
+ */
196
+ async groupAutomationResetBaseline(options) {
197
+ const args = {
198
+ group_id: options.groupId,
199
+ by: options.by ?? 'user',
200
+ };
201
+ if (options.expectedVersion !== undefined) {
202
+ args['expected_version'] = options.expectedVersion;
203
+ }
204
+ return this.call('group_automation_reset_baseline', args);
215
205
  }
216
206
  /**
217
- * List inbox messages
207
+ * Start group
218
208
  */
219
- async list(groupId, actorId, options = {}) {
220
- const response = await this.client.call('inbox_list', {
221
- group_id: groupId,
222
- actor_id: actorId,
223
- kind_filter: options.kind_filter ?? 'all',
224
- limit: options.limit ?? 50,
225
- });
226
- return (handleResponse(response)).messages;
209
+ async groupStart(groupId, by = 'user') {
210
+ return this.call('group_start', { group_id: groupId, by });
227
211
  }
228
212
  /**
229
- * Mark messages as read up to event_id
213
+ * Stop group
230
214
  */
231
- async markRead(groupId, actorId, eventId) {
232
- const response = await this.client.call('inbox_mark_read', {
233
- group_id: groupId,
234
- actor_id: actorId,
235
- event_id: eventId,
236
- });
237
- handleResponse(response);
215
+ async groupStop(groupId, by = 'user') {
216
+ return this.call('group_stop', { group_id: groupId, by });
238
217
  }
239
218
  /**
240
- * Mark all messages as read
219
+ * Attach path to group
241
220
  */
242
- async markAllRead(groupId, actorId) {
243
- const response = await this.client.call('inbox_mark_all_read', {
244
- group_id: groupId,
245
- actor_id: actorId,
246
- });
247
- handleResponse(response);
221
+ async attach(path, groupId = '', by = 'user') {
222
+ const args = { path, by };
223
+ if (groupId)
224
+ args['group_id'] = groupId;
225
+ return this.call('attach', args);
248
226
  }
249
- }
250
- /**
251
- * Context API
252
- */
253
- class ContextAPI {
254
- client;
255
- constructor(client) {
256
- this.client = client;
227
+ // ============================================================
228
+ // Convenience methods: actor operations
229
+ // ============================================================
230
+ /**
231
+ * List actors in group
232
+ */
233
+ async actorList(groupId) {
234
+ return this.call('actor_list', { group_id: groupId });
257
235
  }
258
236
  /**
259
- * Get project context
237
+ * Add actor
260
238
  */
261
- async get(groupId) {
262
- const response = await this.client.call('context_get', { group_id: groupId });
263
- return handleResponse(response);
239
+ async actorAdd(options) {
240
+ const args = {
241
+ group_id: options.groupId,
242
+ by: options.by ?? 'user',
243
+ };
244
+ if (options.actorId)
245
+ args['actor_id'] = options.actorId;
246
+ if (options.title)
247
+ args['title'] = options.title;
248
+ if (options.runtime)
249
+ args['runtime'] = options.runtime;
250
+ if (options.runner)
251
+ args['runner'] = options.runner;
252
+ if (options.command)
253
+ args['command'] = options.command;
254
+ if (options.env)
255
+ args['env'] = options.env;
256
+ if (options.envPrivate)
257
+ args['env_private'] = options.envPrivate;
258
+ if (options.defaultScopeKey)
259
+ args['default_scope_key'] = options.defaultScopeKey;
260
+ if (options.submit)
261
+ args['submit'] = options.submit;
262
+ return this.call('actor_add', args);
264
263
  }
265
264
  /**
266
- * Sync context with batch operations
265
+ * Update actor
267
266
  */
268
- async sync(groupId, ops, dryRun = false) {
269
- const response = await this.client.call('context_sync', {
270
- group_id: groupId,
271
- ops,
272
- dry_run: dryRun,
267
+ async actorUpdate(options) {
268
+ return this.call('actor_update', {
269
+ group_id: options.groupId,
270
+ actor_id: options.actorId,
271
+ patch: options.patch,
272
+ by: options.by ?? 'user',
273
273
  });
274
- return handleResponse(response);
275
274
  }
276
- }
277
- /**
278
- * Tasks API
279
- */
280
- class TasksAPI {
281
- client;
282
- constructor(client) {
283
- this.client = client;
275
+ /**
276
+ * Remove actor
277
+ */
278
+ async actorRemove(groupId, actorId, by = 'user') {
279
+ return this.call('actor_remove', { group_id: groupId, actor_id: actorId, by });
284
280
  }
285
281
  /**
286
- * List tasks
282
+ * Start actor
287
283
  */
288
- async list(groupId, includeArchived = false) {
289
- const response = await this.client.call('task_list', {
290
- group_id: groupId,
291
- include_archived: includeArchived,
292
- });
293
- return (handleResponse(response)).tasks;
284
+ async actorStart(groupId, actorId, by = 'user') {
285
+ return this.call('actor_start', { group_id: groupId, actor_id: actorId, by });
294
286
  }
295
287
  /**
296
- * Create a task
288
+ * Stop actor
297
289
  */
298
- async create(groupId, options) {
299
- const response = await this.client.call('context_sync', {
300
- group_id: groupId,
301
- ops: [
302
- {
303
- op: 'task.create',
304
- name: options.name,
305
- goal: options.goal,
306
- steps: options.steps,
307
- milestone_id: options.milestone_id,
308
- assignee: options.assignee,
309
- },
310
- ],
311
- });
312
- const result = handleResponse(response);
313
- return result.results[0].task;
290
+ async actorStop(groupId, actorId, by = 'user') {
291
+ return this.call('actor_stop', { group_id: groupId, actor_id: actorId, by });
314
292
  }
315
293
  /**
316
- * Update a task
294
+ * Restart actor
317
295
  */
318
- async update(groupId, options) {
319
- const response = await this.client.call('context_sync', {
320
- group_id: groupId,
321
- ops: [
322
- {
323
- op: 'task.update',
324
- task_id: options.task_id,
325
- name: options.name,
326
- goal: options.goal,
327
- status: options.status,
328
- assignee: options.assignee,
329
- milestone_id: options.milestone_id,
330
- step_id: options.step_id,
331
- step_status: options.step_status,
332
- },
333
- ],
334
- });
335
- const result = handleResponse(response);
336
- return result.results[0].task;
296
+ async actorRestart(groupId, actorId, by = 'user') {
297
+ return this.call('actor_restart', { group_id: groupId, actor_id: actorId, by });
337
298
  }
338
- }
339
- /**
340
- * Milestones API
341
- */
342
- class MilestonesAPI {
343
- client;
344
- constructor(client) {
345
- this.client = client;
299
+ /**
300
+ * List actor private env keys (without values)
301
+ */
302
+ async actorEnvPrivateKeys(groupId, actorId, by = 'user') {
303
+ return this.call('actor_env_private_keys', { group_id: groupId, actor_id: actorId, by });
346
304
  }
347
305
  /**
348
- * Create a milestone
306
+ * Update actor private env vars (runtime-only; values are never echoed)
349
307
  */
350
- async create(groupId, options) {
351
- const response = await this.client.call('context_sync', {
352
- group_id: groupId,
353
- ops: [
354
- {
355
- op: 'milestone.create',
356
- name: options.name,
357
- description: options.description,
358
- status: options.status ?? 'planned',
359
- },
360
- ],
308
+ async actorEnvPrivateUpdate(options) {
309
+ return this.call('actor_env_private_update', {
310
+ group_id: options.groupId,
311
+ actor_id: options.actorId,
312
+ by: options.by ?? 'user',
313
+ set: options.set ?? undefined,
314
+ unset: options.unset ?? undefined,
315
+ clear: options.clear ?? false,
361
316
  });
362
- const result = handleResponse(response);
363
- return result.results[0].milestone;
364
317
  }
318
+ // ============================================================
319
+ // Convenience methods: messaging
320
+ // ============================================================
365
321
  /**
366
- * Complete a milestone
322
+ * Send message
367
323
  */
368
- async complete(groupId, milestoneId, outcomes) {
369
- const response = await this.client.call('context_sync', {
370
- group_id: groupId,
371
- ops: [
372
- {
373
- op: 'milestone.complete',
374
- milestone_id: milestoneId,
375
- outcomes,
376
- },
377
- ],
378
- });
379
- handleResponse(response);
324
+ async send(options) {
325
+ const args = {
326
+ group_id: options.groupId,
327
+ text: options.text,
328
+ by: options.by ?? 'user',
329
+ priority: options.priority ?? 'normal',
330
+ reply_required: options.replyRequired ?? false,
331
+ };
332
+ if (options.to)
333
+ args['to'] = options.to;
334
+ if (options.path)
335
+ args['path'] = options.path;
336
+ return this.call('send', args);
337
+ }
338
+ /**
339
+ * Send message across groups
340
+ */
341
+ async sendCrossGroup(options) {
342
+ const args = {
343
+ group_id: options.groupId,
344
+ dst_group_id: options.dstGroupId,
345
+ text: options.text,
346
+ by: options.by ?? 'user',
347
+ priority: options.priority ?? 'normal',
348
+ reply_required: options.replyRequired ?? false,
349
+ };
350
+ if (options.to)
351
+ args['to'] = options.to;
352
+ return this.call('send_cross_group', args);
380
353
  }
381
- }
382
- /**
383
- * Vision API
384
- */
385
- class VisionAPI {
386
- client;
387
- constructor(client) {
388
- this.client = client;
354
+ /**
355
+ * Reply message
356
+ */
357
+ async reply(options) {
358
+ const args = {
359
+ group_id: options.groupId,
360
+ reply_to: options.replyTo,
361
+ text: options.text,
362
+ by: options.by ?? 'user',
363
+ priority: options.priority ?? 'normal',
364
+ reply_required: options.replyRequired ?? false,
365
+ };
366
+ if (options.to)
367
+ args['to'] = options.to;
368
+ return this.call('reply', args);
389
369
  }
390
370
  /**
391
- * Update project vision
371
+ * Acknowledge chat message
392
372
  */
393
- async update(groupId, vision) {
394
- const response = await this.client.call('context_sync', {
373
+ async chatAck(groupId, actorId, eventId, by) {
374
+ return this.call('chat_ack', {
395
375
  group_id: groupId,
396
- ops: [{ op: 'vision.update', vision }],
376
+ actor_id: actorId,
377
+ event_id: eventId,
378
+ by: by ?? actorId,
397
379
  });
398
- handleResponse(response);
399
- }
400
- }
401
- /**
402
- * Sketch API
403
- */
404
- class SketchAPI {
405
- client;
406
- constructor(client) {
407
- this.client = client;
408
380
  }
381
+ // ============================================================
382
+ // Convenience methods: inbox
383
+ // ============================================================
409
384
  /**
410
- * Update execution sketch
385
+ * List inbox
411
386
  */
412
- async update(groupId, sketch) {
413
- const response = await this.client.call('context_sync', {
414
- group_id: groupId,
415
- ops: [{ op: 'sketch.update', sketch }],
387
+ async inboxList(options) {
388
+ return this.call('inbox_list', {
389
+ group_id: options.groupId,
390
+ actor_id: options.actorId,
391
+ by: options.by ?? 'user',
392
+ limit: options.limit ?? 50,
393
+ kind_filter: options.kindFilter ?? 'all',
416
394
  });
417
- handleResponse(response);
418
- }
419
- }
420
- /**
421
- * Headless API for MCP-driven actors
422
- */
423
- class HeadlessAPI {
424
- client;
425
- constructor(client) {
426
- this.client = client;
427
395
  }
428
396
  /**
429
- * Get headless session status
397
+ * Mark message as read
430
398
  */
431
- async status(groupId, actorId) {
432
- const response = await this.client.call('headless_status', {
399
+ async inboxMarkRead(groupId, actorId, eventId, by = 'user') {
400
+ return this.call('inbox_mark_read', {
433
401
  group_id: groupId,
434
402
  actor_id: actorId,
403
+ event_id: eventId,
404
+ by,
435
405
  });
436
- return handleResponse(response);
437
406
  }
438
407
  /**
439
- * Set headless session status
408
+ * Mark all messages as read
440
409
  */
441
- async setStatus(groupId, actorId, status, taskId) {
442
- const response = await this.client.call('headless_set_status', {
410
+ async inboxMarkAllRead(groupId, actorId, by = 'user', kindFilter = 'all') {
411
+ return this.call('inbox_mark_all_read', {
443
412
  group_id: groupId,
444
413
  actor_id: actorId,
445
- status,
446
- task_id: taskId,
414
+ by,
415
+ kind_filter: kindFilter,
447
416
  });
448
- handleResponse(response);
449
417
  }
418
+ // ============================================================
419
+ // Convenience methods: notifications
420
+ // ============================================================
450
421
  /**
451
- * Acknowledge processed message
422
+ * Acknowledge notification
452
423
  */
453
- async ackMessage(groupId, actorId, messageId) {
454
- const response = await this.client.call('headless_ack_message', {
424
+ async notifyAck(groupId, actorId, notifyEventId, by) {
425
+ return this.call('notify_ack', {
455
426
  group_id: groupId,
456
427
  actor_id: actorId,
457
- message_id: messageId,
428
+ notify_event_id: notifyEventId,
429
+ by: by ?? actorId,
458
430
  });
459
- handleResponse(response);
460
- }
461
- }
462
- /**
463
- * CCCC Client
464
- *
465
- * Main entry point for SDK
466
- *
467
- * @example
468
- * ```typescript
469
- * const client = new CCCCClient();
470
- *
471
- * // List all groups
472
- * const groups = await client.groups.list();
473
- *
474
- * // Add an actor
475
- * await client.actors.add(groupId, 'user', {
476
- * actor_id: 'agent-1',
477
- * runtime: 'claude',
478
- * runner: 'pty'
479
- * });
480
- *
481
- * // Send a message
482
- * await client.messages.send(groupId, 'user', {
483
- * text: 'Hello agents!',
484
- * to: ['agent-1']
485
- * });
486
- * ```
487
- */
488
- export class CCCCClient {
489
- options;
490
- // API namespaces
491
- groups;
492
- actors;
493
- messages;
494
- inbox;
495
- context;
496
- tasks;
497
- milestones;
498
- vision;
499
- sketch;
500
- headless;
501
- constructor(options = {}) {
502
- this.options = options;
503
- // Initialize API namespaces
504
- this.groups = new GroupsAPI(this);
505
- this.actors = new ActorsAPI(this);
506
- this.messages = new MessagesAPI(this);
507
- this.inbox = new InboxAPI(this);
508
- this.context = new ContextAPI(this);
509
- this.tasks = new TasksAPI(this);
510
- this.milestones = new MilestonesAPI(this);
511
- this.vision = new VisionAPI(this);
512
- this.sketch = new SketchAPI(this);
513
- this.headless = new HeadlessAPI(this);
514
- }
515
- /**
516
- * Ping daemon to check if it's running
517
- */
518
- async ping() {
519
- return pingDaemon(this.options);
520
431
  }
432
+ // ============================================================
433
+ // Convenience methods: context
434
+ // ============================================================
521
435
  /**
522
- * Low-level daemon call
436
+ * Get group context
523
437
  */
524
- async call(op, args) {
525
- const request = createRequest(op, args);
526
- return callDaemon(request, this.options);
438
+ async contextGet(groupId) {
439
+ return this.call('context_get', { group_id: groupId });
527
440
  }
528
441
  /**
529
- * Shutdown the daemon
442
+ * Sync context
530
443
  */
531
- async shutdown() {
532
- const response = await this.call('shutdown', {});
533
- handleResponse(response);
444
+ async contextSync(options) {
445
+ return this.call('context_sync', {
446
+ group_id: options.groupId,
447
+ ops: options.ops,
448
+ by: options.by ?? 'system',
449
+ dry_run: options.dryRun ?? false,
450
+ });
451
+ }
452
+ // ============================================================
453
+ // Event stream
454
+ // ============================================================
455
+ /**
456
+ * Subscribe to event stream
457
+ */
458
+ async *eventsStream(options) {
459
+ const args = {
460
+ group_id: options.groupId,
461
+ by: options.by ?? 'user',
462
+ };
463
+ if (options.kinds) {
464
+ args['kinds'] = options.kinds instanceof Set
465
+ ? Array.from(options.kinds)
466
+ : options.kinds;
467
+ }
468
+ if (options.sinceEventId) {
469
+ args['since_event_id'] = options.sinceEventId;
470
+ }
471
+ if (options.sinceTs) {
472
+ args['since_ts'] = options.sinceTs;
473
+ }
474
+ const request = {
475
+ v: 1,
476
+ op: 'events_stream',
477
+ args,
478
+ };
479
+ const { socket, handshake, initialBuffer } = await openEventsStream(this._endpoint, request, options.timeoutMs ?? this._timeoutMs);
480
+ if (!handshake.ok) {
481
+ socket.destroy();
482
+ throw new DaemonAPIError(handshake.error?.code ?? 'unknown', handshake.error?.message ?? 'Handshake failed', handshake.error?.details, handshake);
483
+ }
484
+ try {
485
+ for await (const line of readLines(socket, initialBuffer)) {
486
+ try {
487
+ yield JSON.parse(line);
488
+ }
489
+ catch {
490
+ // Skip invalid JSON lines.
491
+ }
492
+ }
493
+ }
494
+ finally {
495
+ socket.destroy();
496
+ }
534
497
  }
535
498
  }
536
499
  //# sourceMappingURL=client.js.map