replicas-engine 0.1.15 → 0.1.17
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/src/index.js +255 -68
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import "dotenv/config";
|
|
|
5
5
|
import { serve } from "@hono/node-server";
|
|
6
6
|
import { Hono as Hono3 } from "hono";
|
|
7
7
|
import { readFile as readFile2 } from "fs/promises";
|
|
8
|
+
import { execSync as execSync2 } from "child_process";
|
|
8
9
|
|
|
9
10
|
// src/middleware/auth.ts
|
|
10
11
|
var authMiddleware = async (c, next) => {
|
|
@@ -218,25 +219,25 @@ function convertCodexEvent(event, linearSessionId) {
|
|
|
218
219
|
const item = event.item;
|
|
219
220
|
if (!item) return null;
|
|
220
221
|
if (item.type === "agent_message") {
|
|
221
|
-
const
|
|
222
|
-
if (
|
|
222
|
+
const text = "text" in item ? String(item.text || "") : "";
|
|
223
|
+
if (text) {
|
|
223
224
|
return {
|
|
224
225
|
linearSessionId,
|
|
225
226
|
content: {
|
|
226
227
|
type: "thought",
|
|
227
|
-
body:
|
|
228
|
+
body: text
|
|
228
229
|
}
|
|
229
230
|
};
|
|
230
231
|
}
|
|
231
232
|
}
|
|
232
233
|
if (item.type === "reasoning") {
|
|
233
|
-
const
|
|
234
|
-
if (
|
|
234
|
+
const text = "text" in item ? String(item.text || "") : "";
|
|
235
|
+
if (text) {
|
|
235
236
|
return {
|
|
236
237
|
linearSessionId,
|
|
237
238
|
content: {
|
|
238
239
|
type: "thought",
|
|
239
|
-
body:
|
|
240
|
+
body: text
|
|
240
241
|
}
|
|
241
242
|
};
|
|
242
243
|
}
|
|
@@ -253,23 +254,24 @@ function convertCodexEvent(event, linearSessionId) {
|
|
|
253
254
|
};
|
|
254
255
|
}
|
|
255
256
|
if (item.type === "file_change") {
|
|
256
|
-
const
|
|
257
|
+
const changes = "changes" in item && Array.isArray(item.changes) ? item.changes : [];
|
|
258
|
+
const paths = changes.map((c) => c.path || "").filter(Boolean);
|
|
257
259
|
return {
|
|
258
260
|
linearSessionId,
|
|
259
261
|
content: {
|
|
260
262
|
type: "action",
|
|
261
263
|
action: "File change",
|
|
262
|
-
parameter:
|
|
264
|
+
parameter: paths.join(", ") || ""
|
|
263
265
|
}
|
|
264
266
|
};
|
|
265
267
|
}
|
|
266
268
|
if (item.type === "mcp_tool_call") {
|
|
267
|
-
const
|
|
269
|
+
const tool = "tool" in item ? String(item.tool || "") : "";
|
|
268
270
|
return {
|
|
269
271
|
linearSessionId,
|
|
270
272
|
content: {
|
|
271
273
|
type: "action",
|
|
272
|
-
action:
|
|
274
|
+
action: tool || "MCP tool call",
|
|
273
275
|
parameter: ""
|
|
274
276
|
}
|
|
275
277
|
};
|
|
@@ -299,45 +301,45 @@ function convertCodexEvent(event, linearSessionId) {
|
|
|
299
301
|
if (event.type === "item.completed") {
|
|
300
302
|
const item = event.item;
|
|
301
303
|
if (!item) return null;
|
|
302
|
-
const getResult = () => {
|
|
303
|
-
if ("output" in item && item.output) return String(item.output);
|
|
304
|
-
if ("result" in item && item.result) return String(item.result);
|
|
305
|
-
if ("exit_code" in item) return `Exit code: ${item.exit_code}`;
|
|
306
|
-
return "Done";
|
|
307
|
-
};
|
|
308
304
|
if (item.type === "command_execution") {
|
|
309
305
|
const command = "command" in item ? String(item.command || "") : "";
|
|
306
|
+
const output = "aggregated_output" in item ? String(item.aggregated_output || "") : "";
|
|
307
|
+
const exitCode = "exit_code" in item ? item.exit_code : void 0;
|
|
308
|
+
const result = exitCode !== void 0 ? `Exit code: ${exitCode}` : output || "Done";
|
|
310
309
|
return {
|
|
311
310
|
linearSessionId,
|
|
312
311
|
content: {
|
|
313
312
|
type: "action",
|
|
314
313
|
action: "Running command",
|
|
315
314
|
parameter: command,
|
|
316
|
-
result
|
|
315
|
+
result
|
|
317
316
|
}
|
|
318
317
|
};
|
|
319
318
|
}
|
|
320
319
|
if (item.type === "file_change") {
|
|
321
|
-
const
|
|
320
|
+
const changes = "changes" in item && Array.isArray(item.changes) ? item.changes : [];
|
|
321
|
+
const paths = changes.map((c) => c.path || "").filter(Boolean);
|
|
322
|
+
const status = "status" in item ? String(item.status || "") : "";
|
|
322
323
|
return {
|
|
323
324
|
linearSessionId,
|
|
324
325
|
content: {
|
|
325
326
|
type: "action",
|
|
326
327
|
action: "File change",
|
|
327
|
-
parameter:
|
|
328
|
-
result:
|
|
328
|
+
parameter: paths.join(", ") || "",
|
|
329
|
+
result: status === "completed" ? "Done" : status || "Done"
|
|
329
330
|
}
|
|
330
331
|
};
|
|
331
332
|
}
|
|
332
333
|
if (item.type === "mcp_tool_call") {
|
|
333
|
-
const
|
|
334
|
+
const tool = "tool" in item ? String(item.tool || "") : "";
|
|
335
|
+
const status = "status" in item ? String(item.status || "") : "";
|
|
334
336
|
return {
|
|
335
337
|
linearSessionId,
|
|
336
338
|
content: {
|
|
337
339
|
type: "action",
|
|
338
|
-
action:
|
|
340
|
+
action: tool || "MCP tool call",
|
|
339
341
|
parameter: "",
|
|
340
|
-
result:
|
|
342
|
+
result: status === "completed" ? "Done" : status || "Done"
|
|
341
343
|
}
|
|
342
344
|
};
|
|
343
345
|
}
|
|
@@ -349,7 +351,7 @@ function convertCodexEvent(event, linearSessionId) {
|
|
|
349
351
|
type: "action",
|
|
350
352
|
action: "Web search",
|
|
351
353
|
parameter: query2,
|
|
352
|
-
result:
|
|
354
|
+
result: "Done"
|
|
353
355
|
}
|
|
354
356
|
};
|
|
355
357
|
}
|
|
@@ -360,18 +362,18 @@ function convertCodexEvent(event, linearSessionId) {
|
|
|
360
362
|
type: "action",
|
|
361
363
|
action: "Updating plan",
|
|
362
364
|
parameter: "",
|
|
363
|
-
result:
|
|
365
|
+
result: "Done"
|
|
364
366
|
}
|
|
365
367
|
};
|
|
366
368
|
}
|
|
367
369
|
if (item.type === "agent_message") {
|
|
368
|
-
const
|
|
369
|
-
if (
|
|
370
|
+
const text = "text" in item ? String(item.text || "") : "";
|
|
371
|
+
if (text) {
|
|
370
372
|
return {
|
|
371
373
|
linearSessionId,
|
|
372
374
|
content: {
|
|
373
375
|
type: "thought",
|
|
374
|
-
body:
|
|
376
|
+
body: text
|
|
375
377
|
}
|
|
376
378
|
};
|
|
377
379
|
}
|
|
@@ -488,13 +490,120 @@ function getGitStatus(workingDirectory) {
|
|
|
488
490
|
};
|
|
489
491
|
}
|
|
490
492
|
|
|
493
|
+
// src/services/message-queue.ts
|
|
494
|
+
var MessageQueue = class {
|
|
495
|
+
queue = [];
|
|
496
|
+
processing = false;
|
|
497
|
+
currentMessageId = null;
|
|
498
|
+
messageIdCounter = 0;
|
|
499
|
+
processMessage;
|
|
500
|
+
constructor(processMessage) {
|
|
501
|
+
this.processMessage = processMessage;
|
|
502
|
+
}
|
|
503
|
+
generateMessageId() {
|
|
504
|
+
return `msg_${Date.now()}_${++this.messageIdCounter}`;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Add a message to the queue or start processing immediately if not busy
|
|
508
|
+
* @returns Object indicating whether the message was queued or started processing
|
|
509
|
+
*/
|
|
510
|
+
async enqueue(message, model, customInstructions) {
|
|
511
|
+
const messageId = this.generateMessageId();
|
|
512
|
+
const queuedMessage = {
|
|
513
|
+
id: messageId,
|
|
514
|
+
message,
|
|
515
|
+
model,
|
|
516
|
+
customInstructions,
|
|
517
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
518
|
+
};
|
|
519
|
+
if (this.processing) {
|
|
520
|
+
this.queue.push(queuedMessage);
|
|
521
|
+
return {
|
|
522
|
+
queued: true,
|
|
523
|
+
messageId,
|
|
524
|
+
position: this.queue.length
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
this.startProcessing(queuedMessage);
|
|
528
|
+
return {
|
|
529
|
+
queued: false,
|
|
530
|
+
messageId,
|
|
531
|
+
position: 0
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
async startProcessing(queuedMessage) {
|
|
535
|
+
this.processing = true;
|
|
536
|
+
this.currentMessageId = queuedMessage.id;
|
|
537
|
+
try {
|
|
538
|
+
await this.processMessage(
|
|
539
|
+
queuedMessage.message,
|
|
540
|
+
queuedMessage.model,
|
|
541
|
+
queuedMessage.customInstructions
|
|
542
|
+
);
|
|
543
|
+
} catch (error) {
|
|
544
|
+
console.error("[MessageQueue] Error processing message:", error);
|
|
545
|
+
} finally {
|
|
546
|
+
this.processing = false;
|
|
547
|
+
this.currentMessageId = null;
|
|
548
|
+
await this.processNextInQueue();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async processNextInQueue() {
|
|
552
|
+
const nextMessage = this.queue.shift();
|
|
553
|
+
if (nextMessage) {
|
|
554
|
+
await this.startProcessing(nextMessage);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Check if currently processing a message
|
|
559
|
+
*/
|
|
560
|
+
isProcessing() {
|
|
561
|
+
return this.processing;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Get the current queue length
|
|
565
|
+
*/
|
|
566
|
+
getQueueLength() {
|
|
567
|
+
return this.queue.length;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Get full queue status
|
|
571
|
+
*/
|
|
572
|
+
getStatus() {
|
|
573
|
+
return {
|
|
574
|
+
isProcessing: this.processing,
|
|
575
|
+
queueLength: this.queue.length,
|
|
576
|
+
currentMessageId: this.currentMessageId,
|
|
577
|
+
queuedMessages: this.queue.map((msg, index) => ({
|
|
578
|
+
id: msg.id,
|
|
579
|
+
queuedAt: msg.queuedAt,
|
|
580
|
+
position: index + 1
|
|
581
|
+
}))
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Clear the queue (does not stop current processing)
|
|
586
|
+
*/
|
|
587
|
+
clearQueue() {
|
|
588
|
+
this.queue = [];
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Reset everything including clearing processing state
|
|
592
|
+
*/
|
|
593
|
+
reset() {
|
|
594
|
+
this.queue = [];
|
|
595
|
+
this.processing = false;
|
|
596
|
+
this.currentMessageId = null;
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
|
|
491
600
|
// src/services/codex-manager.ts
|
|
492
601
|
var CodexManager = class {
|
|
493
602
|
codex;
|
|
494
603
|
currentThreadId = null;
|
|
495
604
|
currentThread = null;
|
|
496
605
|
workingDirectory;
|
|
497
|
-
|
|
606
|
+
messageQueue;
|
|
498
607
|
constructor(workingDirectory) {
|
|
499
608
|
this.codex = new Codex();
|
|
500
609
|
if (workingDirectory) {
|
|
@@ -508,14 +617,38 @@ var CodexManager = class {
|
|
|
508
617
|
this.workingDirectory = workspaceHome;
|
|
509
618
|
}
|
|
510
619
|
}
|
|
620
|
+
this.messageQueue = new MessageQueue(this.processMessageInternal.bind(this));
|
|
511
621
|
}
|
|
512
622
|
isProcessing() {
|
|
513
|
-
return this.
|
|
623
|
+
return this.messageQueue.isProcessing();
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Enqueue a message for processing. If not currently processing, starts immediately.
|
|
627
|
+
* If already processing, adds to queue.
|
|
628
|
+
* @returns Object with queued status, messageId, and position in queue
|
|
629
|
+
*/
|
|
630
|
+
async enqueueMessage(message, model, customInstructions) {
|
|
631
|
+
return this.messageQueue.enqueue(message, model, customInstructions);
|
|
514
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Get the current queue status
|
|
635
|
+
*/
|
|
636
|
+
getQueueStatus() {
|
|
637
|
+
return this.messageQueue.getStatus();
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Legacy sendMessage method - now uses the queue internally
|
|
641
|
+
* @deprecated Use enqueueMessage for better control over queue status
|
|
642
|
+
*/
|
|
515
643
|
async sendMessage(message, model, customInstructions) {
|
|
644
|
+
await this.enqueueMessage(message, model, customInstructions);
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Internal method that actually processes the message
|
|
648
|
+
*/
|
|
649
|
+
async processMessageInternal(message, model, customInstructions) {
|
|
516
650
|
const linearSessionId = process.env.LINEAR_SESSION_ID;
|
|
517
651
|
try {
|
|
518
|
-
this.processing = true;
|
|
519
652
|
if (!this.currentThread) {
|
|
520
653
|
if (this.currentThreadId) {
|
|
521
654
|
this.currentThread = this.codex.resumeThread(this.currentThreadId, {
|
|
@@ -557,7 +690,6 @@ var CodexManager = class {
|
|
|
557
690
|
}
|
|
558
691
|
}
|
|
559
692
|
} finally {
|
|
560
|
-
this.processing = false;
|
|
561
693
|
if (linearSessionId) {
|
|
562
694
|
const status = getGitStatus(this.workingDirectory);
|
|
563
695
|
monolithService.sendEvent({ type: "agent_turn_complete", payload: { linearSessionId, status } }).catch(() => {
|
|
@@ -600,7 +732,7 @@ var CodexManager = class {
|
|
|
600
732
|
async reset() {
|
|
601
733
|
this.currentThread = null;
|
|
602
734
|
this.currentThreadId = null;
|
|
603
|
-
this.
|
|
735
|
+
this.messageQueue.reset();
|
|
604
736
|
}
|
|
605
737
|
getThreadId() {
|
|
606
738
|
return this.currentThreadId;
|
|
@@ -682,21 +814,15 @@ codex.post("/send", async (c) => {
|
|
|
682
814
|
if (!message || typeof message !== "string") {
|
|
683
815
|
return c.json({ error: "Message is required and must be a string" }, 400);
|
|
684
816
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
{
|
|
688
|
-
error: "A turn is already in progress. Please wait."
|
|
689
|
-
},
|
|
690
|
-
400
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
codexManager.sendMessage(message, model, customInstructions).catch((error) => {
|
|
694
|
-
console.error("[Codex Route] Error in background message processing:", error);
|
|
695
|
-
});
|
|
696
|
-
return c.json({
|
|
817
|
+
const result = await codexManager.enqueueMessage(message, model, customInstructions);
|
|
818
|
+
const response = {
|
|
697
819
|
success: true,
|
|
698
|
-
message: "Message sent successfully"
|
|
699
|
-
|
|
820
|
+
message: result.queued ? `Message queued at position ${result.position}` : "Message sent successfully",
|
|
821
|
+
queued: result.queued,
|
|
822
|
+
messageId: result.messageId,
|
|
823
|
+
position: result.position
|
|
824
|
+
};
|
|
825
|
+
return c.json(response);
|
|
700
826
|
} catch (error) {
|
|
701
827
|
console.error("Error in /codex/send:", error);
|
|
702
828
|
return c.json(
|
|
@@ -764,6 +890,21 @@ codex.get("/status", async (c) => {
|
|
|
764
890
|
);
|
|
765
891
|
}
|
|
766
892
|
});
|
|
893
|
+
codex.get("/queue", async (c) => {
|
|
894
|
+
try {
|
|
895
|
+
const queueStatus = codexManager.getQueueStatus();
|
|
896
|
+
return c.json(queueStatus);
|
|
897
|
+
} catch (error) {
|
|
898
|
+
console.error("Error in /codex/queue:", error);
|
|
899
|
+
return c.json(
|
|
900
|
+
{
|
|
901
|
+
error: "Failed to retrieve queue status",
|
|
902
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
903
|
+
},
|
|
904
|
+
500
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
});
|
|
767
908
|
codex.post("/reset", async (c) => {
|
|
768
909
|
try {
|
|
769
910
|
await codexManager.reset();
|
|
@@ -799,7 +940,7 @@ var ClaudeManager = class {
|
|
|
799
940
|
historyFile;
|
|
800
941
|
sessionId = null;
|
|
801
942
|
initialized;
|
|
802
|
-
|
|
943
|
+
messageQueue;
|
|
803
944
|
constructor(workingDirectory) {
|
|
804
945
|
if (workingDirectory) {
|
|
805
946
|
this.workingDirectory = workingDirectory;
|
|
@@ -814,17 +955,42 @@ var ClaudeManager = class {
|
|
|
814
955
|
}
|
|
815
956
|
this.historyFile = join2(homedir2(), ".replicas", "claude", "history.jsonl");
|
|
816
957
|
this.initialized = this.initialize();
|
|
958
|
+
this.messageQueue = new MessageQueue(this.processMessageInternal.bind(this));
|
|
817
959
|
}
|
|
818
960
|
isProcessing() {
|
|
819
|
-
return this.
|
|
961
|
+
return this.messageQueue.isProcessing();
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Enqueue a message for processing. If not currently processing, starts immediately.
|
|
965
|
+
* If already processing, adds to queue.
|
|
966
|
+
* @returns Object with queued status, messageId, and position in queue
|
|
967
|
+
*/
|
|
968
|
+
async enqueueMessage(message, model, customInstructions) {
|
|
969
|
+
await this.initialized;
|
|
970
|
+
return this.messageQueue.enqueue(message, model, customInstructions);
|
|
820
971
|
}
|
|
972
|
+
/**
|
|
973
|
+
* Get the current queue status
|
|
974
|
+
*/
|
|
975
|
+
getQueueStatus() {
|
|
976
|
+
return this.messageQueue.getStatus();
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Legacy sendMessage method - now uses the queue internally
|
|
980
|
+
* @deprecated Use enqueueMessage for better control over queue status
|
|
981
|
+
*/
|
|
821
982
|
async sendMessage(message, model, customInstructions) {
|
|
983
|
+
await this.enqueueMessage(message, model, customInstructions);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Internal method that actually processes the message
|
|
987
|
+
*/
|
|
988
|
+
async processMessageInternal(message, model, customInstructions) {
|
|
822
989
|
const linearSessionId = process.env.LINEAR_SESSION_ID;
|
|
823
990
|
if (!message || !message.trim()) {
|
|
824
991
|
throw new Error("Message cannot be empty");
|
|
825
992
|
}
|
|
826
993
|
await this.initialized;
|
|
827
|
-
this.processing = true;
|
|
828
994
|
try {
|
|
829
995
|
const userMessage = {
|
|
830
996
|
type: "user",
|
|
@@ -872,7 +1038,6 @@ var ClaudeManager = class {
|
|
|
872
1038
|
}
|
|
873
1039
|
}
|
|
874
1040
|
} finally {
|
|
875
|
-
this.processing = false;
|
|
876
1041
|
if (linearSessionId) {
|
|
877
1042
|
const status = getGitStatus(this.workingDirectory);
|
|
878
1043
|
monolithService.sendEvent({ type: "agent_turn_complete", payload: { linearSessionId, status } }).catch(() => {
|
|
@@ -891,7 +1056,7 @@ var ClaudeManager = class {
|
|
|
891
1056
|
async getStatus() {
|
|
892
1057
|
await this.initialized;
|
|
893
1058
|
const status = {
|
|
894
|
-
has_active_thread: this.
|
|
1059
|
+
has_active_thread: this.messageQueue.isProcessing(),
|
|
895
1060
|
thread_id: this.sessionId,
|
|
896
1061
|
working_directory: this.workingDirectory
|
|
897
1062
|
};
|
|
@@ -901,13 +1066,13 @@ var ClaudeManager = class {
|
|
|
901
1066
|
await this.initialized;
|
|
902
1067
|
const allEvents = await readJSONL(this.historyFile);
|
|
903
1068
|
const events = allEvents.filter((event) => event.timestamp > since);
|
|
904
|
-
const isComplete = !this.
|
|
1069
|
+
const isComplete = !this.messageQueue.isProcessing();
|
|
905
1070
|
return { events, isComplete };
|
|
906
1071
|
}
|
|
907
1072
|
async reset() {
|
|
908
1073
|
await this.initialized;
|
|
909
1074
|
this.sessionId = null;
|
|
910
|
-
this.
|
|
1075
|
+
this.messageQueue.reset();
|
|
911
1076
|
try {
|
|
912
1077
|
await rm(this.historyFile, { force: true });
|
|
913
1078
|
} catch {
|
|
@@ -944,21 +1109,15 @@ claude.post("/send", async (c) => {
|
|
|
944
1109
|
if (!message || typeof message !== "string") {
|
|
945
1110
|
return c.json({ error: "Message is required and must be a string" }, 400);
|
|
946
1111
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
{
|
|
950
|
-
error: "A turn is already in progress. Please wait."
|
|
951
|
-
},
|
|
952
|
-
400
|
|
953
|
-
);
|
|
954
|
-
}
|
|
955
|
-
claudeManager.sendMessage(message, model, customInstructions).catch((error) => {
|
|
956
|
-
console.error("[Claude Route] Error in background message processing:", error);
|
|
957
|
-
});
|
|
958
|
-
return c.json({
|
|
1112
|
+
const result = await claudeManager.enqueueMessage(message, model, customInstructions);
|
|
1113
|
+
const response = {
|
|
959
1114
|
success: true,
|
|
960
|
-
message: "Message sent successfully"
|
|
961
|
-
|
|
1115
|
+
message: result.queued ? `Message queued at position ${result.position}` : "Message sent successfully",
|
|
1116
|
+
queued: result.queued,
|
|
1117
|
+
messageId: result.messageId,
|
|
1118
|
+
position: result.position
|
|
1119
|
+
};
|
|
1120
|
+
return c.json(response);
|
|
962
1121
|
} catch (error) {
|
|
963
1122
|
console.error("Error in /claude/send:", error);
|
|
964
1123
|
return c.json(
|
|
@@ -1026,6 +1185,21 @@ claude.get("/status", async (c) => {
|
|
|
1026
1185
|
);
|
|
1027
1186
|
}
|
|
1028
1187
|
});
|
|
1188
|
+
claude.get("/queue", async (c) => {
|
|
1189
|
+
try {
|
|
1190
|
+
const queueStatus = claudeManager.getQueueStatus();
|
|
1191
|
+
return c.json(queueStatus);
|
|
1192
|
+
} catch (error) {
|
|
1193
|
+
console.error("Error in /claude/queue:", error);
|
|
1194
|
+
return c.json(
|
|
1195
|
+
{
|
|
1196
|
+
error: "Failed to retrieve queue status",
|
|
1197
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
1198
|
+
},
|
|
1199
|
+
500
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1029
1203
|
claude.post("/reset", async (c) => {
|
|
1030
1204
|
try {
|
|
1031
1205
|
await claudeManager.reset();
|
|
@@ -1216,6 +1390,15 @@ async function initializeGitRepository() {
|
|
|
1216
1390
|
// src/index.ts
|
|
1217
1391
|
var READY_MESSAGE = "========= REPLICAS WORKSPACE READY ==========";
|
|
1218
1392
|
var COMPLETION_MESSAGE = "========= REPLICAS WORKSPACE INITIALIZATION COMPLETE ==========";
|
|
1393
|
+
function checkActiveSSHSessions() {
|
|
1394
|
+
try {
|
|
1395
|
+
const output = execSync2('who | grep -v "^$" | wc -l', { encoding: "utf-8" });
|
|
1396
|
+
const sessionCount = parseInt(output.trim(), 10);
|
|
1397
|
+
return sessionCount > 0;
|
|
1398
|
+
} catch {
|
|
1399
|
+
return false;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1219
1402
|
var app = new Hono3();
|
|
1220
1403
|
app.get("/health", async (c) => {
|
|
1221
1404
|
try {
|
|
@@ -1243,11 +1426,13 @@ app.get("/status", async (c) => {
|
|
|
1243
1426
|
const isClaudeUsed = claudeHistory.thread_id !== null;
|
|
1244
1427
|
const claudeStatus = await claudeManager.getStatus();
|
|
1245
1428
|
const workingDirectory = claudeStatus.working_directory;
|
|
1429
|
+
const hasActiveSSHSessions = checkActiveSSHSessions();
|
|
1246
1430
|
return c.json({
|
|
1247
1431
|
isCodexProcessing,
|
|
1248
1432
|
isClaudeProcessing,
|
|
1249
1433
|
isCodexUsed,
|
|
1250
1434
|
isClaudeUsed,
|
|
1435
|
+
hasActiveSSHSessions,
|
|
1251
1436
|
...getGitStatus(workingDirectory),
|
|
1252
1437
|
linearBetaEnabled: true
|
|
1253
1438
|
// TODO: delete
|
|
@@ -1283,5 +1468,7 @@ serve(
|
|
|
1283
1468
|
console.warn(`Git initialization warning: ${gitResult.error}`);
|
|
1284
1469
|
}
|
|
1285
1470
|
await githubTokenManager.start();
|
|
1471
|
+
const monolithService2 = new MonolithService();
|
|
1472
|
+
await monolithService2.sendEvent({ type: "workspace_ready", payload: {} });
|
|
1286
1473
|
}
|
|
1287
1474
|
);
|