@moxxy/cli 0.0.7 → 0.0.9

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,1386 @@
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 {
290
+ return null;
291
+ }
292
+ }
293
+ /**
294
+ * Convert stream event to chat stream chunk
295
+ * Handles both CLI format and API format events
296
+ */
297
+ toChunk(event) {
298
+ switch (event.type) {
299
+ // --- Claude CLI format events ---
300
+ case "assistant": {
301
+ const message = event.message;
302
+ if (!message?.content) return null;
303
+ const textBlocks = message.content.filter((b) => b.type === "text");
304
+ const text = textBlocks.map((b) => b.text || "").join("");
305
+ const thinkingBlocks = message.content.filter((b) => b.type === "thinking");
306
+ const thinking = thinkingBlocks.length > 0 ? thinkingBlocks.map((b) => b.thinking || b.text || "").join("") : void 0;
307
+ if (!text && !thinking) return null;
308
+ return {
309
+ content: text,
310
+ thinking,
311
+ done: false,
312
+ sessionKey: this.sessionKey,
313
+ timestamp: Date.now()
314
+ };
315
+ }
316
+ case "result": {
317
+ const resultText = event.result || "";
318
+ return {
319
+ content: resultText,
320
+ done: true,
321
+ sessionKey: this.sessionKey,
322
+ timestamp: Date.now()
323
+ };
324
+ }
325
+ case "system":
326
+ return null;
327
+ // --- Anthropic API format events (legacy/fallback) ---
328
+ case "content_block_delta": {
329
+ const text = event.delta?.text || "";
330
+ return {
331
+ content: text,
332
+ done: false,
333
+ sessionKey: this.sessionKey,
334
+ timestamp: Date.now()
335
+ };
336
+ }
337
+ case "thinking_block": {
338
+ return {
339
+ content: "",
340
+ thinking: event.content || "",
341
+ done: false,
342
+ sessionKey: this.sessionKey,
343
+ timestamp: Date.now()
344
+ };
345
+ }
346
+ case "message_stop": {
347
+ return {
348
+ content: "",
349
+ done: true,
350
+ sessionKey: this.sessionKey,
351
+ timestamp: Date.now()
352
+ };
353
+ }
354
+ case "content_block_start":
355
+ case "content_block_stop":
356
+ case "session_start":
357
+ case "message_start":
358
+ return null;
359
+ default:
360
+ return null;
361
+ }
362
+ }
363
+ /**
364
+ * Parse stream asynchronously
365
+ */
366
+ async *parseStream(stream) {
367
+ const reader = createInterface({
368
+ input: stream,
369
+ crlfDelay: Number.POSITIVE_INFINITY
370
+ });
371
+ try {
372
+ for await (const line of reader) {
373
+ const event = this.parseLine(line);
374
+ if (event) {
375
+ const chunk = this.toChunk(event);
376
+ if (chunk) {
377
+ yield chunk;
378
+ if (chunk.done) {
379
+ break;
380
+ }
381
+ }
382
+ }
383
+ }
384
+ } catch (error) {
385
+ if (error instanceof Error) {
386
+ throw new StreamParseError("Error parsing stream", "", error);
387
+ }
388
+ throw error;
389
+ } finally {
390
+ reader.close();
391
+ }
392
+ }
393
+ /**
394
+ * Collect full response from stream
395
+ * Returns full text and optional thinking
396
+ */
397
+ async collectFullResponse(stream) {
398
+ let fullContent = "";
399
+ let thinking;
400
+ for await (const chunk of this.parseStream(stream)) {
401
+ if (chunk.content) {
402
+ fullContent += chunk.content;
403
+ }
404
+ if (chunk.thinking) {
405
+ thinking = chunk.thinking;
406
+ }
407
+ }
408
+ return { content: fullContent, thinking };
409
+ }
410
+ };
411
+
412
+ // ../claude/src/modules/chat.ts
413
+ var ChatModule = class extends BaseModule {
414
+ /**
415
+ * Send a message and wait for complete response
416
+ */
417
+ async send(options) {
418
+ return this.request("chat.send", options);
419
+ }
420
+ /**
421
+ * Send a message with streaming response
422
+ */
423
+ async *sendStream(options) {
424
+ const sessionKey = options.sessionKey || `claude-stream-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
425
+ const agentId = options.agentId || "claude";
426
+ const transport = this.transport;
427
+ const sessionManager = transport.getSessionManager();
428
+ const processManager = transport.getProcessManager();
429
+ const session = sessionManager.getOrCreateSession(sessionKey, agentId);
430
+ sessionManager.addMessage(sessionKey, {
431
+ role: "user",
432
+ content: options.message,
433
+ timestamp: Date.now()
434
+ });
435
+ this.events.emit("chat:stream:start", sessionKey, agentId);
436
+ options.onStart?.(sessionKey, agentId);
437
+ const existingProc = processManager.get(sessionKey);
438
+ if (existingProc) {
439
+ processManager.kill(sessionKey);
440
+ }
441
+ const proc = await processManager.spawn({
442
+ sessionKey,
443
+ model: transport.config.model,
444
+ thinking: options.thinking,
445
+ permissionMode: transport.config.permissionMode,
446
+ workingDirectory: transport.config.workingDirectory,
447
+ timeout: options.timeout,
448
+ message: options.message,
449
+ outputFormat: "stream-json"
450
+ });
451
+ processManager.setStatus(sessionKey, "busy");
452
+ let fullResponse = "";
453
+ let thinking;
454
+ try {
455
+ const parser = new ClaudeStreamParser(sessionKey);
456
+ for await (const chunk of parser.parseStream(proc.process.stdout)) {
457
+ if (chunk.content) {
458
+ fullResponse += chunk.content;
459
+ }
460
+ if (chunk.thinking) {
461
+ thinking = chunk.thinking;
462
+ }
463
+ this.events.emit("chat:stream", chunk);
464
+ options.onChunk?.(chunk);
465
+ yield chunk;
466
+ if (chunk.done) {
467
+ break;
468
+ }
469
+ }
470
+ sessionManager.addMessage(sessionKey, {
471
+ role: "assistant",
472
+ content: fullResponse,
473
+ thinking,
474
+ timestamp: Date.now()
475
+ });
476
+ processManager.setStatus(sessionKey, "idle");
477
+ processManager.touch(sessionKey);
478
+ this.events.emit("chat:stream:end", sessionKey, agentId);
479
+ options.onEnd?.(sessionKey, agentId);
480
+ return {
481
+ sessionKey,
482
+ agentId,
483
+ response: fullResponse,
484
+ thinking,
485
+ timestamp: Date.now()
486
+ };
487
+ } catch (error) {
488
+ processManager.setStatus(sessionKey, "error");
489
+ throw error;
490
+ }
491
+ }
492
+ /**
493
+ * Get chat history for a session
494
+ */
495
+ async history(sessionKey, options) {
496
+ return this.request("sessions.history", { sessionKey, ...options });
497
+ }
498
+ };
499
+
500
+ // ../claude/src/modules/sessions.ts
501
+ var SessionsModule = class extends BaseModule {
502
+ /**
503
+ * List sessions with optional filtering
504
+ */
505
+ async list(options) {
506
+ return this.request("sessions.list", options);
507
+ }
508
+ /**
509
+ * Get session details
510
+ */
511
+ async get(sessionKey) {
512
+ return this.request("sessions.get", { sessionKey });
513
+ }
514
+ /**
515
+ * Delete a session
516
+ */
517
+ async delete(sessionKey) {
518
+ await this.request("sessions.delete", { sessionKey });
519
+ }
520
+ /**
521
+ * Get session message history
522
+ */
523
+ async history(sessionKey, options) {
524
+ return this.request("sessions.history", { sessionKey, ...options });
525
+ }
526
+ /**
527
+ * Create a new session (convenience method)
528
+ * Sessions are created automatically on first message, but this can be used to pre-create
529
+ */
530
+ async create(agentId = "claude") {
531
+ const sessionKey = `claude-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
532
+ return { sessionKey };
533
+ }
534
+ };
535
+
536
+ // ../claude/src/modules/system.ts
537
+ var SystemModule = class extends BaseModule {
538
+ /**
539
+ * Get health status
540
+ */
541
+ async health() {
542
+ return this.request("system.health");
543
+ }
544
+ /**
545
+ * Get system status
546
+ */
547
+ async status() {
548
+ return this.request("system.status");
549
+ }
550
+ /**
551
+ * Ping (simple health check)
552
+ */
553
+ async ping() {
554
+ try {
555
+ const health = await this.health();
556
+ return {
557
+ ok: health.healthy,
558
+ timestamp: Date.now()
559
+ };
560
+ } catch {
561
+ return {
562
+ ok: false,
563
+ timestamp: Date.now()
564
+ };
565
+ }
566
+ }
567
+ };
568
+
569
+ // ../claude/src/transport/cli.ts
570
+ import { exec } from "child_process";
571
+ import { promisify } from "util";
572
+
573
+ // ../claude/src/transport/process-manager.ts
574
+ import { spawn } from "child_process";
575
+ var ProcessManager = class {
576
+ constructor(config, events) {
577
+ this.config = config;
578
+ this.events = events;
579
+ }
580
+ processes = /* @__PURE__ */ new Map();
581
+ cleanupInterval = null;
582
+ events;
583
+ /**
584
+ * Spawn a new Claude process
585
+ */
586
+ async spawn(options) {
587
+ if (this.processes.size >= this.config.maxProcesses) {
588
+ throw new ProcessError(
589
+ `Maximum process limit reached (${this.config.maxProcesses})`,
590
+ options.sessionKey
591
+ );
592
+ }
593
+ const args = this.buildArgs(options);
594
+ const proc = spawn(this.config.cliPath, args, {
595
+ cwd: options.workingDirectory || this.config.workingDirectory || process.cwd(),
596
+ env: process.env,
597
+ stdio: ["pipe", "pipe", "pipe"]
598
+ });
599
+ const claudeProc = {
600
+ sessionKey: options.sessionKey,
601
+ process: proc,
602
+ status: "idle",
603
+ createdAt: Date.now(),
604
+ lastUsedAt: Date.now()
605
+ };
606
+ this.setupProcessHandlers(claudeProc);
607
+ this.processes.set(options.sessionKey, claudeProc);
608
+ this.events.emit("process:spawn", options.sessionKey, proc.pid ?? 0);
609
+ return claudeProc;
610
+ }
611
+ /**
612
+ * Build CLI arguments
613
+ */
614
+ buildArgs(options) {
615
+ const outputFormat = options.outputFormat || "json";
616
+ const args = [
617
+ "--print",
618
+ "--output-format",
619
+ outputFormat
620
+ ];
621
+ if (outputFormat === "stream-json") {
622
+ args.push("--verbose");
623
+ }
624
+ const model = options.model || this.config.model;
625
+ if (model) {
626
+ args.push("--model", model);
627
+ }
628
+ if (options.thinking && options.thinking !== "none") {
629
+ args.push("--thinking", options.thinking);
630
+ }
631
+ const permissionMode = options.permissionMode || this.config.permissionMode;
632
+ if (permissionMode) {
633
+ args.push("--permission-mode", permissionMode);
634
+ }
635
+ const workingDir = options.workingDirectory || this.config.workingDirectory;
636
+ if (workingDir) {
637
+ args.push("--add-dir", workingDir);
638
+ }
639
+ if (options.message) {
640
+ args.push(options.message);
641
+ }
642
+ return args;
643
+ }
644
+ /**
645
+ * Setup process event handlers
646
+ */
647
+ setupProcessHandlers(proc) {
648
+ proc.process.on("error", (error) => {
649
+ proc.status = "error";
650
+ this.events.emit("process:error", proc.sessionKey, error);
651
+ });
652
+ proc.process.on("exit", (code) => {
653
+ this.processes.delete(proc.sessionKey);
654
+ this.events.emit("process:exit", proc.sessionKey, code);
655
+ });
656
+ proc.process.stderr?.on("data", (data) => {
657
+ const message = data.toString();
658
+ this.events.emit("agent:log", proc.sessionKey, message, "error");
659
+ });
660
+ }
661
+ /**
662
+ * Get existing process by session key
663
+ */
664
+ get(sessionKey) {
665
+ return this.processes.get(sessionKey);
666
+ }
667
+ /**
668
+ * Update process last used time
669
+ */
670
+ touch(sessionKey) {
671
+ const proc = this.processes.get(sessionKey);
672
+ if (proc) {
673
+ proc.lastUsedAt = Date.now();
674
+ }
675
+ }
676
+ /**
677
+ * Set process status
678
+ */
679
+ setStatus(sessionKey, status) {
680
+ const proc = this.processes.get(sessionKey);
681
+ if (proc) {
682
+ proc.status = status;
683
+ }
684
+ }
685
+ /**
686
+ * Kill a specific process
687
+ */
688
+ kill(sessionKey) {
689
+ const proc = this.processes.get(sessionKey);
690
+ if (proc) {
691
+ try {
692
+ proc.process.kill("SIGTERM");
693
+ } catch (error) {
694
+ console.error(`Error killing process ${sessionKey}:`, error);
695
+ }
696
+ this.processes.delete(sessionKey);
697
+ }
698
+ }
699
+ /**
700
+ * Kill all processes
701
+ */
702
+ killAll() {
703
+ for (const sessionKey of Array.from(this.processes.keys())) {
704
+ this.kill(sessionKey);
705
+ }
706
+ }
707
+ /**
708
+ * Start periodic cleanup of idle processes
709
+ */
710
+ startCleanup(intervalMs = 6e4) {
711
+ if (this.cleanupInterval) {
712
+ return;
713
+ }
714
+ this.cleanupInterval = setInterval(() => {
715
+ this.cleanupIdleProcesses();
716
+ }, intervalMs);
717
+ }
718
+ /**
719
+ * Stop cleanup interval
720
+ */
721
+ stopCleanup() {
722
+ if (this.cleanupInterval) {
723
+ clearInterval(this.cleanupInterval);
724
+ this.cleanupInterval = null;
725
+ }
726
+ }
727
+ /**
728
+ * Cleanup idle processes
729
+ */
730
+ cleanupIdleProcesses() {
731
+ const now = Date.now();
732
+ const timeout = this.config.processIdleTimeout;
733
+ for (const [sessionKey, proc] of this.processes.entries()) {
734
+ if (proc.status === "idle" && now - proc.lastUsedAt > timeout) {
735
+ this.kill(sessionKey);
736
+ }
737
+ }
738
+ }
739
+ /**
740
+ * Get process count
741
+ */
742
+ getProcessCount() {
743
+ return this.processes.size;
744
+ }
745
+ /**
746
+ * Get all session keys with active processes
747
+ */
748
+ getActiveSessionKeys() {
749
+ return Array.from(this.processes.keys());
750
+ }
751
+ };
752
+
753
+ // ../claude/src/session/storage.ts
754
+ var InMemorySessionStorage = class {
755
+ sessions = /* @__PURE__ */ new Map();
756
+ get(sessionKey) {
757
+ return this.sessions.get(sessionKey);
758
+ }
759
+ set(sessionKey, session) {
760
+ this.sessions.set(sessionKey, session);
761
+ }
762
+ delete(sessionKey) {
763
+ this.sessions.delete(sessionKey);
764
+ }
765
+ has(sessionKey) {
766
+ return this.sessions.has(sessionKey);
767
+ }
768
+ getAll() {
769
+ return Array.from(this.sessions.values());
770
+ }
771
+ clear() {
772
+ this.sessions.clear();
773
+ }
774
+ count() {
775
+ return this.sessions.size;
776
+ }
777
+ };
778
+
779
+ // ../claude/src/session/manager.ts
780
+ var SessionManager = class {
781
+ storage;
782
+ events;
783
+ constructor(events, storage) {
784
+ this.storage = storage || new InMemorySessionStorage();
785
+ this.events = events;
786
+ }
787
+ /**
788
+ * Get or create a session
789
+ */
790
+ getOrCreateSession(sessionKey, agentId) {
791
+ let session = this.storage.get(sessionKey);
792
+ if (!session) {
793
+ session = {
794
+ sessionKey,
795
+ agentId,
796
+ messages: [],
797
+ createdAt: Date.now(),
798
+ updatedAt: Date.now()
799
+ };
800
+ this.storage.set(sessionKey, session);
801
+ this.events.emit("session:created", sessionKey);
802
+ }
803
+ return session;
804
+ }
805
+ /**
806
+ * Get session by key
807
+ */
808
+ getSession(sessionKey) {
809
+ return this.storage.get(sessionKey);
810
+ }
811
+ /**
812
+ * Add message to session
813
+ */
814
+ addMessage(sessionKey, message) {
815
+ const session = this.storage.get(sessionKey);
816
+ if (!session) {
817
+ throw new NotFoundError("Session", sessionKey);
818
+ }
819
+ session.messages.push(message);
820
+ session.updatedAt = Date.now();
821
+ this.storage.set(sessionKey, session);
822
+ }
823
+ /**
824
+ * Get session history
825
+ */
826
+ getHistory(sessionKey, limit) {
827
+ const session = this.storage.get(sessionKey);
828
+ if (!session) {
829
+ throw new NotFoundError("Session", sessionKey);
830
+ }
831
+ if (limit && limit > 0) {
832
+ return session.messages.slice(-limit);
833
+ }
834
+ return session.messages;
835
+ }
836
+ /**
837
+ * List sessions with optional filtering
838
+ */
839
+ listSessions(options) {
840
+ let sessions = this.storage.getAll();
841
+ if (options?.agentId) {
842
+ sessions = sessions.filter((s) => s.agentId === options.agentId);
843
+ }
844
+ if (options?.activeWithinMinutes) {
845
+ const cutoff = Date.now() - options.activeWithinMinutes * 60 * 1e3;
846
+ sessions = sessions.filter((s) => s.updatedAt >= cutoff);
847
+ }
848
+ sessions.sort((a, b) => b.updatedAt - a.updatedAt);
849
+ if (options?.offset) {
850
+ sessions = sessions.slice(options.offset);
851
+ }
852
+ if (options?.limit) {
853
+ sessions = sessions.slice(0, options.limit);
854
+ }
855
+ return sessions;
856
+ }
857
+ /**
858
+ * Delete session
859
+ */
860
+ deleteSession(sessionKey) {
861
+ if (!this.storage.has(sessionKey)) {
862
+ throw new NotFoundError("Session", sessionKey);
863
+ }
864
+ this.storage.delete(sessionKey);
865
+ this.events.emit("session:deleted", sessionKey);
866
+ }
867
+ /**
868
+ * Update session process ID
869
+ */
870
+ setProcessId(sessionKey, processId) {
871
+ const session = this.storage.get(sessionKey);
872
+ if (!session) {
873
+ throw new NotFoundError("Session", sessionKey);
874
+ }
875
+ session.processId = processId;
876
+ session.updatedAt = Date.now();
877
+ this.storage.set(sessionKey, session);
878
+ }
879
+ /**
880
+ * Clear process ID from session
881
+ */
882
+ clearProcessId(sessionKey) {
883
+ const session = this.storage.get(sessionKey);
884
+ if (session) {
885
+ delete session.processId;
886
+ session.updatedAt = Date.now();
887
+ this.storage.set(sessionKey, session);
888
+ }
889
+ }
890
+ /**
891
+ * Get session count
892
+ */
893
+ getSessionCount() {
894
+ return this.storage.count();
895
+ }
896
+ /**
897
+ * Clear all sessions
898
+ */
899
+ clearAll() {
900
+ this.storage.clear();
901
+ }
902
+ };
903
+
904
+ // ../claude/src/transport/cli.ts
905
+ var execAsync = promisify(exec);
906
+ var CLITransport = class {
907
+ state = "disconnected";
908
+ processManager;
909
+ sessionManager;
910
+ events;
911
+ config;
912
+ constructor(config, events) {
913
+ this.events = events;
914
+ this.config = {
915
+ ...config,
916
+ autoConnect: config.autoConnect ?? true,
917
+ timeout: config.timeout ?? 3e5
918
+ // 5 minutes default
919
+ };
920
+ this.processManager = new ProcessManager(
921
+ {
922
+ cliPath: config.cliPath,
923
+ maxProcesses: config.maxProcesses,
924
+ processIdleTimeout: config.processIdleTimeout,
925
+ model: config.model,
926
+ permissionMode: config.permissionMode,
927
+ workingDirectory: config.workingDirectory
928
+ },
929
+ events
930
+ );
931
+ this.sessionManager = new SessionManager(events);
932
+ }
933
+ /**
934
+ * Connect - verify Claude CLI is available
935
+ */
936
+ async connect() {
937
+ if (this.state === "connected") {
938
+ return;
939
+ }
940
+ this.state = "connecting";
941
+ this.events.emit("connection:state", "connecting");
942
+ try {
943
+ await this.verifyCLI();
944
+ this.processManager.startCleanup();
945
+ this.state = "connected";
946
+ this.events.emit("connection:state", "connected");
947
+ this.events.emit("connection:connected");
948
+ } catch (error) {
949
+ this.state = "error";
950
+ this.events.emit("connection:state", "error");
951
+ this.events.emit("connection:error", error);
952
+ throw new ConnectionError("Failed to connect to Claude CLI", void 0, error);
953
+ }
954
+ }
955
+ /**
956
+ * Verify Claude CLI is available
957
+ */
958
+ async verifyCLI() {
959
+ try {
960
+ const { stdout } = await execAsync(`${this.config.cliPath} --version`);
961
+ console.log(`Claude CLI available: ${stdout.trim()}`);
962
+ } catch (error) {
963
+ throw new CLIError(
964
+ `Claude CLI not found at '${this.config.cliPath}'. Ensure Claude CLI is installed and in PATH.`,
965
+ error.code,
966
+ "",
967
+ error.message
968
+ );
969
+ }
970
+ }
971
+ /**
972
+ * Disconnect and cleanup
973
+ */
974
+ disconnect() {
975
+ if (this.state === "disconnected") {
976
+ return;
977
+ }
978
+ this.processManager.killAll();
979
+ this.processManager.stopCleanup();
980
+ this.state = "disconnected";
981
+ this.events.emit("connection:state", "disconnected");
982
+ this.events.emit("connection:disconnected");
983
+ }
984
+ /**
985
+ * Check if connected
986
+ */
987
+ isConnected() {
988
+ return this.state === "connected";
989
+ }
990
+ /**
991
+ * Get connection state
992
+ */
993
+ getConnectionState() {
994
+ return this.state;
995
+ }
996
+ /**
997
+ * Make an RPC request
998
+ */
999
+ async request(method, params = {}) {
1000
+ if (!this.isConnected()) {
1001
+ throw new ConnectionError("Not connected to Claude CLI");
1002
+ }
1003
+ switch (method) {
1004
+ case "chat.send":
1005
+ return this.handleChatSend(params);
1006
+ case "sessions.list":
1007
+ return this.handleSessionsList(params);
1008
+ case "sessions.get":
1009
+ return this.handleSessionsGet(params.sessionKey);
1010
+ case "sessions.delete":
1011
+ return this.handleSessionsDelete(params.sessionKey);
1012
+ case "sessions.history":
1013
+ return this.handleSessionsHistory(params.sessionKey);
1014
+ case "system.health":
1015
+ return this.handleSystemHealth();
1016
+ case "system.status":
1017
+ return this.handleSystemStatus();
1018
+ case "agents.list":
1019
+ return this.handleAgentsList();
1020
+ case "agents.get":
1021
+ return this.handleAgentsGet(params.agentId);
1022
+ default:
1023
+ throw new Error(`Unknown RPC method: ${method}`);
1024
+ }
1025
+ }
1026
+ /**
1027
+ * Handle chat.send request
1028
+ * Uses --output-format=json for single-shot responses
1029
+ */
1030
+ async handleChatSend(options) {
1031
+ const sessionKey = options.sessionKey || `claude-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
1032
+ const agentId = options.agentId || "claude";
1033
+ const timeout = options.timeout || this.config.timeout;
1034
+ const session = this.sessionManager.getOrCreateSession(sessionKey, agentId);
1035
+ this.sessionManager.addMessage(sessionKey, {
1036
+ role: "user",
1037
+ content: options.message,
1038
+ timestamp: Date.now()
1039
+ });
1040
+ const existingProc = this.processManager.get(sessionKey);
1041
+ if (existingProc) {
1042
+ this.processManager.kill(sessionKey);
1043
+ }
1044
+ const proc = await this.processManager.spawn({
1045
+ sessionKey,
1046
+ model: this.config.model,
1047
+ thinking: options.thinking,
1048
+ permissionMode: this.config.permissionMode,
1049
+ workingDirectory: this.config.workingDirectory,
1050
+ timeout,
1051
+ message: options.message,
1052
+ outputFormat: "json"
1053
+ });
1054
+ this.processManager.setStatus(sessionKey, "busy");
1055
+ try {
1056
+ const output = await Promise.race([
1057
+ this.collectStdout(proc.process),
1058
+ this.createTimeoutPromise(timeout)
1059
+ ]);
1060
+ let content = "";
1061
+ let thinking;
1062
+ try {
1063
+ const parsed = JSON.parse(output);
1064
+ content = parsed.result || "";
1065
+ if (parsed.thinking) {
1066
+ thinking = parsed.thinking;
1067
+ }
1068
+ } catch {
1069
+ content = output.trim();
1070
+ }
1071
+ this.sessionManager.addMessage(sessionKey, {
1072
+ role: "assistant",
1073
+ content,
1074
+ thinking,
1075
+ timestamp: Date.now()
1076
+ });
1077
+ this.processManager.setStatus(sessionKey, "idle");
1078
+ this.processManager.touch(sessionKey);
1079
+ return {
1080
+ sessionKey,
1081
+ agentId,
1082
+ response: content,
1083
+ thinking,
1084
+ timestamp: Date.now()
1085
+ };
1086
+ } catch (error) {
1087
+ this.processManager.setStatus(sessionKey, "error");
1088
+ throw error;
1089
+ }
1090
+ }
1091
+ /**
1092
+ * Collect full stdout from a child process as a string
1093
+ */
1094
+ collectStdout(proc) {
1095
+ return new Promise((resolve, reject) => {
1096
+ const chunks = [];
1097
+ proc.stdout?.on("data", (chunk) => chunks.push(chunk));
1098
+ proc.stdout?.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8").trim()));
1099
+ proc.stdout?.on("error", reject);
1100
+ proc.on("error", reject);
1101
+ });
1102
+ }
1103
+ /**
1104
+ * Handle sessions.list request
1105
+ */
1106
+ async handleSessionsList(options) {
1107
+ const sessions = this.sessionManager.listSessions(options);
1108
+ return {
1109
+ sessions: sessions.map((s) => ({
1110
+ sessionKey: s.sessionKey,
1111
+ agentId: s.agentId,
1112
+ messageCount: s.messages.length,
1113
+ createdAt: s.createdAt,
1114
+ updatedAt: s.updatedAt
1115
+ })),
1116
+ total: sessions.length
1117
+ };
1118
+ }
1119
+ /**
1120
+ * Handle sessions.get request
1121
+ */
1122
+ async handleSessionsGet(sessionKey) {
1123
+ const session = this.sessionManager.getSession(sessionKey);
1124
+ if (!session) {
1125
+ throw new NotFoundError("Session", sessionKey);
1126
+ }
1127
+ return {
1128
+ sessionKey: session.sessionKey,
1129
+ agentId: session.agentId,
1130
+ messageCount: session.messages.length,
1131
+ createdAt: session.createdAt,
1132
+ updatedAt: session.updatedAt
1133
+ };
1134
+ }
1135
+ /**
1136
+ * Handle sessions.delete request
1137
+ */
1138
+ async handleSessionsDelete(sessionKey) {
1139
+ this.processManager.kill(sessionKey);
1140
+ this.sessionManager.deleteSession(sessionKey);
1141
+ }
1142
+ /**
1143
+ * Handle sessions.history request
1144
+ */
1145
+ async handleSessionsHistory(sessionKey) {
1146
+ const messages = this.sessionManager.getHistory(sessionKey);
1147
+ return { messages };
1148
+ }
1149
+ /**
1150
+ * Handle system.health request
1151
+ */
1152
+ async handleSystemHealth() {
1153
+ try {
1154
+ await this.verifyCLI();
1155
+ return {
1156
+ healthy: true,
1157
+ cliAvailable: true,
1158
+ activeSessions: this.sessionManager.getSessionCount(),
1159
+ activeProcesses: this.processManager.getProcessCount()
1160
+ };
1161
+ } catch {
1162
+ return {
1163
+ healthy: false,
1164
+ cliAvailable: false,
1165
+ activeSessions: this.sessionManager.getSessionCount(),
1166
+ activeProcesses: this.processManager.getProcessCount()
1167
+ };
1168
+ }
1169
+ }
1170
+ /**
1171
+ * Handle system.status request
1172
+ */
1173
+ async handleSystemStatus() {
1174
+ return {
1175
+ state: this.state,
1176
+ activeSessions: this.sessionManager.getSessionCount(),
1177
+ activeProcesses: this.processManager.getProcessCount(),
1178
+ cliPath: this.config.cliPath
1179
+ };
1180
+ }
1181
+ /**
1182
+ * Handle agents.list request (stub)
1183
+ */
1184
+ async handleAgentsList() {
1185
+ return {
1186
+ agents: [
1187
+ {
1188
+ agentId: "claude",
1189
+ status: "available",
1190
+ workspace: this.config.workingDirectory
1191
+ }
1192
+ ],
1193
+ defaultAgentId: "claude"
1194
+ };
1195
+ }
1196
+ /**
1197
+ * Handle agents.get request (stub)
1198
+ */
1199
+ async handleAgentsGet(agentId) {
1200
+ return {
1201
+ agentId,
1202
+ status: "available",
1203
+ workspace: this.config.workingDirectory
1204
+ };
1205
+ }
1206
+ /**
1207
+ * Create timeout promise
1208
+ */
1209
+ createTimeoutPromise(timeout) {
1210
+ return new Promise((_, reject) => {
1211
+ setTimeout(() => {
1212
+ reject(new TimeoutError("Operation timed out", timeout));
1213
+ }, timeout);
1214
+ });
1215
+ }
1216
+ /**
1217
+ * Subscribe to event
1218
+ */
1219
+ on(event, handler) {
1220
+ this.events.on(event, handler);
1221
+ }
1222
+ /**
1223
+ * Unsubscribe from event
1224
+ */
1225
+ off(event, handler) {
1226
+ this.events.off(event, handler);
1227
+ }
1228
+ /**
1229
+ * Emit event
1230
+ */
1231
+ emit(event, ...args) {
1232
+ this.events.emit(event, ...args);
1233
+ }
1234
+ /**
1235
+ * Get process manager (for internal use)
1236
+ */
1237
+ getProcessManager() {
1238
+ return this.processManager;
1239
+ }
1240
+ /**
1241
+ * Get session manager (for internal use)
1242
+ */
1243
+ getSessionManager() {
1244
+ return this.sessionManager;
1245
+ }
1246
+ };
1247
+
1248
+ // ../claude/src/sdk.ts
1249
+ var DEFAULT_CONFIG = {
1250
+ cliPath: "claude",
1251
+ timeout: 3e5,
1252
+ // 5 minutes
1253
+ maxProcesses: 10,
1254
+ processIdleTimeout: 6e5,
1255
+ // 10 minutes
1256
+ model: "claude-sonnet-4-5",
1257
+ permissionMode: "default",
1258
+ workingDirectory: process.cwd(),
1259
+ autoConnect: true
1260
+ };
1261
+ var ClaudeSDK = class _ClaudeSDK {
1262
+ transport;
1263
+ events;
1264
+ // Domain modules
1265
+ system;
1266
+ agents;
1267
+ sessions;
1268
+ chat;
1269
+ constructor(config) {
1270
+ this.events = createEventEmitter();
1271
+ const transportConfig = {
1272
+ cliPath: config.cliPath,
1273
+ timeout: config.timeout,
1274
+ maxProcesses: config.maxProcesses,
1275
+ processIdleTimeout: config.processIdleTimeout,
1276
+ model: config.model,
1277
+ permissionMode: config.permissionMode,
1278
+ workingDirectory: config.workingDirectory,
1279
+ autoConnect: false
1280
+ // We'll connect manually after initialization
1281
+ };
1282
+ this.transport = new CLITransport(transportConfig, this.events);
1283
+ this.system = new SystemModule(this.transport, this.events);
1284
+ this.agents = new AgentsModule(this.transport, this.events);
1285
+ this.sessions = new SessionsModule(this.transport, this.events);
1286
+ this.chat = new ChatModule(this.transport, this.events);
1287
+ }
1288
+ /**
1289
+ * Create a new ClaudeSDK instance
1290
+ * Auto-connects by default
1291
+ */
1292
+ static async create(config) {
1293
+ const fullConfig = {
1294
+ ...DEFAULT_CONFIG,
1295
+ ...config
1296
+ };
1297
+ const sdk = new _ClaudeSDK(fullConfig);
1298
+ if (fullConfig.autoConnect) {
1299
+ await sdk.connect();
1300
+ }
1301
+ return sdk;
1302
+ }
1303
+ /**
1304
+ * Create a disconnected SDK instance
1305
+ * Must call connect() manually
1306
+ */
1307
+ static createDisconnected(config) {
1308
+ const fullConfig = {
1309
+ ...DEFAULT_CONFIG,
1310
+ ...config,
1311
+ autoConnect: false
1312
+ };
1313
+ return new _ClaudeSDK(fullConfig);
1314
+ }
1315
+ /**
1316
+ * Connect to Claude CLI
1317
+ */
1318
+ async connect() {
1319
+ await this.transport.connect();
1320
+ }
1321
+ /**
1322
+ * Disconnect and cleanup
1323
+ */
1324
+ disconnect() {
1325
+ this.transport.disconnect();
1326
+ }
1327
+ /**
1328
+ * Check if connected
1329
+ */
1330
+ isConnected() {
1331
+ return this.transport.isConnected();
1332
+ }
1333
+ /**
1334
+ * Get connection state
1335
+ */
1336
+ getConnectionState() {
1337
+ return this.transport.getConnectionState();
1338
+ }
1339
+ /**
1340
+ * Subscribe to an event
1341
+ */
1342
+ on(event, handler) {
1343
+ this.events.on(event, handler);
1344
+ return this;
1345
+ }
1346
+ /**
1347
+ * Subscribe to an event once
1348
+ */
1349
+ once(event, handler) {
1350
+ this.events.once(event, handler);
1351
+ return this;
1352
+ }
1353
+ /**
1354
+ * Unsubscribe from an event
1355
+ */
1356
+ off(event, handler) {
1357
+ this.events.off(event, handler);
1358
+ return this;
1359
+ }
1360
+ /**
1361
+ * Remove all listeners for an event
1362
+ */
1363
+ removeAllListeners(event) {
1364
+ this.events.removeAllListeners(event);
1365
+ return this;
1366
+ }
1367
+ /**
1368
+ * Wait for an event (promisified)
1369
+ */
1370
+ waitFor(event, timeout) {
1371
+ return this.events.waitFor(event, timeout);
1372
+ }
1373
+ };
1374
+ export {
1375
+ CLIError,
1376
+ ClaudeError,
1377
+ ClaudeSDK,
1378
+ ConnectionError,
1379
+ NotConnectedError,
1380
+ NotFoundError,
1381
+ NotImplementedError,
1382
+ ProcessError,
1383
+ StreamParseError,
1384
+ TimeoutError,
1385
+ ValidationError
1386
+ };