replicas-engine 0.1.25 → 0.1.27
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 +157 -13
- package/package.json +2 -2
package/dist/src/index.js
CHANGED
|
@@ -54,6 +54,35 @@ async function readJSONL(filePath) {
|
|
|
54
54
|
return [];
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
async function readJSONLPaginated(filePath, limit, offset = 0) {
|
|
58
|
+
try {
|
|
59
|
+
const content = await readFile(filePath, "utf-8");
|
|
60
|
+
const lines = content.split("\n").filter((line) => line.trim());
|
|
61
|
+
const allEvents = lines.map((line) => {
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(line);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}).filter((event) => event !== null);
|
|
68
|
+
const total = allEvents.length;
|
|
69
|
+
const startIndex = Math.max(0, total - offset - (limit ?? total));
|
|
70
|
+
const endIndex = Math.max(0, total - offset);
|
|
71
|
+
const events = allEvents.slice(startIndex, endIndex);
|
|
72
|
+
const hasMore = startIndex > 0;
|
|
73
|
+
return {
|
|
74
|
+
events,
|
|
75
|
+
total,
|
|
76
|
+
hasMore
|
|
77
|
+
};
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return {
|
|
80
|
+
events: [],
|
|
81
|
+
total: 0,
|
|
82
|
+
hasMore: false
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
57
86
|
|
|
58
87
|
// src/services/codex-manager.ts
|
|
59
88
|
import { readdir, stat, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
@@ -110,6 +139,14 @@ function summarizeInput(input) {
|
|
|
110
139
|
if (obj.pattern) return `pattern: ${String(obj.pattern)}`;
|
|
111
140
|
if (obj.query) return String(obj.query);
|
|
112
141
|
if (obj.url) return String(obj.url);
|
|
142
|
+
if (obj.todos && Array.isArray(obj.todos)) {
|
|
143
|
+
const todos = obj.todos;
|
|
144
|
+
const completed = todos.filter((t) => t.status === "completed").length;
|
|
145
|
+
const total = todos.length;
|
|
146
|
+
const currentTask = todos.find((t) => t.status === "in_progress");
|
|
147
|
+
const summary = `${completed}/${total}`;
|
|
148
|
+
return currentTask?.content ? `${summary}: ${currentTask.content}` : summary;
|
|
149
|
+
}
|
|
113
150
|
const keys = Object.keys(obj);
|
|
114
151
|
if (keys.length > 0) {
|
|
115
152
|
const firstKey = keys[0];
|
|
@@ -298,12 +335,18 @@ function convertCodexEvent(event, linearSessionId) {
|
|
|
298
335
|
};
|
|
299
336
|
}
|
|
300
337
|
if (item.type === "todo_list") {
|
|
338
|
+
const items = "items" in item && Array.isArray(item.items) ? item.items : [];
|
|
339
|
+
const completed = items.filter((t) => t.completed).length;
|
|
340
|
+
const total = items.length;
|
|
341
|
+
const currentTask = items.find((t) => !t.completed);
|
|
342
|
+
const summary = `${completed}/${total}`;
|
|
343
|
+
const parameter = currentTask?.text ? `${summary}: ${currentTask.text}` : summary;
|
|
301
344
|
return {
|
|
302
345
|
linearSessionId,
|
|
303
346
|
content: {
|
|
304
347
|
type: "action",
|
|
305
348
|
action: "Updating plan",
|
|
306
|
-
parameter
|
|
349
|
+
parameter
|
|
307
350
|
}
|
|
308
351
|
};
|
|
309
352
|
}
|
|
@@ -366,12 +409,18 @@ function convertCodexEvent(event, linearSessionId) {
|
|
|
366
409
|
};
|
|
367
410
|
}
|
|
368
411
|
if (item.type === "todo_list") {
|
|
412
|
+
const items = "items" in item && Array.isArray(item.items) ? item.items : [];
|
|
413
|
+
const completed = items.filter((t) => t.completed).length;
|
|
414
|
+
const total = items.length;
|
|
415
|
+
const currentTask = items.find((t) => !t.completed);
|
|
416
|
+
const summary = `${completed}/${total}`;
|
|
417
|
+
const parameter = currentTask?.text ? `${summary}: ${currentTask.text}` : summary;
|
|
369
418
|
return {
|
|
370
419
|
linearSessionId,
|
|
371
420
|
content: {
|
|
372
421
|
type: "action",
|
|
373
422
|
action: "Updating plan",
|
|
374
|
-
parameter
|
|
423
|
+
parameter,
|
|
375
424
|
result: "Done"
|
|
376
425
|
}
|
|
377
426
|
};
|
|
@@ -518,6 +567,9 @@ async function getPullRequestUrl(cwd) {
|
|
|
518
567
|
return cachedPr.prUrl;
|
|
519
568
|
}
|
|
520
569
|
cachedPr = null;
|
|
570
|
+
if (persistedState.prUrl && persistedState.branch !== currentBranch) {
|
|
571
|
+
await saveEngineState({ prUrl: null });
|
|
572
|
+
}
|
|
521
573
|
try {
|
|
522
574
|
const remoteRef = execSync(`git ls-remote --heads origin ${currentBranch}`, {
|
|
523
575
|
cwd,
|
|
@@ -1126,11 +1178,10 @@ var CodexManager = class {
|
|
|
1126
1178
|
});
|
|
1127
1179
|
}
|
|
1128
1180
|
} finally {
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
}
|
|
1181
|
+
const status = await getGitStatus(this.workingDirectory);
|
|
1182
|
+
const payload = linearSessionId ? { linearSessionId, status } : { status };
|
|
1183
|
+
monolithService.sendEvent({ type: "agent_turn_complete", payload }).catch(() => {
|
|
1184
|
+
});
|
|
1134
1185
|
}
|
|
1135
1186
|
}
|
|
1136
1187
|
async getHistory() {
|
|
@@ -1153,6 +1204,38 @@ var CodexManager = class {
|
|
|
1153
1204
|
events
|
|
1154
1205
|
};
|
|
1155
1206
|
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Get paginated history from the end (bottom-up pagination).
|
|
1209
|
+
* @param limit - Maximum number of events to return
|
|
1210
|
+
* @param offset - Number of events to skip from the end
|
|
1211
|
+
* @returns Paginated history result
|
|
1212
|
+
*/
|
|
1213
|
+
async getHistoryPaginated(limit, offset = 0) {
|
|
1214
|
+
if (!this.currentThreadId) {
|
|
1215
|
+
return {
|
|
1216
|
+
thread_id: null,
|
|
1217
|
+
events: [],
|
|
1218
|
+
total: 0,
|
|
1219
|
+
hasMore: false
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
const sessionFile = await this.findSessionFile(this.currentThreadId);
|
|
1223
|
+
if (!sessionFile) {
|
|
1224
|
+
return {
|
|
1225
|
+
thread_id: this.currentThreadId,
|
|
1226
|
+
events: [],
|
|
1227
|
+
total: 0,
|
|
1228
|
+
hasMore: false
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
const result = await readJSONLPaginated(sessionFile, limit, offset);
|
|
1232
|
+
return {
|
|
1233
|
+
thread_id: this.currentThreadId,
|
|
1234
|
+
events: result.events,
|
|
1235
|
+
total: result.total,
|
|
1236
|
+
hasMore: result.hasMore
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1156
1239
|
async getStatus() {
|
|
1157
1240
|
let sessionFile = null;
|
|
1158
1241
|
if (this.currentThreadId) {
|
|
@@ -1273,6 +1356,29 @@ codex.post("/send", async (c) => {
|
|
|
1273
1356
|
});
|
|
1274
1357
|
codex.get("/history", async (c) => {
|
|
1275
1358
|
try {
|
|
1359
|
+
const limitStr = c.req.query("limit");
|
|
1360
|
+
const offsetStr = c.req.query("offset");
|
|
1361
|
+
if (limitStr !== void 0 || offsetStr !== void 0) {
|
|
1362
|
+
const limit = limitStr ? parseInt(limitStr, 10) : void 0;
|
|
1363
|
+
const offset = offsetStr ? parseInt(offsetStr, 10) : 0;
|
|
1364
|
+
if (limitStr && (isNaN(limit) || limit < 1)) {
|
|
1365
|
+
return c.json({ error: "limit must be a positive integer" }, 400);
|
|
1366
|
+
}
|
|
1367
|
+
if (isNaN(offset) || offset < 0) {
|
|
1368
|
+
return c.json({ error: "offset must be a non-negative integer" }, 400);
|
|
1369
|
+
}
|
|
1370
|
+
const history2 = await codexManager.getHistoryPaginated(limit, offset);
|
|
1371
|
+
if (!history2.thread_id) {
|
|
1372
|
+
return c.json({
|
|
1373
|
+
message: "No active thread",
|
|
1374
|
+
thread_id: null,
|
|
1375
|
+
events: [],
|
|
1376
|
+
total: 0,
|
|
1377
|
+
hasMore: false
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
return c.json(history2);
|
|
1381
|
+
}
|
|
1276
1382
|
const history = await codexManager.getHistory();
|
|
1277
1383
|
if (!history.thread_id) {
|
|
1278
1384
|
return c.json({
|
|
@@ -1544,11 +1650,10 @@ var ClaudeManager = class {
|
|
|
1544
1650
|
});
|
|
1545
1651
|
}
|
|
1546
1652
|
} finally {
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
}
|
|
1653
|
+
const status = await getGitStatus(this.workingDirectory);
|
|
1654
|
+
const payload = linearSessionId ? { linearSessionId, status } : { status };
|
|
1655
|
+
monolithService.sendEvent({ type: "agent_turn_complete", payload }).catch(() => {
|
|
1656
|
+
});
|
|
1552
1657
|
}
|
|
1553
1658
|
}
|
|
1554
1659
|
async getHistory() {
|
|
@@ -1559,6 +1664,22 @@ var ClaudeManager = class {
|
|
|
1559
1664
|
events
|
|
1560
1665
|
};
|
|
1561
1666
|
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Get paginated history from the end (bottom-up pagination).
|
|
1669
|
+
* @param limit - Maximum number of events to return
|
|
1670
|
+
* @param offset - Number of events to skip from the end
|
|
1671
|
+
* @returns Paginated history result
|
|
1672
|
+
*/
|
|
1673
|
+
async getHistoryPaginated(limit, offset = 0) {
|
|
1674
|
+
await this.initialized;
|
|
1675
|
+
const result = await readJSONLPaginated(this.historyFile, limit, offset);
|
|
1676
|
+
return {
|
|
1677
|
+
thread_id: this.sessionId,
|
|
1678
|
+
events: result.events,
|
|
1679
|
+
total: result.total,
|
|
1680
|
+
hasMore: result.hasMore
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1562
1683
|
async getStatus() {
|
|
1563
1684
|
await this.initialized;
|
|
1564
1685
|
const status = {
|
|
@@ -1645,6 +1766,29 @@ claude.post("/send", async (c) => {
|
|
|
1645
1766
|
});
|
|
1646
1767
|
claude.get("/history", async (c) => {
|
|
1647
1768
|
try {
|
|
1769
|
+
const limitStr = c.req.query("limit");
|
|
1770
|
+
const offsetStr = c.req.query("offset");
|
|
1771
|
+
if (limitStr !== void 0 || offsetStr !== void 0) {
|
|
1772
|
+
const limit = limitStr ? parseInt(limitStr, 10) : void 0;
|
|
1773
|
+
const offset = offsetStr ? parseInt(offsetStr, 10) : 0;
|
|
1774
|
+
if (limitStr && (isNaN(limit) || limit < 1)) {
|
|
1775
|
+
return c.json({ error: "limit must be a positive integer" }, 400);
|
|
1776
|
+
}
|
|
1777
|
+
if (isNaN(offset) || offset < 0) {
|
|
1778
|
+
return c.json({ error: "offset must be a non-negative integer" }, 400);
|
|
1779
|
+
}
|
|
1780
|
+
const history2 = await claudeManager.getHistoryPaginated(limit, offset);
|
|
1781
|
+
if (!history2.thread_id) {
|
|
1782
|
+
return c.json({
|
|
1783
|
+
message: "No active session",
|
|
1784
|
+
thread_id: null,
|
|
1785
|
+
events: [],
|
|
1786
|
+
total: 0,
|
|
1787
|
+
hasMore: false
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
return c.json(history2);
|
|
1791
|
+
}
|
|
1648
1792
|
const history = await claudeManager.getHistory();
|
|
1649
1793
|
if (!history.thread_id) {
|
|
1650
1794
|
return c.json({
|
|
@@ -2086,7 +2230,7 @@ async function initializeGitRepository() {
|
|
|
2086
2230
|
console.log(`[GitInit] Creating workspace branch: ${branchName}`);
|
|
2087
2231
|
runGitCommand(`git checkout -b ${branchName}`, repoPath);
|
|
2088
2232
|
initializedBranch = branchName;
|
|
2089
|
-
await saveEngineState({ branch: branchName });
|
|
2233
|
+
await saveEngineState({ branch: branchName, prUrl: null });
|
|
2090
2234
|
console.log(`[GitInit] Successfully initialized on branch: ${branchName}`);
|
|
2091
2235
|
return {
|
|
2092
2236
|
success: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replicas-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"description": "Lightweight API server for Replicas workspaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@openai/codex-sdk": "^0.50.0",
|
|
32
32
|
"dotenv": "^17.2.3",
|
|
33
33
|
"hono": "^4.10.3",
|
|
34
|
-
"zod": "3.
|
|
34
|
+
"zod": "^3.25.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^20.11.17",
|