replicas-engine 0.1.6 → 0.1.7
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/routes/codex.js +26 -23
- package/dist/services/codex-manager.js +33 -8
- package/package.json +1 -1
package/dist/routes/codex.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import { stream } from 'hono/streaming';
|
|
3
2
|
import { CodexManager } from '../services/codex-manager.js';
|
|
4
3
|
const codex = new Hono();
|
|
5
4
|
const codexManager = new CodexManager();
|
|
6
5
|
/**
|
|
7
6
|
* POST /codex/send
|
|
8
|
-
* send a message to Codex
|
|
7
|
+
* send a message to Codex (non-blocking, writes to JSONL automatically)
|
|
9
8
|
*/
|
|
10
9
|
codex.post('/send', async (c) => {
|
|
11
10
|
try {
|
|
@@ -14,27 +13,10 @@ codex.post('/send', async (c) => {
|
|
|
14
13
|
if (!message || typeof message !== 'string') {
|
|
15
14
|
return c.json({ error: 'Message is required and must be a string' }, 400);
|
|
16
15
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
stream.onAbort(() => {
|
|
22
|
-
console.log('Client aborted SSE connection');
|
|
23
|
-
});
|
|
24
|
-
try {
|
|
25
|
-
for await (const event of codexManager.sendMessage(message)) {
|
|
26
|
-
const sseData = `event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`;
|
|
27
|
-
await stream.write(sseData);
|
|
28
|
-
}
|
|
29
|
-
await stream.write('event: done\ndata: {}\n\n');
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
console.error('Error during Codex streaming:', error);
|
|
33
|
-
const errorData = `event: error\ndata: ${JSON.stringify({
|
|
34
|
-
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
35
|
-
})}\n\n`;
|
|
36
|
-
await stream.write(errorData);
|
|
37
|
-
}
|
|
16
|
+
await codexManager.sendMessage(message);
|
|
17
|
+
return c.json({
|
|
18
|
+
success: true,
|
|
19
|
+
message: 'Message sent successfully',
|
|
38
20
|
});
|
|
39
21
|
}
|
|
40
22
|
catch (error) {
|
|
@@ -69,6 +51,27 @@ codex.get('/history', async (c) => {
|
|
|
69
51
|
}, 500);
|
|
70
52
|
}
|
|
71
53
|
});
|
|
54
|
+
/**
|
|
55
|
+
* GET /codex/updates
|
|
56
|
+
* get new events since a given timestamp (for polling)
|
|
57
|
+
*/
|
|
58
|
+
codex.get('/updates', async (c) => {
|
|
59
|
+
try {
|
|
60
|
+
const since = c.req.query('since') || '';
|
|
61
|
+
if (!since) {
|
|
62
|
+
return c.json({ error: 'Missing "since" query parameter' }, 400);
|
|
63
|
+
}
|
|
64
|
+
const updates = await codexManager.getUpdates(since);
|
|
65
|
+
return c.json(updates);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error('Error in /codex/updates:', error);
|
|
69
|
+
return c.json({
|
|
70
|
+
error: 'Failed to retrieve updates',
|
|
71
|
+
details: error instanceof Error ? error.message : 'Unknown error',
|
|
72
|
+
}, 500);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
72
75
|
/**
|
|
73
76
|
* GET /codex/status
|
|
74
77
|
* get current thread status and information
|
|
@@ -22,7 +22,7 @@ export class CodexManager {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
async
|
|
25
|
+
async sendMessage(message) {
|
|
26
26
|
if (!this.currentThread) {
|
|
27
27
|
if (this.currentThreadId) {
|
|
28
28
|
console.log(`Resuming thread ${this.currentThreadId}`);
|
|
@@ -41,13 +41,10 @@ export class CodexManager {
|
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(`Thread started: ${this.currentThreadId}`);
|
|
49
|
-
}
|
|
50
|
-
yield event;
|
|
44
|
+
await this.currentThread.run(message);
|
|
45
|
+
if (!this.currentThreadId && this.currentThread.id) {
|
|
46
|
+
this.currentThreadId = this.currentThread.id;
|
|
47
|
+
console.log(`Thread started: ${this.currentThreadId}`);
|
|
51
48
|
}
|
|
52
49
|
}
|
|
53
50
|
async getHistory() {
|
|
@@ -92,4 +89,32 @@ export class CodexManager {
|
|
|
92
89
|
getThreadId() {
|
|
93
90
|
return this.currentThreadId;
|
|
94
91
|
}
|
|
92
|
+
async getUpdates(since) {
|
|
93
|
+
if (!this.currentThreadId) {
|
|
94
|
+
return {
|
|
95
|
+
events: [],
|
|
96
|
+
isComplete: true,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const sessionFile = await findSessionFile(this.currentThreadId);
|
|
100
|
+
if (!sessionFile) {
|
|
101
|
+
return {
|
|
102
|
+
events: [],
|
|
103
|
+
isComplete: true,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const allEvents = await readJSONL(sessionFile);
|
|
107
|
+
// Filter events that occurred after the 'since' timestamp
|
|
108
|
+
const filteredEvents = allEvents.filter((event) => {
|
|
109
|
+
return event.timestamp > since;
|
|
110
|
+
});
|
|
111
|
+
// Check if thread is complete by looking for turn.completed or error events
|
|
112
|
+
const isComplete = this.currentThread === null ||
|
|
113
|
+
allEvents.some((event) => event.type === 'event_msg' &&
|
|
114
|
+
(event.payload?.type === 'turn.completed' || event.payload?.type === 'error'));
|
|
115
|
+
return {
|
|
116
|
+
events: filteredEvents,
|
|
117
|
+
isComplete,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
95
120
|
}
|