claudehq 1.0.2 → 1.0.5

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.
@@ -0,0 +1,417 @@
1
+ /**
2
+ * Orchestration API Routes - HTTP route handlers for multi-agent orchestration
3
+ *
4
+ * Provides REST API endpoints for:
5
+ * - Orchestration CRUD
6
+ * - Agent CRUD
7
+ * - Dependency management
8
+ * - Templates
9
+ * - Execution control
10
+ */
11
+
12
+ /**
13
+ * Create orchestration route handlers
14
+ * @param {Object} deps - Dependencies
15
+ * @param {Object} deps.orchestrationData - Orchestration data module
16
+ * @param {Object} deps.orchestrationExecutor - Orchestration executor module
17
+ * @returns {Object} Route handlers
18
+ */
19
+ function createOrchestrationRoutes(deps) {
20
+ const { orchestrationData, orchestrationExecutor } = deps;
21
+
22
+ return {
23
+ // =========================================================================
24
+ // Orchestration CRUD
25
+ // =========================================================================
26
+
27
+ /**
28
+ * GET /api/orchestrations - List all orchestrations
29
+ */
30
+ handleListOrchestrations(req, res) {
31
+ const orchestrations = orchestrationData.listOrchestrations();
32
+ res.writeHead(200, { 'Content-Type': 'application/json' });
33
+ res.end(JSON.stringify({ ok: true, orchestrations }));
34
+ },
35
+
36
+ /**
37
+ * POST /api/orchestrations - Create a new orchestration
38
+ */
39
+ handleCreateOrchestration(req, res) {
40
+ let body = '';
41
+ req.on('data', chunk => body += chunk);
42
+ req.on('end', () => {
43
+ try {
44
+ const config = JSON.parse(body);
45
+ const result = orchestrationData.createOrchestration(config);
46
+
47
+ if (result.error) {
48
+ res.writeHead(400, { 'Content-Type': 'application/json' });
49
+ res.end(JSON.stringify({ ok: false, error: result.error }));
50
+ } else {
51
+ res.writeHead(201, { 'Content-Type': 'application/json' });
52
+ res.end(JSON.stringify({ ok: true, orchestration: result.orchestration }));
53
+ }
54
+ } catch (e) {
55
+ res.writeHead(400, { 'Content-Type': 'application/json' });
56
+ res.end(JSON.stringify({ ok: false, error: e.message }));
57
+ }
58
+ });
59
+ },
60
+
61
+ /**
62
+ * GET /api/orchestrations/:id - Get orchestration by ID
63
+ */
64
+ handleGetOrchestration(req, res, id) {
65
+ const orchestration = orchestrationData.getOrchestration(id);
66
+ if (orchestration) {
67
+ res.writeHead(200, { 'Content-Type': 'application/json' });
68
+ res.end(JSON.stringify({ ok: true, orchestration }));
69
+ } else {
70
+ res.writeHead(404, { 'Content-Type': 'application/json' });
71
+ res.end(JSON.stringify({ ok: false, error: 'Orchestration not found' }));
72
+ }
73
+ },
74
+
75
+ /**
76
+ * PATCH /api/orchestrations/:id - Update orchestration
77
+ */
78
+ handleUpdateOrchestration(req, res, id) {
79
+ let body = '';
80
+ req.on('data', chunk => body += chunk);
81
+ req.on('end', () => {
82
+ try {
83
+ const updates = JSON.parse(body);
84
+ const result = orchestrationData.updateOrchestration(id, updates);
85
+
86
+ if (result.error) {
87
+ res.writeHead(404, { 'Content-Type': 'application/json' });
88
+ res.end(JSON.stringify({ ok: false, error: result.error }));
89
+ } else {
90
+ res.writeHead(200, { 'Content-Type': 'application/json' });
91
+ res.end(JSON.stringify({ ok: true, orchestration: result.orchestration }));
92
+ }
93
+ } catch (e) {
94
+ res.writeHead(400, { 'Content-Type': 'application/json' });
95
+ res.end(JSON.stringify({ ok: false, error: e.message }));
96
+ }
97
+ });
98
+ },
99
+
100
+ /**
101
+ * DELETE /api/orchestrations/:id - Delete orchestration
102
+ */
103
+ handleDeleteOrchestration(req, res, id) {
104
+ const result = orchestrationData.deleteOrchestration(id);
105
+ if (result.error) {
106
+ res.writeHead(404, { 'Content-Type': 'application/json' });
107
+ res.end(JSON.stringify({ ok: false, error: result.error }));
108
+ } else {
109
+ res.writeHead(200, { 'Content-Type': 'application/json' });
110
+ res.end(JSON.stringify({ ok: true }));
111
+ }
112
+ },
113
+
114
+ // =========================================================================
115
+ // Orchestration Execution
116
+ // =========================================================================
117
+
118
+ /**
119
+ * POST /api/orchestrations/:id/start - Start orchestration
120
+ */
121
+ handleStartOrchestration(req, res, id) {
122
+ orchestrationExecutor.startOrchestration(id).then((result) => {
123
+ if (result.error) {
124
+ res.writeHead(400, { 'Content-Type': 'application/json' });
125
+ res.end(JSON.stringify({ ok: false, error: result.error }));
126
+ } else {
127
+ res.writeHead(200, { 'Content-Type': 'application/json' });
128
+ res.end(JSON.stringify({ ok: true, ...result }));
129
+ }
130
+ }).catch((e) => {
131
+ res.writeHead(500, { 'Content-Type': 'application/json' });
132
+ res.end(JSON.stringify({ ok: false, error: e.message }));
133
+ });
134
+ },
135
+
136
+ /**
137
+ * POST /api/orchestrations/:id/stop - Stop orchestration
138
+ */
139
+ handleStopOrchestration(req, res, id) {
140
+ orchestrationExecutor.stopOrchestration(id).then((result) => {
141
+ if (result.error) {
142
+ res.writeHead(400, { 'Content-Type': 'application/json' });
143
+ res.end(JSON.stringify({ ok: false, error: result.error }));
144
+ } else {
145
+ res.writeHead(200, { 'Content-Type': 'application/json' });
146
+ res.end(JSON.stringify({ ok: true, ...result }));
147
+ }
148
+ }).catch((e) => {
149
+ res.writeHead(500, { 'Content-Type': 'application/json' });
150
+ res.end(JSON.stringify({ ok: false, error: e.message }));
151
+ });
152
+ },
153
+
154
+ /**
155
+ * GET /api/orchestrations/:id/stats - Get orchestration statistics
156
+ */
157
+ handleGetStats(req, res, id) {
158
+ const stats = orchestrationData.getOrchestrationStats(id);
159
+ if (stats) {
160
+ res.writeHead(200, { 'Content-Type': 'application/json' });
161
+ res.end(JSON.stringify({ ok: true, stats }));
162
+ } else {
163
+ res.writeHead(404, { 'Content-Type': 'application/json' });
164
+ res.end(JSON.stringify({ ok: false, error: 'Orchestration not found' }));
165
+ }
166
+ },
167
+
168
+ // =========================================================================
169
+ // Agent CRUD
170
+ // =========================================================================
171
+
172
+ /**
173
+ * POST /api/orchestrations/:id/agents - Add agent
174
+ */
175
+ handleAddAgent(req, res, orchestrationId) {
176
+ let body = '';
177
+ req.on('data', chunk => body += chunk);
178
+ req.on('end', () => {
179
+ try {
180
+ const agentConfig = JSON.parse(body);
181
+ const result = orchestrationData.addAgent(orchestrationId, agentConfig);
182
+
183
+ if (result.error) {
184
+ res.writeHead(400, { 'Content-Type': 'application/json' });
185
+ res.end(JSON.stringify({ ok: false, error: result.error }));
186
+ } else {
187
+ res.writeHead(201, { 'Content-Type': 'application/json' });
188
+ res.end(JSON.stringify({ ok: true, agent: result.agent }));
189
+ }
190
+ } catch (e) {
191
+ res.writeHead(400, { 'Content-Type': 'application/json' });
192
+ res.end(JSON.stringify({ ok: false, error: e.message }));
193
+ }
194
+ });
195
+ },
196
+
197
+ /**
198
+ * PATCH /api/orchestrations/:id/agents/:agentId - Update agent
199
+ */
200
+ handleUpdateAgent(req, res, orchestrationId, agentId) {
201
+ let body = '';
202
+ req.on('data', chunk => body += chunk);
203
+ req.on('end', () => {
204
+ try {
205
+ const updates = JSON.parse(body);
206
+ const result = orchestrationData.updateAgent(orchestrationId, agentId, updates);
207
+
208
+ if (result.error) {
209
+ res.writeHead(400, { 'Content-Type': 'application/json' });
210
+ res.end(JSON.stringify({ ok: false, error: result.error }));
211
+ } else {
212
+ res.writeHead(200, { 'Content-Type': 'application/json' });
213
+ res.end(JSON.stringify({ ok: true, agent: result.agent }));
214
+ }
215
+ } catch (e) {
216
+ res.writeHead(400, { 'Content-Type': 'application/json' });
217
+ res.end(JSON.stringify({ ok: false, error: e.message }));
218
+ }
219
+ });
220
+ },
221
+
222
+ /**
223
+ * DELETE /api/orchestrations/:id/agents/:agentId - Remove agent
224
+ */
225
+ handleRemoveAgent(req, res, orchestrationId, agentId) {
226
+ const result = orchestrationData.removeAgent(orchestrationId, agentId);
227
+ if (result.error) {
228
+ res.writeHead(400, { 'Content-Type': 'application/json' });
229
+ res.end(JSON.stringify({ ok: false, error: result.error }));
230
+ } else {
231
+ res.writeHead(200, { 'Content-Type': 'application/json' });
232
+ res.end(JSON.stringify({ ok: true }));
233
+ }
234
+ },
235
+
236
+ /**
237
+ * GET /api/orchestrations/:id/agents/:agentId/status - Get agent tmux status
238
+ */
239
+ handleGetAgentStatus(req, res, orchestrationId, agentId) {
240
+ orchestrationExecutor.getAgentTmuxStatus(agentId).then((status) => {
241
+ res.writeHead(200, { 'Content-Type': 'application/json' });
242
+ res.end(JSON.stringify({ ok: true, ...status }));
243
+ }).catch((e) => {
244
+ res.writeHead(500, { 'Content-Type': 'application/json' });
245
+ res.end(JSON.stringify({ ok: false, error: e.message }));
246
+ });
247
+ },
248
+
249
+ /**
250
+ * POST /api/orchestrations/:id/agents/:agentId/spawn - Manually spawn agent
251
+ */
252
+ handleSpawnAgent(req, res, orchestrationId, agentId) {
253
+ orchestrationExecutor.spawnAgent(orchestrationId, agentId).then((result) => {
254
+ if (result.error) {
255
+ res.writeHead(400, { 'Content-Type': 'application/json' });
256
+ res.end(JSON.stringify({ ok: false, error: result.error }));
257
+ } else {
258
+ res.writeHead(200, { 'Content-Type': 'application/json' });
259
+ res.end(JSON.stringify({ ok: true, ...result }));
260
+ }
261
+ }).catch((e) => {
262
+ res.writeHead(500, { 'Content-Type': 'application/json' });
263
+ res.end(JSON.stringify({ ok: false, error: e.message }));
264
+ });
265
+ },
266
+
267
+ /**
268
+ * POST /api/orchestrations/:id/agents/:agentId/kill - Kill agent
269
+ */
270
+ handleKillAgent(req, res, orchestrationId, agentId) {
271
+ orchestrationExecutor.killAgent(orchestrationId, agentId).then((result) => {
272
+ if (result.error) {
273
+ res.writeHead(400, { 'Content-Type': 'application/json' });
274
+ res.end(JSON.stringify({ ok: false, error: result.error }));
275
+ } else {
276
+ res.writeHead(200, { 'Content-Type': 'application/json' });
277
+ res.end(JSON.stringify({ ok: true }));
278
+ }
279
+ }).catch((e) => {
280
+ res.writeHead(500, { 'Content-Type': 'application/json' });
281
+ res.end(JSON.stringify({ ok: false, error: e.message }));
282
+ });
283
+ },
284
+
285
+ /**
286
+ * POST /api/orchestrations/:id/agents/:agentId/prompt - Send prompt to agent
287
+ */
288
+ handleSendAgentPrompt(req, res, orchestrationId, agentId) {
289
+ let body = '';
290
+ req.on('data', chunk => body += chunk);
291
+ req.on('end', async () => {
292
+ try {
293
+ const { prompt } = JSON.parse(body);
294
+ if (!prompt) {
295
+ res.writeHead(400, { 'Content-Type': 'application/json' });
296
+ res.end(JSON.stringify({ ok: false, error: 'Prompt is required' }));
297
+ return;
298
+ }
299
+
300
+ const result = await orchestrationExecutor.sendPromptToAgent(orchestrationId, agentId, prompt);
301
+ if (result.error) {
302
+ res.writeHead(400, { 'Content-Type': 'application/json' });
303
+ res.end(JSON.stringify({ ok: false, error: result.error }));
304
+ } else {
305
+ res.writeHead(200, { 'Content-Type': 'application/json' });
306
+ res.end(JSON.stringify({ ok: true }));
307
+ }
308
+ } catch (e) {
309
+ res.writeHead(400, { 'Content-Type': 'application/json' });
310
+ res.end(JSON.stringify({ ok: false, error: e.message }));
311
+ }
312
+ });
313
+ },
314
+
315
+ // =========================================================================
316
+ // Dependencies
317
+ // =========================================================================
318
+
319
+ /**
320
+ * POST /api/orchestrations/:id/agents/:agentId/dependencies - Add dependency
321
+ */
322
+ handleAddDependency(req, res, orchestrationId, agentId) {
323
+ let body = '';
324
+ req.on('data', chunk => body += chunk);
325
+ req.on('end', () => {
326
+ try {
327
+ const { dependsOnAgentId } = JSON.parse(body);
328
+ if (!dependsOnAgentId) {
329
+ res.writeHead(400, { 'Content-Type': 'application/json' });
330
+ res.end(JSON.stringify({ ok: false, error: 'dependsOnAgentId is required' }));
331
+ return;
332
+ }
333
+
334
+ const result = orchestrationData.addAgentDependency(orchestrationId, agentId, dependsOnAgentId);
335
+ if (result.error) {
336
+ res.writeHead(400, { 'Content-Type': 'application/json' });
337
+ res.end(JSON.stringify({ ok: false, error: result.error }));
338
+ } else {
339
+ res.writeHead(200, { 'Content-Type': 'application/json' });
340
+ res.end(JSON.stringify({ ok: true }));
341
+ }
342
+ } catch (e) {
343
+ res.writeHead(400, { 'Content-Type': 'application/json' });
344
+ res.end(JSON.stringify({ ok: false, error: e.message }));
345
+ }
346
+ });
347
+ },
348
+
349
+ /**
350
+ * DELETE /api/orchestrations/:id/agents/:agentId/dependencies/:depId - Remove dependency
351
+ */
352
+ handleRemoveDependency(req, res, orchestrationId, agentId, depId) {
353
+ const result = orchestrationData.removeAgentDependency(orchestrationId, agentId, depId);
354
+ if (result.error) {
355
+ res.writeHead(400, { 'Content-Type': 'application/json' });
356
+ res.end(JSON.stringify({ ok: false, error: result.error }));
357
+ } else {
358
+ res.writeHead(200, { 'Content-Type': 'application/json' });
359
+ res.end(JSON.stringify({ ok: true }));
360
+ }
361
+ },
362
+
363
+ // =========================================================================
364
+ // Templates
365
+ // =========================================================================
366
+
367
+ /**
368
+ * GET /api/orchestration-templates - List templates
369
+ */
370
+ handleListTemplates(req, res) {
371
+ const templates = orchestrationData.getTemplates();
372
+ res.writeHead(200, { 'Content-Type': 'application/json' });
373
+ res.end(JSON.stringify({ ok: true, templates }));
374
+ },
375
+
376
+ /**
377
+ * GET /api/orchestration-templates/:id - Get template details
378
+ */
379
+ handleGetTemplate(req, res, templateId) {
380
+ const template = orchestrationData.getTemplate(templateId);
381
+ if (template) {
382
+ res.writeHead(200, { 'Content-Type': 'application/json' });
383
+ res.end(JSON.stringify({ ok: true, template }));
384
+ } else {
385
+ res.writeHead(404, { 'Content-Type': 'application/json' });
386
+ res.end(JSON.stringify({ ok: false, error: 'Template not found' }));
387
+ }
388
+ },
389
+
390
+ /**
391
+ * POST /api/orchestration-templates/:id - Create from template
392
+ */
393
+ handleCreateFromTemplate(req, res, templateId) {
394
+ let body = '';
395
+ req.on('data', chunk => body += chunk);
396
+ req.on('end', () => {
397
+ try {
398
+ const options = body ? JSON.parse(body) : {};
399
+ const result = orchestrationData.createFromTemplate(templateId, options);
400
+
401
+ if (result.error) {
402
+ res.writeHead(400, { 'Content-Type': 'application/json' });
403
+ res.end(JSON.stringify({ ok: false, error: result.error }));
404
+ } else {
405
+ res.writeHead(201, { 'Content-Type': 'application/json' });
406
+ res.end(JSON.stringify({ ok: true, orchestration: result.orchestration }));
407
+ }
408
+ } catch (e) {
409
+ res.writeHead(400, { 'Content-Type': 'application/json' });
410
+ res.end(JSON.stringify({ ok: false, error: e.message }));
411
+ }
412
+ });
413
+ }
414
+ };
415
+ }
416
+
417
+ module.exports = { createOrchestrationRoutes };