@moxxy/cli 0.0.2 → 0.0.3
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/dist/chunk-6DZX6EAA.mjs +37 -0
- package/dist/chunk-C46NSEKG.mjs +211 -0
- package/dist/chunk-NGVL4Q5C.mjs +1102 -0
- package/dist/cli-7MK4YGOP.mjs +7 -0
- package/dist/dist-7LTHRYKA.mjs +11569 -0
- package/dist/dist-FAXRJMEN.mjs +6812 -0
- package/dist/dist-LKIOZQ42.mjs +17 -0
- package/dist/dist-UYA4RJUH.mjs +2792 -0
- package/dist/index.js +23868 -111
- package/dist/index.mjs +3 -2
- package/dist/src-D5HMDDVE.mjs +1324 -0
- package/package.json +11 -11
|
@@ -0,0 +1,1324 @@
|
|
|
1
|
+
import "./chunk-6DZX6EAA.mjs";
|
|
2
|
+
|
|
3
|
+
// ../claude/src/events/emitter.ts
|
|
4
|
+
var TypedEventEmitter = class {
|
|
5
|
+
handlers = /* @__PURE__ */ new Map();
|
|
6
|
+
/**
|
|
7
|
+
* Subscribe to an event with type-safe handler
|
|
8
|
+
*/
|
|
9
|
+
on(event, handler) {
|
|
10
|
+
if (!this.handlers.has(event)) {
|
|
11
|
+
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
12
|
+
}
|
|
13
|
+
this.handlers.get(event).add(handler);
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Subscribe to an event once
|
|
18
|
+
*/
|
|
19
|
+
once(event, handler) {
|
|
20
|
+
const onceHandler = ((...args) => {
|
|
21
|
+
this.off(event, onceHandler);
|
|
22
|
+
handler(...args);
|
|
23
|
+
});
|
|
24
|
+
return this.on(event, onceHandler);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Unsubscribe from an event
|
|
28
|
+
*/
|
|
29
|
+
off(event, handler) {
|
|
30
|
+
const eventHandlers = this.handlers.get(event);
|
|
31
|
+
if (eventHandlers) {
|
|
32
|
+
eventHandlers.delete(handler);
|
|
33
|
+
if (eventHandlers.size === 0) {
|
|
34
|
+
this.handlers.delete(event);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Emit an event with type-safe arguments
|
|
41
|
+
*/
|
|
42
|
+
emit(event, ...args) {
|
|
43
|
+
const eventHandlers = this.handlers.get(event);
|
|
44
|
+
if (!eventHandlers || eventHandlers.size === 0) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
eventHandlers.forEach((handler) => {
|
|
48
|
+
try {
|
|
49
|
+
handler(...args);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(`Error in event handler for '${event}':`, error);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Remove all handlers for an event or all events
|
|
58
|
+
*/
|
|
59
|
+
removeAllListeners(event) {
|
|
60
|
+
if (event) {
|
|
61
|
+
this.handlers.delete(event);
|
|
62
|
+
} else {
|
|
63
|
+
this.handlers.clear();
|
|
64
|
+
}
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the number of listeners for an event
|
|
69
|
+
*/
|
|
70
|
+
listenerCount(event) {
|
|
71
|
+
return this.handlers.get(event)?.size ?? 0;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get all registered event names
|
|
75
|
+
*/
|
|
76
|
+
eventNames() {
|
|
77
|
+
return Array.from(this.handlers.keys());
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Wait for an event to occur (promisified)
|
|
81
|
+
*/
|
|
82
|
+
waitFor(event, timeout) {
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
let timeoutId;
|
|
85
|
+
const handler = ((...args) => {
|
|
86
|
+
if (timeoutId) {
|
|
87
|
+
clearTimeout(timeoutId);
|
|
88
|
+
}
|
|
89
|
+
resolve(args);
|
|
90
|
+
});
|
|
91
|
+
this.once(event, handler);
|
|
92
|
+
if (timeout) {
|
|
93
|
+
timeoutId = setTimeout(() => {
|
|
94
|
+
this.off(event, handler);
|
|
95
|
+
reject(new Error(`Timeout waiting for event '${event}'`));
|
|
96
|
+
}, timeout);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
function createEventEmitter() {
|
|
102
|
+
return new TypedEventEmitter();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ../claude/src/modules/base.ts
|
|
106
|
+
var BaseModule = class {
|
|
107
|
+
constructor(transport, events) {
|
|
108
|
+
this.transport = transport;
|
|
109
|
+
this.events = events;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Make a request through the transport
|
|
113
|
+
*/
|
|
114
|
+
async request(method, params) {
|
|
115
|
+
return this.transport.request(method, params);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// ../claude/src/errors/errors.ts
|
|
120
|
+
var ClaudeError = class extends Error {
|
|
121
|
+
code;
|
|
122
|
+
details;
|
|
123
|
+
cause;
|
|
124
|
+
constructor(message, code, details, cause) {
|
|
125
|
+
super(message);
|
|
126
|
+
this.name = this.constructor.name;
|
|
127
|
+
this.code = code;
|
|
128
|
+
this.details = details;
|
|
129
|
+
this.cause = cause;
|
|
130
|
+
if (Error.captureStackTrace) {
|
|
131
|
+
Error.captureStackTrace(this, this.constructor);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
toJSON() {
|
|
135
|
+
return {
|
|
136
|
+
name: this.name,
|
|
137
|
+
message: this.message,
|
|
138
|
+
code: this.code,
|
|
139
|
+
details: this.details,
|
|
140
|
+
cause: this.cause ? {
|
|
141
|
+
name: this.cause.name,
|
|
142
|
+
message: this.cause.message
|
|
143
|
+
} : void 0
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var CLIError = class extends ClaudeError {
|
|
148
|
+
exitCode;
|
|
149
|
+
stdout;
|
|
150
|
+
stderr;
|
|
151
|
+
constructor(message, exitCode, stdout, stderr, cause) {
|
|
152
|
+
super(message, "CLI_ERROR", { exitCode, stdout, stderr }, cause);
|
|
153
|
+
this.exitCode = exitCode;
|
|
154
|
+
this.stdout = stdout;
|
|
155
|
+
this.stderr = stderr;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
var ProcessError = class extends ClaudeError {
|
|
159
|
+
processId;
|
|
160
|
+
constructor(message, processId, details, cause) {
|
|
161
|
+
super(
|
|
162
|
+
message,
|
|
163
|
+
"PROCESS_ERROR",
|
|
164
|
+
{ processId, ...details && typeof details === "object" ? details : {} },
|
|
165
|
+
cause
|
|
166
|
+
);
|
|
167
|
+
this.processId = processId;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var StreamParseError = class extends ClaudeError {
|
|
171
|
+
line;
|
|
172
|
+
constructor(message, line, cause) {
|
|
173
|
+
super(message, "STREAM_PARSE_ERROR", { line }, cause);
|
|
174
|
+
this.line = line;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var ConnectionError = class extends ClaudeError {
|
|
178
|
+
constructor(message, details, cause) {
|
|
179
|
+
super(message, "CONNECTION_ERROR", details, cause);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var TimeoutError = class extends ClaudeError {
|
|
183
|
+
timeout;
|
|
184
|
+
constructor(message, timeout) {
|
|
185
|
+
super(message, "TIMEOUT_ERROR", { timeout });
|
|
186
|
+
this.timeout = timeout;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
var ValidationError = class extends ClaudeError {
|
|
190
|
+
field;
|
|
191
|
+
constructor(message, field, details) {
|
|
192
|
+
super(
|
|
193
|
+
message,
|
|
194
|
+
"VALIDATION_ERROR",
|
|
195
|
+
{ field, ...details && typeof details === "object" ? details : {} }
|
|
196
|
+
);
|
|
197
|
+
this.field = field;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
var NotFoundError = class extends ClaudeError {
|
|
201
|
+
resource;
|
|
202
|
+
identifier;
|
|
203
|
+
constructor(resource, identifier) {
|
|
204
|
+
super(`${resource} not found: ${identifier}`, "NOT_FOUND", { resource, identifier });
|
|
205
|
+
this.resource = resource;
|
|
206
|
+
this.identifier = identifier;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
var NotConnectedError = class extends ClaudeError {
|
|
210
|
+
constructor(message = "SDK not connected") {
|
|
211
|
+
super(message, "NOT_CONNECTED");
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
var NotImplementedError = class extends ClaudeError {
|
|
215
|
+
constructor(feature) {
|
|
216
|
+
super(`Feature not implemented: ${feature}`, "NOT_IMPLEMENTED", { feature });
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// ../claude/src/modules/agents.ts
|
|
221
|
+
var AgentsModule = class extends BaseModule {
|
|
222
|
+
/**
|
|
223
|
+
* List agents
|
|
224
|
+
* Returns a single stub agent since Claude CLI doesn't have multiple agents
|
|
225
|
+
*/
|
|
226
|
+
async list() {
|
|
227
|
+
return this.request("agents.list");
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get agent by ID
|
|
231
|
+
* Returns stub agent data
|
|
232
|
+
*/
|
|
233
|
+
async get(agentId) {
|
|
234
|
+
return this.request("agents.get", { agentId });
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Create agent
|
|
238
|
+
* Not supported - Claude CLI agents are not managed this way
|
|
239
|
+
*/
|
|
240
|
+
async create() {
|
|
241
|
+
throw new NotImplementedError("Agent creation not supported with Claude CLI");
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Update agent
|
|
245
|
+
* Not supported - Claude CLI agents are not managed this way
|
|
246
|
+
*/
|
|
247
|
+
async update() {
|
|
248
|
+
throw new NotImplementedError("Agent updates not supported with Claude CLI");
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Delete agent
|
|
252
|
+
* Not supported - Claude CLI agents are not managed this way
|
|
253
|
+
*/
|
|
254
|
+
async delete() {
|
|
255
|
+
throw new NotImplementedError("Agent deletion not supported with Claude CLI");
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Set agent identity
|
|
259
|
+
* Not supported - Claude CLI manages identity
|
|
260
|
+
*/
|
|
261
|
+
async setIdentity() {
|
|
262
|
+
throw new NotImplementedError("Agent identity changes not supported with Claude CLI");
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Set agent model
|
|
266
|
+
* Not supported - Model is set per-session in Claude CLI
|
|
267
|
+
*/
|
|
268
|
+
async setModel() {
|
|
269
|
+
throw new NotImplementedError("Agent model changes not supported with Claude CLI. Use SDK config instead.");
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// ../claude/src/parsers/stream-parser.ts
|
|
274
|
+
import { createInterface } from "readline";
|
|
275
|
+
var ClaudeStreamParser = class {
|
|
276
|
+
sessionKey;
|
|
277
|
+
constructor(sessionKey) {
|
|
278
|
+
this.sessionKey = sessionKey;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Parse a single line of JSON stream output
|
|
282
|
+
*/
|
|
283
|
+
parseLine(line) {
|
|
284
|
+
if (!line.trim()) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
return JSON.parse(line);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Convert stream event to chat stream chunk
|
|
295
|
+
*/
|
|
296
|
+
toChunk(event) {
|
|
297
|
+
switch (event.type) {
|
|
298
|
+
case "content_block_delta": {
|
|
299
|
+
const text = event.delta?.text || "";
|
|
300
|
+
return {
|
|
301
|
+
content: text,
|
|
302
|
+
done: false,
|
|
303
|
+
sessionKey: this.sessionKey,
|
|
304
|
+
timestamp: Date.now()
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
case "thinking_block": {
|
|
308
|
+
return {
|
|
309
|
+
content: "",
|
|
310
|
+
thinking: event.content || "",
|
|
311
|
+
done: false,
|
|
312
|
+
sessionKey: this.sessionKey,
|
|
313
|
+
timestamp: Date.now()
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
case "message_stop": {
|
|
317
|
+
return {
|
|
318
|
+
content: "",
|
|
319
|
+
done: true,
|
|
320
|
+
sessionKey: this.sessionKey,
|
|
321
|
+
timestamp: Date.now()
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
case "content_block_start":
|
|
325
|
+
case "content_block_stop":
|
|
326
|
+
case "session_start":
|
|
327
|
+
case "message_start":
|
|
328
|
+
return null;
|
|
329
|
+
default:
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Parse stream asynchronously
|
|
335
|
+
*/
|
|
336
|
+
async *parseStream(stream) {
|
|
337
|
+
const reader = createInterface({
|
|
338
|
+
input: stream,
|
|
339
|
+
crlfDelay: Number.POSITIVE_INFINITY
|
|
340
|
+
});
|
|
341
|
+
try {
|
|
342
|
+
for await (const line of reader) {
|
|
343
|
+
const event = this.parseLine(line);
|
|
344
|
+
if (event) {
|
|
345
|
+
const chunk = this.toChunk(event);
|
|
346
|
+
if (chunk) {
|
|
347
|
+
yield chunk;
|
|
348
|
+
if (chunk.done) {
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (error instanceof Error) {
|
|
356
|
+
throw new StreamParseError("Error parsing stream", "", error);
|
|
357
|
+
}
|
|
358
|
+
throw error;
|
|
359
|
+
} finally {
|
|
360
|
+
reader.close();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Collect full response from stream
|
|
365
|
+
* Returns full text and optional thinking
|
|
366
|
+
*/
|
|
367
|
+
async collectFullResponse(stream) {
|
|
368
|
+
let fullContent = "";
|
|
369
|
+
let thinking;
|
|
370
|
+
for await (const chunk of this.parseStream(stream)) {
|
|
371
|
+
if (chunk.content) {
|
|
372
|
+
fullContent += chunk.content;
|
|
373
|
+
}
|
|
374
|
+
if (chunk.thinking) {
|
|
375
|
+
thinking = chunk.thinking;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return { content: fullContent, thinking };
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// ../claude/src/modules/chat.ts
|
|
383
|
+
var ChatModule = class extends BaseModule {
|
|
384
|
+
/**
|
|
385
|
+
* Send a message and wait for complete response
|
|
386
|
+
*/
|
|
387
|
+
async send(options) {
|
|
388
|
+
return this.request("chat.send", options);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Send a message with streaming response
|
|
392
|
+
*/
|
|
393
|
+
async *sendStream(options) {
|
|
394
|
+
const sessionKey = options.sessionKey || `claude-stream-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
395
|
+
const agentId = options.agentId || "claude";
|
|
396
|
+
const transport = this.transport;
|
|
397
|
+
const sessionManager = transport.getSessionManager();
|
|
398
|
+
const processManager = transport.getProcessManager();
|
|
399
|
+
const session = sessionManager.getOrCreateSession(sessionKey, agentId);
|
|
400
|
+
sessionManager.addMessage(sessionKey, {
|
|
401
|
+
role: "user",
|
|
402
|
+
content: options.message,
|
|
403
|
+
timestamp: Date.now()
|
|
404
|
+
});
|
|
405
|
+
this.events.emit("chat:stream:start", sessionKey, agentId);
|
|
406
|
+
options.onStart?.(sessionKey, agentId);
|
|
407
|
+
let proc = processManager.get(sessionKey);
|
|
408
|
+
if (!proc) {
|
|
409
|
+
proc = await processManager.spawn({
|
|
410
|
+
sessionKey,
|
|
411
|
+
model: transport.config.model,
|
|
412
|
+
thinking: options.thinking,
|
|
413
|
+
permissionMode: transport.config.permissionMode,
|
|
414
|
+
workingDirectory: transport.config.workingDirectory,
|
|
415
|
+
timeout: options.timeout
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
processManager.setStatus(sessionKey, "busy");
|
|
419
|
+
let fullResponse = "";
|
|
420
|
+
let thinking;
|
|
421
|
+
try {
|
|
422
|
+
proc.process.stdin?.write(options.message + "\n");
|
|
423
|
+
proc.process.stdin?.end();
|
|
424
|
+
const parser = new ClaudeStreamParser(sessionKey);
|
|
425
|
+
for await (const chunk of parser.parseStream(proc.process.stdout)) {
|
|
426
|
+
if (chunk.content) {
|
|
427
|
+
fullResponse += chunk.content;
|
|
428
|
+
}
|
|
429
|
+
if (chunk.thinking) {
|
|
430
|
+
thinking = chunk.thinking;
|
|
431
|
+
}
|
|
432
|
+
this.events.emit("chat:stream", chunk);
|
|
433
|
+
options.onChunk?.(chunk);
|
|
434
|
+
yield chunk;
|
|
435
|
+
if (chunk.done) {
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
sessionManager.addMessage(sessionKey, {
|
|
440
|
+
role: "assistant",
|
|
441
|
+
content: fullResponse,
|
|
442
|
+
thinking,
|
|
443
|
+
timestamp: Date.now()
|
|
444
|
+
});
|
|
445
|
+
processManager.setStatus(sessionKey, "idle");
|
|
446
|
+
processManager.touch(sessionKey);
|
|
447
|
+
this.events.emit("chat:stream:end", sessionKey, agentId);
|
|
448
|
+
options.onEnd?.(sessionKey, agentId);
|
|
449
|
+
return {
|
|
450
|
+
sessionKey,
|
|
451
|
+
agentId,
|
|
452
|
+
response: fullResponse,
|
|
453
|
+
thinking,
|
|
454
|
+
timestamp: Date.now()
|
|
455
|
+
};
|
|
456
|
+
} catch (error) {
|
|
457
|
+
processManager.setStatus(sessionKey, "error");
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Get chat history for a session
|
|
463
|
+
*/
|
|
464
|
+
async history(sessionKey, options) {
|
|
465
|
+
return this.request("sessions.history", { sessionKey, ...options });
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// ../claude/src/modules/sessions.ts
|
|
470
|
+
var SessionsModule = class extends BaseModule {
|
|
471
|
+
/**
|
|
472
|
+
* List sessions with optional filtering
|
|
473
|
+
*/
|
|
474
|
+
async list(options) {
|
|
475
|
+
return this.request("sessions.list", options);
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Get session details
|
|
479
|
+
*/
|
|
480
|
+
async get(sessionKey) {
|
|
481
|
+
return this.request("sessions.get", { sessionKey });
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Delete a session
|
|
485
|
+
*/
|
|
486
|
+
async delete(sessionKey) {
|
|
487
|
+
await this.request("sessions.delete", { sessionKey });
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Get session message history
|
|
491
|
+
*/
|
|
492
|
+
async history(sessionKey, options) {
|
|
493
|
+
return this.request("sessions.history", { sessionKey, ...options });
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Create a new session (convenience method)
|
|
497
|
+
* Sessions are created automatically on first message, but this can be used to pre-create
|
|
498
|
+
*/
|
|
499
|
+
async create(agentId = "claude") {
|
|
500
|
+
const sessionKey = `claude-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
501
|
+
return { sessionKey };
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// ../claude/src/modules/system.ts
|
|
506
|
+
var SystemModule = class extends BaseModule {
|
|
507
|
+
/**
|
|
508
|
+
* Get health status
|
|
509
|
+
*/
|
|
510
|
+
async health() {
|
|
511
|
+
return this.request("system.health");
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Get system status
|
|
515
|
+
*/
|
|
516
|
+
async status() {
|
|
517
|
+
return this.request("system.status");
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Ping (simple health check)
|
|
521
|
+
*/
|
|
522
|
+
async ping() {
|
|
523
|
+
try {
|
|
524
|
+
const health = await this.health();
|
|
525
|
+
return {
|
|
526
|
+
ok: health.healthy,
|
|
527
|
+
timestamp: Date.now()
|
|
528
|
+
};
|
|
529
|
+
} catch {
|
|
530
|
+
return {
|
|
531
|
+
ok: false,
|
|
532
|
+
timestamp: Date.now()
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// ../claude/src/transport/cli.ts
|
|
539
|
+
import { exec } from "child_process";
|
|
540
|
+
import { promisify } from "util";
|
|
541
|
+
|
|
542
|
+
// ../claude/src/transport/process-manager.ts
|
|
543
|
+
import { spawn } from "child_process";
|
|
544
|
+
var ProcessManager = class {
|
|
545
|
+
constructor(config, events) {
|
|
546
|
+
this.config = config;
|
|
547
|
+
this.events = events;
|
|
548
|
+
}
|
|
549
|
+
processes = /* @__PURE__ */ new Map();
|
|
550
|
+
cleanupInterval = null;
|
|
551
|
+
events;
|
|
552
|
+
/**
|
|
553
|
+
* Spawn a new Claude process
|
|
554
|
+
*/
|
|
555
|
+
async spawn(options) {
|
|
556
|
+
if (this.processes.size >= this.config.maxProcesses) {
|
|
557
|
+
throw new ProcessError(
|
|
558
|
+
`Maximum process limit reached (${this.config.maxProcesses})`,
|
|
559
|
+
options.sessionKey
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
const args = this.buildArgs(options);
|
|
563
|
+
const proc = spawn(this.config.cliPath, args, {
|
|
564
|
+
cwd: options.workingDirectory || this.config.workingDirectory || process.cwd(),
|
|
565
|
+
env: process.env,
|
|
566
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
567
|
+
});
|
|
568
|
+
const claudeProc = {
|
|
569
|
+
sessionKey: options.sessionKey,
|
|
570
|
+
process: proc,
|
|
571
|
+
status: "idle",
|
|
572
|
+
createdAt: Date.now(),
|
|
573
|
+
lastUsedAt: Date.now()
|
|
574
|
+
};
|
|
575
|
+
this.setupProcessHandlers(claudeProc);
|
|
576
|
+
this.processes.set(options.sessionKey, claudeProc);
|
|
577
|
+
this.events.emit("process:spawn", options.sessionKey, proc.pid ?? 0);
|
|
578
|
+
return claudeProc;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Build CLI arguments
|
|
582
|
+
*/
|
|
583
|
+
buildArgs(options) {
|
|
584
|
+
const args = [
|
|
585
|
+
"--print",
|
|
586
|
+
"--output-format=stream-json",
|
|
587
|
+
`--session-id=${options.sessionKey}`
|
|
588
|
+
];
|
|
589
|
+
const model = options.model || this.config.model;
|
|
590
|
+
if (model) {
|
|
591
|
+
args.push(`--model=${model}`);
|
|
592
|
+
}
|
|
593
|
+
if (options.thinking && options.thinking !== "none") {
|
|
594
|
+
args.push(`--thinking=${options.thinking}`);
|
|
595
|
+
}
|
|
596
|
+
const permissionMode = options.permissionMode || this.config.permissionMode;
|
|
597
|
+
if (permissionMode) {
|
|
598
|
+
args.push(`--permission-mode=${permissionMode}`);
|
|
599
|
+
}
|
|
600
|
+
const workingDir = options.workingDirectory || this.config.workingDirectory;
|
|
601
|
+
if (workingDir) {
|
|
602
|
+
args.push(`--add-dir=${workingDir}`);
|
|
603
|
+
}
|
|
604
|
+
return args;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Setup process event handlers
|
|
608
|
+
*/
|
|
609
|
+
setupProcessHandlers(proc) {
|
|
610
|
+
proc.process.on("error", (error) => {
|
|
611
|
+
proc.status = "error";
|
|
612
|
+
this.events.emit("process:error", proc.sessionKey, error);
|
|
613
|
+
});
|
|
614
|
+
proc.process.on("exit", (code) => {
|
|
615
|
+
this.processes.delete(proc.sessionKey);
|
|
616
|
+
this.events.emit("process:exit", proc.sessionKey, code);
|
|
617
|
+
});
|
|
618
|
+
proc.process.stderr?.on("data", (data) => {
|
|
619
|
+
const message = data.toString();
|
|
620
|
+
this.events.emit("agent:log", proc.sessionKey, message, "error");
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Get existing process by session key
|
|
625
|
+
*/
|
|
626
|
+
get(sessionKey) {
|
|
627
|
+
return this.processes.get(sessionKey);
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Update process last used time
|
|
631
|
+
*/
|
|
632
|
+
touch(sessionKey) {
|
|
633
|
+
const proc = this.processes.get(sessionKey);
|
|
634
|
+
if (proc) {
|
|
635
|
+
proc.lastUsedAt = Date.now();
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Set process status
|
|
640
|
+
*/
|
|
641
|
+
setStatus(sessionKey, status) {
|
|
642
|
+
const proc = this.processes.get(sessionKey);
|
|
643
|
+
if (proc) {
|
|
644
|
+
proc.status = status;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Kill a specific process
|
|
649
|
+
*/
|
|
650
|
+
kill(sessionKey) {
|
|
651
|
+
const proc = this.processes.get(sessionKey);
|
|
652
|
+
if (proc) {
|
|
653
|
+
try {
|
|
654
|
+
proc.process.kill("SIGTERM");
|
|
655
|
+
} catch (error) {
|
|
656
|
+
console.error(`Error killing process ${sessionKey}:`, error);
|
|
657
|
+
}
|
|
658
|
+
this.processes.delete(sessionKey);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Kill all processes
|
|
663
|
+
*/
|
|
664
|
+
killAll() {
|
|
665
|
+
for (const sessionKey of Array.from(this.processes.keys())) {
|
|
666
|
+
this.kill(sessionKey);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Start periodic cleanup of idle processes
|
|
671
|
+
*/
|
|
672
|
+
startCleanup(intervalMs = 6e4) {
|
|
673
|
+
if (this.cleanupInterval) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
this.cleanupInterval = setInterval(() => {
|
|
677
|
+
this.cleanupIdleProcesses();
|
|
678
|
+
}, intervalMs);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Stop cleanup interval
|
|
682
|
+
*/
|
|
683
|
+
stopCleanup() {
|
|
684
|
+
if (this.cleanupInterval) {
|
|
685
|
+
clearInterval(this.cleanupInterval);
|
|
686
|
+
this.cleanupInterval = null;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Cleanup idle processes
|
|
691
|
+
*/
|
|
692
|
+
cleanupIdleProcesses() {
|
|
693
|
+
const now = Date.now();
|
|
694
|
+
const timeout = this.config.processIdleTimeout;
|
|
695
|
+
for (const [sessionKey, proc] of this.processes.entries()) {
|
|
696
|
+
if (proc.status === "idle" && now - proc.lastUsedAt > timeout) {
|
|
697
|
+
this.kill(sessionKey);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Get process count
|
|
703
|
+
*/
|
|
704
|
+
getProcessCount() {
|
|
705
|
+
return this.processes.size;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Get all session keys with active processes
|
|
709
|
+
*/
|
|
710
|
+
getActiveSessionKeys() {
|
|
711
|
+
return Array.from(this.processes.keys());
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
// ../claude/src/session/storage.ts
|
|
716
|
+
var InMemorySessionStorage = class {
|
|
717
|
+
sessions = /* @__PURE__ */ new Map();
|
|
718
|
+
get(sessionKey) {
|
|
719
|
+
return this.sessions.get(sessionKey);
|
|
720
|
+
}
|
|
721
|
+
set(sessionKey, session) {
|
|
722
|
+
this.sessions.set(sessionKey, session);
|
|
723
|
+
}
|
|
724
|
+
delete(sessionKey) {
|
|
725
|
+
this.sessions.delete(sessionKey);
|
|
726
|
+
}
|
|
727
|
+
has(sessionKey) {
|
|
728
|
+
return this.sessions.has(sessionKey);
|
|
729
|
+
}
|
|
730
|
+
getAll() {
|
|
731
|
+
return Array.from(this.sessions.values());
|
|
732
|
+
}
|
|
733
|
+
clear() {
|
|
734
|
+
this.sessions.clear();
|
|
735
|
+
}
|
|
736
|
+
count() {
|
|
737
|
+
return this.sessions.size;
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
// ../claude/src/session/manager.ts
|
|
742
|
+
var SessionManager = class {
|
|
743
|
+
storage;
|
|
744
|
+
events;
|
|
745
|
+
constructor(events, storage) {
|
|
746
|
+
this.storage = storage || new InMemorySessionStorage();
|
|
747
|
+
this.events = events;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Get or create a session
|
|
751
|
+
*/
|
|
752
|
+
getOrCreateSession(sessionKey, agentId) {
|
|
753
|
+
let session = this.storage.get(sessionKey);
|
|
754
|
+
if (!session) {
|
|
755
|
+
session = {
|
|
756
|
+
sessionKey,
|
|
757
|
+
agentId,
|
|
758
|
+
messages: [],
|
|
759
|
+
createdAt: Date.now(),
|
|
760
|
+
updatedAt: Date.now()
|
|
761
|
+
};
|
|
762
|
+
this.storage.set(sessionKey, session);
|
|
763
|
+
this.events.emit("session:created", sessionKey);
|
|
764
|
+
}
|
|
765
|
+
return session;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Get session by key
|
|
769
|
+
*/
|
|
770
|
+
getSession(sessionKey) {
|
|
771
|
+
return this.storage.get(sessionKey);
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Add message to session
|
|
775
|
+
*/
|
|
776
|
+
addMessage(sessionKey, message) {
|
|
777
|
+
const session = this.storage.get(sessionKey);
|
|
778
|
+
if (!session) {
|
|
779
|
+
throw new NotFoundError("Session", sessionKey);
|
|
780
|
+
}
|
|
781
|
+
session.messages.push(message);
|
|
782
|
+
session.updatedAt = Date.now();
|
|
783
|
+
this.storage.set(sessionKey, session);
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Get session history
|
|
787
|
+
*/
|
|
788
|
+
getHistory(sessionKey, limit) {
|
|
789
|
+
const session = this.storage.get(sessionKey);
|
|
790
|
+
if (!session) {
|
|
791
|
+
throw new NotFoundError("Session", sessionKey);
|
|
792
|
+
}
|
|
793
|
+
if (limit && limit > 0) {
|
|
794
|
+
return session.messages.slice(-limit);
|
|
795
|
+
}
|
|
796
|
+
return session.messages;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* List sessions with optional filtering
|
|
800
|
+
*/
|
|
801
|
+
listSessions(options) {
|
|
802
|
+
let sessions = this.storage.getAll();
|
|
803
|
+
if (options?.agentId) {
|
|
804
|
+
sessions = sessions.filter((s) => s.agentId === options.agentId);
|
|
805
|
+
}
|
|
806
|
+
if (options?.activeWithinMinutes) {
|
|
807
|
+
const cutoff = Date.now() - options.activeWithinMinutes * 60 * 1e3;
|
|
808
|
+
sessions = sessions.filter((s) => s.updatedAt >= cutoff);
|
|
809
|
+
}
|
|
810
|
+
sessions.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
811
|
+
if (options?.offset) {
|
|
812
|
+
sessions = sessions.slice(options.offset);
|
|
813
|
+
}
|
|
814
|
+
if (options?.limit) {
|
|
815
|
+
sessions = sessions.slice(0, options.limit);
|
|
816
|
+
}
|
|
817
|
+
return sessions;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Delete session
|
|
821
|
+
*/
|
|
822
|
+
deleteSession(sessionKey) {
|
|
823
|
+
if (!this.storage.has(sessionKey)) {
|
|
824
|
+
throw new NotFoundError("Session", sessionKey);
|
|
825
|
+
}
|
|
826
|
+
this.storage.delete(sessionKey);
|
|
827
|
+
this.events.emit("session:deleted", sessionKey);
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Update session process ID
|
|
831
|
+
*/
|
|
832
|
+
setProcessId(sessionKey, processId) {
|
|
833
|
+
const session = this.storage.get(sessionKey);
|
|
834
|
+
if (!session) {
|
|
835
|
+
throw new NotFoundError("Session", sessionKey);
|
|
836
|
+
}
|
|
837
|
+
session.processId = processId;
|
|
838
|
+
session.updatedAt = Date.now();
|
|
839
|
+
this.storage.set(sessionKey, session);
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Clear process ID from session
|
|
843
|
+
*/
|
|
844
|
+
clearProcessId(sessionKey) {
|
|
845
|
+
const session = this.storage.get(sessionKey);
|
|
846
|
+
if (session) {
|
|
847
|
+
delete session.processId;
|
|
848
|
+
session.updatedAt = Date.now();
|
|
849
|
+
this.storage.set(sessionKey, session);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Get session count
|
|
854
|
+
*/
|
|
855
|
+
getSessionCount() {
|
|
856
|
+
return this.storage.count();
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Clear all sessions
|
|
860
|
+
*/
|
|
861
|
+
clearAll() {
|
|
862
|
+
this.storage.clear();
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
// ../claude/src/transport/cli.ts
|
|
867
|
+
var execAsync = promisify(exec);
|
|
868
|
+
var CLITransport = class {
|
|
869
|
+
state = "disconnected";
|
|
870
|
+
processManager;
|
|
871
|
+
sessionManager;
|
|
872
|
+
events;
|
|
873
|
+
config;
|
|
874
|
+
constructor(config, events) {
|
|
875
|
+
this.events = events;
|
|
876
|
+
this.config = {
|
|
877
|
+
...config,
|
|
878
|
+
autoConnect: config.autoConnect ?? true,
|
|
879
|
+
timeout: config.timeout ?? 3e5
|
|
880
|
+
// 5 minutes default
|
|
881
|
+
};
|
|
882
|
+
this.processManager = new ProcessManager(
|
|
883
|
+
{
|
|
884
|
+
cliPath: config.cliPath,
|
|
885
|
+
maxProcesses: config.maxProcesses,
|
|
886
|
+
processIdleTimeout: config.processIdleTimeout,
|
|
887
|
+
model: config.model,
|
|
888
|
+
permissionMode: config.permissionMode,
|
|
889
|
+
workingDirectory: config.workingDirectory
|
|
890
|
+
},
|
|
891
|
+
events
|
|
892
|
+
);
|
|
893
|
+
this.sessionManager = new SessionManager(events);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Connect - verify Claude CLI is available
|
|
897
|
+
*/
|
|
898
|
+
async connect() {
|
|
899
|
+
if (this.state === "connected") {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
this.state = "connecting";
|
|
903
|
+
this.events.emit("connection:state", "connecting");
|
|
904
|
+
try {
|
|
905
|
+
await this.verifyCLI();
|
|
906
|
+
this.processManager.startCleanup();
|
|
907
|
+
this.state = "connected";
|
|
908
|
+
this.events.emit("connection:state", "connected");
|
|
909
|
+
this.events.emit("connection:connected");
|
|
910
|
+
} catch (error) {
|
|
911
|
+
this.state = "error";
|
|
912
|
+
this.events.emit("connection:state", "error");
|
|
913
|
+
this.events.emit("connection:error", error);
|
|
914
|
+
throw new ConnectionError("Failed to connect to Claude CLI", void 0, error);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Verify Claude CLI is available
|
|
919
|
+
*/
|
|
920
|
+
async verifyCLI() {
|
|
921
|
+
try {
|
|
922
|
+
const { stdout } = await execAsync(`${this.config.cliPath} --version`);
|
|
923
|
+
console.log(`Claude CLI available: ${stdout.trim()}`);
|
|
924
|
+
} catch (error) {
|
|
925
|
+
throw new CLIError(
|
|
926
|
+
`Claude CLI not found at '${this.config.cliPath}'. Ensure Claude CLI is installed and in PATH.`,
|
|
927
|
+
error.code,
|
|
928
|
+
"",
|
|
929
|
+
error.message
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Disconnect and cleanup
|
|
935
|
+
*/
|
|
936
|
+
disconnect() {
|
|
937
|
+
if (this.state === "disconnected") {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
this.processManager.killAll();
|
|
941
|
+
this.processManager.stopCleanup();
|
|
942
|
+
this.state = "disconnected";
|
|
943
|
+
this.events.emit("connection:state", "disconnected");
|
|
944
|
+
this.events.emit("connection:disconnected");
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Check if connected
|
|
948
|
+
*/
|
|
949
|
+
isConnected() {
|
|
950
|
+
return this.state === "connected";
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Get connection state
|
|
954
|
+
*/
|
|
955
|
+
getConnectionState() {
|
|
956
|
+
return this.state;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Make an RPC request
|
|
960
|
+
*/
|
|
961
|
+
async request(method, params = {}) {
|
|
962
|
+
if (!this.isConnected()) {
|
|
963
|
+
throw new ConnectionError("Not connected to Claude CLI");
|
|
964
|
+
}
|
|
965
|
+
switch (method) {
|
|
966
|
+
case "chat.send":
|
|
967
|
+
return this.handleChatSend(params);
|
|
968
|
+
case "sessions.list":
|
|
969
|
+
return this.handleSessionsList(params);
|
|
970
|
+
case "sessions.get":
|
|
971
|
+
return this.handleSessionsGet(params.sessionKey);
|
|
972
|
+
case "sessions.delete":
|
|
973
|
+
return this.handleSessionsDelete(params.sessionKey);
|
|
974
|
+
case "sessions.history":
|
|
975
|
+
return this.handleSessionsHistory(params.sessionKey);
|
|
976
|
+
case "system.health":
|
|
977
|
+
return this.handleSystemHealth();
|
|
978
|
+
case "system.status":
|
|
979
|
+
return this.handleSystemStatus();
|
|
980
|
+
case "agents.list":
|
|
981
|
+
return this.handleAgentsList();
|
|
982
|
+
case "agents.get":
|
|
983
|
+
return this.handleAgentsGet(params.agentId);
|
|
984
|
+
default:
|
|
985
|
+
throw new Error(`Unknown RPC method: ${method}`);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Handle chat.send request
|
|
990
|
+
*/
|
|
991
|
+
async handleChatSend(options) {
|
|
992
|
+
const sessionKey = options.sessionKey || `claude-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
993
|
+
const agentId = options.agentId || "claude";
|
|
994
|
+
const timeout = options.timeout || this.config.timeout;
|
|
995
|
+
const session = this.sessionManager.getOrCreateSession(sessionKey, agentId);
|
|
996
|
+
this.sessionManager.addMessage(sessionKey, {
|
|
997
|
+
role: "user",
|
|
998
|
+
content: options.message,
|
|
999
|
+
timestamp: Date.now()
|
|
1000
|
+
});
|
|
1001
|
+
let proc = this.processManager.get(sessionKey);
|
|
1002
|
+
if (!proc) {
|
|
1003
|
+
proc = await this.processManager.spawn({
|
|
1004
|
+
sessionKey,
|
|
1005
|
+
model: this.config.model,
|
|
1006
|
+
thinking: options.thinking,
|
|
1007
|
+
permissionMode: this.config.permissionMode,
|
|
1008
|
+
workingDirectory: this.config.workingDirectory,
|
|
1009
|
+
timeout
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
this.processManager.setStatus(sessionKey, "busy");
|
|
1013
|
+
try {
|
|
1014
|
+
proc.process.stdin?.write(options.message + "\n");
|
|
1015
|
+
proc.process.stdin?.end();
|
|
1016
|
+
const parser = new ClaudeStreamParser(sessionKey);
|
|
1017
|
+
const { content, thinking } = await Promise.race([
|
|
1018
|
+
parser.collectFullResponse(proc.process.stdout),
|
|
1019
|
+
this.createTimeoutPromise(timeout)
|
|
1020
|
+
]);
|
|
1021
|
+
this.sessionManager.addMessage(sessionKey, {
|
|
1022
|
+
role: "assistant",
|
|
1023
|
+
content,
|
|
1024
|
+
thinking,
|
|
1025
|
+
timestamp: Date.now()
|
|
1026
|
+
});
|
|
1027
|
+
this.processManager.setStatus(sessionKey, "idle");
|
|
1028
|
+
this.processManager.touch(sessionKey);
|
|
1029
|
+
return {
|
|
1030
|
+
sessionKey,
|
|
1031
|
+
agentId,
|
|
1032
|
+
response: content,
|
|
1033
|
+
thinking,
|
|
1034
|
+
timestamp: Date.now()
|
|
1035
|
+
};
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
this.processManager.setStatus(sessionKey, "error");
|
|
1038
|
+
throw error;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Handle sessions.list request
|
|
1043
|
+
*/
|
|
1044
|
+
async handleSessionsList(options) {
|
|
1045
|
+
const sessions = this.sessionManager.listSessions(options);
|
|
1046
|
+
return {
|
|
1047
|
+
sessions: sessions.map((s) => ({
|
|
1048
|
+
sessionKey: s.sessionKey,
|
|
1049
|
+
agentId: s.agentId,
|
|
1050
|
+
messageCount: s.messages.length,
|
|
1051
|
+
createdAt: s.createdAt,
|
|
1052
|
+
updatedAt: s.updatedAt
|
|
1053
|
+
})),
|
|
1054
|
+
total: sessions.length
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Handle sessions.get request
|
|
1059
|
+
*/
|
|
1060
|
+
async handleSessionsGet(sessionKey) {
|
|
1061
|
+
const session = this.sessionManager.getSession(sessionKey);
|
|
1062
|
+
if (!session) {
|
|
1063
|
+
throw new NotFoundError("Session", sessionKey);
|
|
1064
|
+
}
|
|
1065
|
+
return {
|
|
1066
|
+
sessionKey: session.sessionKey,
|
|
1067
|
+
agentId: session.agentId,
|
|
1068
|
+
messageCount: session.messages.length,
|
|
1069
|
+
createdAt: session.createdAt,
|
|
1070
|
+
updatedAt: session.updatedAt
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Handle sessions.delete request
|
|
1075
|
+
*/
|
|
1076
|
+
async handleSessionsDelete(sessionKey) {
|
|
1077
|
+
this.processManager.kill(sessionKey);
|
|
1078
|
+
this.sessionManager.deleteSession(sessionKey);
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Handle sessions.history request
|
|
1082
|
+
*/
|
|
1083
|
+
async handleSessionsHistory(sessionKey) {
|
|
1084
|
+
const messages = this.sessionManager.getHistory(sessionKey);
|
|
1085
|
+
return { messages };
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Handle system.health request
|
|
1089
|
+
*/
|
|
1090
|
+
async handleSystemHealth() {
|
|
1091
|
+
try {
|
|
1092
|
+
await this.verifyCLI();
|
|
1093
|
+
return {
|
|
1094
|
+
healthy: true,
|
|
1095
|
+
cliAvailable: true,
|
|
1096
|
+
activeSessions: this.sessionManager.getSessionCount(),
|
|
1097
|
+
activeProcesses: this.processManager.getProcessCount()
|
|
1098
|
+
};
|
|
1099
|
+
} catch {
|
|
1100
|
+
return {
|
|
1101
|
+
healthy: false,
|
|
1102
|
+
cliAvailable: false,
|
|
1103
|
+
activeSessions: this.sessionManager.getSessionCount(),
|
|
1104
|
+
activeProcesses: this.processManager.getProcessCount()
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Handle system.status request
|
|
1110
|
+
*/
|
|
1111
|
+
async handleSystemStatus() {
|
|
1112
|
+
return {
|
|
1113
|
+
state: this.state,
|
|
1114
|
+
activeSessions: this.sessionManager.getSessionCount(),
|
|
1115
|
+
activeProcesses: this.processManager.getProcessCount(),
|
|
1116
|
+
cliPath: this.config.cliPath
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Handle agents.list request (stub)
|
|
1121
|
+
*/
|
|
1122
|
+
async handleAgentsList() {
|
|
1123
|
+
return {
|
|
1124
|
+
agents: [
|
|
1125
|
+
{
|
|
1126
|
+
agentId: "claude",
|
|
1127
|
+
status: "available",
|
|
1128
|
+
workspace: this.config.workingDirectory
|
|
1129
|
+
}
|
|
1130
|
+
],
|
|
1131
|
+
defaultAgentId: "claude"
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Handle agents.get request (stub)
|
|
1136
|
+
*/
|
|
1137
|
+
async handleAgentsGet(agentId) {
|
|
1138
|
+
return {
|
|
1139
|
+
agentId,
|
|
1140
|
+
status: "available",
|
|
1141
|
+
workspace: this.config.workingDirectory
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Create timeout promise
|
|
1146
|
+
*/
|
|
1147
|
+
createTimeoutPromise(timeout) {
|
|
1148
|
+
return new Promise((_, reject) => {
|
|
1149
|
+
setTimeout(() => {
|
|
1150
|
+
reject(new TimeoutError("Operation timed out", timeout));
|
|
1151
|
+
}, timeout);
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Subscribe to event
|
|
1156
|
+
*/
|
|
1157
|
+
on(event, handler) {
|
|
1158
|
+
this.events.on(event, handler);
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Unsubscribe from event
|
|
1162
|
+
*/
|
|
1163
|
+
off(event, handler) {
|
|
1164
|
+
this.events.off(event, handler);
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Emit event
|
|
1168
|
+
*/
|
|
1169
|
+
emit(event, ...args) {
|
|
1170
|
+
this.events.emit(event, ...args);
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Get process manager (for internal use)
|
|
1174
|
+
*/
|
|
1175
|
+
getProcessManager() {
|
|
1176
|
+
return this.processManager;
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Get session manager (for internal use)
|
|
1180
|
+
*/
|
|
1181
|
+
getSessionManager() {
|
|
1182
|
+
return this.sessionManager;
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
// ../claude/src/sdk.ts
|
|
1187
|
+
var DEFAULT_CONFIG = {
|
|
1188
|
+
cliPath: "claude",
|
|
1189
|
+
timeout: 3e5,
|
|
1190
|
+
// 5 minutes
|
|
1191
|
+
maxProcesses: 10,
|
|
1192
|
+
processIdleTimeout: 6e5,
|
|
1193
|
+
// 10 minutes
|
|
1194
|
+
model: "claude-sonnet-4-5",
|
|
1195
|
+
permissionMode: "default",
|
|
1196
|
+
workingDirectory: process.cwd(),
|
|
1197
|
+
autoConnect: true
|
|
1198
|
+
};
|
|
1199
|
+
var ClaudeSDK = class _ClaudeSDK {
|
|
1200
|
+
transport;
|
|
1201
|
+
events;
|
|
1202
|
+
// Domain modules
|
|
1203
|
+
system;
|
|
1204
|
+
agents;
|
|
1205
|
+
sessions;
|
|
1206
|
+
chat;
|
|
1207
|
+
constructor(config) {
|
|
1208
|
+
this.events = createEventEmitter();
|
|
1209
|
+
const transportConfig = {
|
|
1210
|
+
cliPath: config.cliPath,
|
|
1211
|
+
timeout: config.timeout,
|
|
1212
|
+
maxProcesses: config.maxProcesses,
|
|
1213
|
+
processIdleTimeout: config.processIdleTimeout,
|
|
1214
|
+
model: config.model,
|
|
1215
|
+
permissionMode: config.permissionMode,
|
|
1216
|
+
workingDirectory: config.workingDirectory,
|
|
1217
|
+
autoConnect: false
|
|
1218
|
+
// We'll connect manually after initialization
|
|
1219
|
+
};
|
|
1220
|
+
this.transport = new CLITransport(transportConfig, this.events);
|
|
1221
|
+
this.system = new SystemModule(this.transport, this.events);
|
|
1222
|
+
this.agents = new AgentsModule(this.transport, this.events);
|
|
1223
|
+
this.sessions = new SessionsModule(this.transport, this.events);
|
|
1224
|
+
this.chat = new ChatModule(this.transport, this.events);
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Create a new ClaudeSDK instance
|
|
1228
|
+
* Auto-connects by default
|
|
1229
|
+
*/
|
|
1230
|
+
static async create(config) {
|
|
1231
|
+
const fullConfig = {
|
|
1232
|
+
...DEFAULT_CONFIG,
|
|
1233
|
+
...config
|
|
1234
|
+
};
|
|
1235
|
+
const sdk = new _ClaudeSDK(fullConfig);
|
|
1236
|
+
if (fullConfig.autoConnect) {
|
|
1237
|
+
await sdk.connect();
|
|
1238
|
+
}
|
|
1239
|
+
return sdk;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Create a disconnected SDK instance
|
|
1243
|
+
* Must call connect() manually
|
|
1244
|
+
*/
|
|
1245
|
+
static createDisconnected(config) {
|
|
1246
|
+
const fullConfig = {
|
|
1247
|
+
...DEFAULT_CONFIG,
|
|
1248
|
+
...config,
|
|
1249
|
+
autoConnect: false
|
|
1250
|
+
};
|
|
1251
|
+
return new _ClaudeSDK(fullConfig);
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Connect to Claude CLI
|
|
1255
|
+
*/
|
|
1256
|
+
async connect() {
|
|
1257
|
+
await this.transport.connect();
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Disconnect and cleanup
|
|
1261
|
+
*/
|
|
1262
|
+
disconnect() {
|
|
1263
|
+
this.transport.disconnect();
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Check if connected
|
|
1267
|
+
*/
|
|
1268
|
+
isConnected() {
|
|
1269
|
+
return this.transport.isConnected();
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Get connection state
|
|
1273
|
+
*/
|
|
1274
|
+
getConnectionState() {
|
|
1275
|
+
return this.transport.getConnectionState();
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Subscribe to an event
|
|
1279
|
+
*/
|
|
1280
|
+
on(event, handler) {
|
|
1281
|
+
this.events.on(event, handler);
|
|
1282
|
+
return this;
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Subscribe to an event once
|
|
1286
|
+
*/
|
|
1287
|
+
once(event, handler) {
|
|
1288
|
+
this.events.once(event, handler);
|
|
1289
|
+
return this;
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Unsubscribe from an event
|
|
1293
|
+
*/
|
|
1294
|
+
off(event, handler) {
|
|
1295
|
+
this.events.off(event, handler);
|
|
1296
|
+
return this;
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Remove all listeners for an event
|
|
1300
|
+
*/
|
|
1301
|
+
removeAllListeners(event) {
|
|
1302
|
+
this.events.removeAllListeners(event);
|
|
1303
|
+
return this;
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Wait for an event (promisified)
|
|
1307
|
+
*/
|
|
1308
|
+
waitFor(event, timeout) {
|
|
1309
|
+
return this.events.waitFor(event, timeout);
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1312
|
+
export {
|
|
1313
|
+
CLIError,
|
|
1314
|
+
ClaudeError,
|
|
1315
|
+
ClaudeSDK,
|
|
1316
|
+
ConnectionError,
|
|
1317
|
+
NotConnectedError,
|
|
1318
|
+
NotFoundError,
|
|
1319
|
+
NotImplementedError,
|
|
1320
|
+
ProcessError,
|
|
1321
|
+
StreamParseError,
|
|
1322
|
+
TimeoutError,
|
|
1323
|
+
ValidationError
|
|
1324
|
+
};
|