replicas-engine 0.1.27 → 0.1.29
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 +119 -28
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import "dotenv/config";
|
|
5
5
|
import { serve } from "@hono/node-server";
|
|
6
|
-
import { Hono as
|
|
7
|
-
import { readFile as
|
|
6
|
+
import { Hono as Hono4 } from "hono";
|
|
7
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
8
8
|
import { execSync as execSync2 } from "child_process";
|
|
9
9
|
|
|
10
10
|
// src/middleware/auth.ts
|
|
@@ -630,7 +630,7 @@ var MessageQueue = class {
|
|
|
630
630
|
* Add a message to the queue or start processing immediately if not busy
|
|
631
631
|
* @returns Object indicating whether the message was queued or started processing
|
|
632
632
|
*/
|
|
633
|
-
async enqueue(message, model, customInstructions, images) {
|
|
633
|
+
async enqueue(message, model, customInstructions, images, permissionMode) {
|
|
634
634
|
const messageId = this.generateMessageId();
|
|
635
635
|
const queuedMessage = {
|
|
636
636
|
id: messageId,
|
|
@@ -638,6 +638,7 @@ var MessageQueue = class {
|
|
|
638
638
|
model,
|
|
639
639
|
customInstructions,
|
|
640
640
|
images,
|
|
641
|
+
permissionMode,
|
|
641
642
|
queuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
642
643
|
};
|
|
643
644
|
if (this.processing) {
|
|
@@ -663,7 +664,8 @@ var MessageQueue = class {
|
|
|
663
664
|
queuedMessage.message,
|
|
664
665
|
queuedMessage.model,
|
|
665
666
|
queuedMessage.customInstructions,
|
|
666
|
-
queuedMessage.images
|
|
667
|
+
queuedMessage.images,
|
|
668
|
+
queuedMessage.permissionMode
|
|
667
669
|
);
|
|
668
670
|
} catch (error) {
|
|
669
671
|
console.error("[MessageQueue] Error processing message:", error);
|
|
@@ -983,7 +985,7 @@ Commands: ${hooks.length}
|
|
|
983
985
|
var replicasConfigService = new ReplicasConfigService();
|
|
984
986
|
|
|
985
987
|
// src/services/codex-manager.ts
|
|
986
|
-
var DEFAULT_MODEL = "gpt-5.
|
|
988
|
+
var DEFAULT_MODEL = "gpt-5.2-codex";
|
|
987
989
|
var CodexManager = class {
|
|
988
990
|
codex;
|
|
989
991
|
currentThreadId = null;
|
|
@@ -1025,9 +1027,9 @@ var CodexManager = class {
|
|
|
1025
1027
|
* If already processing, adds to queue.
|
|
1026
1028
|
* @returns Object with queued status, messageId, and position in queue
|
|
1027
1029
|
*/
|
|
1028
|
-
async enqueueMessage(message, model, customInstructions, images) {
|
|
1030
|
+
async enqueueMessage(message, model, customInstructions, images, permissionMode) {
|
|
1029
1031
|
await this.initialized;
|
|
1030
|
-
return this.messageQueue.enqueue(message, model, customInstructions, images);
|
|
1032
|
+
return this.messageQueue.enqueue(message, model, customInstructions, images, permissionMode);
|
|
1031
1033
|
}
|
|
1032
1034
|
/**
|
|
1033
1035
|
* Get the current queue status
|
|
@@ -1066,8 +1068,8 @@ var CodexManager = class {
|
|
|
1066
1068
|
* Legacy sendMessage method - now uses the queue internally
|
|
1067
1069
|
* @deprecated Use enqueueMessage for better control over queue status
|
|
1068
1070
|
*/
|
|
1069
|
-
async sendMessage(message, model, customInstructions, images) {
|
|
1070
|
-
await this.enqueueMessage(message, model, customInstructions, images);
|
|
1071
|
+
async sendMessage(message, model, customInstructions, images, permissionMode) {
|
|
1072
|
+
await this.enqueueMessage(message, model, customInstructions, images, permissionMode);
|
|
1071
1073
|
}
|
|
1072
1074
|
/**
|
|
1073
1075
|
* Helper method to save normalized images to temp files for Codex SDK
|
|
@@ -1089,7 +1091,7 @@ var CodexManager = class {
|
|
|
1089
1091
|
/**
|
|
1090
1092
|
* Internal method that actually processes the message
|
|
1091
1093
|
*/
|
|
1092
|
-
async processMessageInternal(message, model, customInstructions, images) {
|
|
1094
|
+
async processMessageInternal(message, model, customInstructions, images, permissionMode) {
|
|
1093
1095
|
const linearSessionId = process.env.LINEAR_SESSION_ID;
|
|
1094
1096
|
let tempImagePaths = [];
|
|
1095
1097
|
try {
|
|
@@ -1097,19 +1099,20 @@ var CodexManager = class {
|
|
|
1097
1099
|
const normalizedImages = await normalizeImages(images);
|
|
1098
1100
|
tempImagePaths = await this.saveImagesToTempFiles(normalizedImages);
|
|
1099
1101
|
}
|
|
1102
|
+
const sandboxMode = permissionMode === "read" ? "read-only" : "danger-full-access";
|
|
1100
1103
|
if (!this.currentThread) {
|
|
1101
1104
|
if (this.currentThreadId) {
|
|
1102
1105
|
this.currentThread = this.codex.resumeThread(this.currentThreadId, {
|
|
1103
1106
|
workingDirectory: this.workingDirectory,
|
|
1104
1107
|
skipGitRepoCheck: true,
|
|
1105
|
-
sandboxMode
|
|
1108
|
+
sandboxMode,
|
|
1106
1109
|
model: model || DEFAULT_MODEL
|
|
1107
1110
|
});
|
|
1108
1111
|
} else {
|
|
1109
1112
|
this.currentThread = this.codex.startThread({
|
|
1110
1113
|
workingDirectory: this.workingDirectory,
|
|
1111
1114
|
skipGitRepoCheck: true,
|
|
1112
|
-
sandboxMode
|
|
1115
|
+
sandboxMode,
|
|
1113
1116
|
model: model || DEFAULT_MODEL
|
|
1114
1117
|
});
|
|
1115
1118
|
const startHooksInstruction = this.getStartHooksInstruction();
|
|
@@ -1330,11 +1333,11 @@ var codexManager = new CodexManager();
|
|
|
1330
1333
|
codex.post("/send", async (c) => {
|
|
1331
1334
|
try {
|
|
1332
1335
|
const body = await c.req.json();
|
|
1333
|
-
const { message, model, customInstructions, images } = body;
|
|
1336
|
+
const { message, model, customInstructions, images, permissionMode } = body;
|
|
1334
1337
|
if (!message || typeof message !== "string") {
|
|
1335
1338
|
return c.json({ error: "Message is required and must be a string" }, 400);
|
|
1336
1339
|
}
|
|
1337
|
-
const result = await codexManager.enqueueMessage(message, model, customInstructions, images);
|
|
1340
|
+
const result = await codexManager.enqueueMessage(message, model, customInstructions, images, permissionMode);
|
|
1338
1341
|
const response = {
|
|
1339
1342
|
success: true,
|
|
1340
1343
|
message: result.queued ? `Message queued at position ${result.position}` : "Message sent successfully",
|
|
@@ -1509,9 +1512,9 @@ var ClaudeManager = class {
|
|
|
1509
1512
|
* If already processing, adds to queue.
|
|
1510
1513
|
* @returns Object with queued status, messageId, and position in queue
|
|
1511
1514
|
*/
|
|
1512
|
-
async enqueueMessage(message, model, customInstructions, images) {
|
|
1515
|
+
async enqueueMessage(message, model, customInstructions, images, permissionMode) {
|
|
1513
1516
|
await this.initialized;
|
|
1514
|
-
return this.messageQueue.enqueue(message, model, customInstructions, images);
|
|
1517
|
+
return this.messageQueue.enqueue(message, model, customInstructions, images, permissionMode);
|
|
1515
1518
|
}
|
|
1516
1519
|
/**
|
|
1517
1520
|
* Get the current queue status
|
|
@@ -1550,13 +1553,13 @@ var ClaudeManager = class {
|
|
|
1550
1553
|
* Legacy sendMessage method - now uses the queue internally
|
|
1551
1554
|
* @deprecated Use enqueueMessage for better control over queue status
|
|
1552
1555
|
*/
|
|
1553
|
-
async sendMessage(message, model, customInstructions, images) {
|
|
1554
|
-
await this.enqueueMessage(message, model, customInstructions, images);
|
|
1556
|
+
async sendMessage(message, model, customInstructions, images, permissionMode) {
|
|
1557
|
+
await this.enqueueMessage(message, model, customInstructions, images, permissionMode);
|
|
1555
1558
|
}
|
|
1556
1559
|
/**
|
|
1557
1560
|
* Internal method that actually processes the message
|
|
1558
1561
|
*/
|
|
1559
|
-
async processMessageInternal(message, model, customInstructions, images) {
|
|
1562
|
+
async processMessageInternal(message, model, customInstructions, images, permissionMode) {
|
|
1560
1563
|
const linearSessionId = process.env.LINEAR_SESSION_ID;
|
|
1561
1564
|
if (!message || !message.trim()) {
|
|
1562
1565
|
throw new Error("Message cannot be empty");
|
|
@@ -1612,8 +1615,8 @@ var ClaudeManager = class {
|
|
|
1612
1615
|
options: {
|
|
1613
1616
|
resume: this.sessionId || void 0,
|
|
1614
1617
|
cwd: this.workingDirectory,
|
|
1615
|
-
permissionMode: "bypassPermissions",
|
|
1616
|
-
allowDangerouslySkipPermissions:
|
|
1618
|
+
permissionMode: permissionMode === "read" ? "plan" : "bypassPermissions",
|
|
1619
|
+
allowDangerouslySkipPermissions: permissionMode !== "read",
|
|
1617
1620
|
settingSources: ["user", "project", "local"],
|
|
1618
1621
|
systemPrompt: {
|
|
1619
1622
|
type: "preset",
|
|
@@ -1740,11 +1743,11 @@ var claudeManager = new ClaudeManager();
|
|
|
1740
1743
|
claude.post("/send", async (c) => {
|
|
1741
1744
|
try {
|
|
1742
1745
|
const body = await c.req.json();
|
|
1743
|
-
const { message, model, customInstructions, images } = body;
|
|
1746
|
+
const { message, model, customInstructions, images, permissionMode } = body;
|
|
1744
1747
|
if (!message || typeof message !== "string") {
|
|
1745
1748
|
return c.json({ error: "Message is required and must be a string" }, 400);
|
|
1746
1749
|
}
|
|
1747
|
-
const result = await claudeManager.enqueueMessage(message, model, customInstructions, images);
|
|
1750
|
+
const result = await claudeManager.enqueueMessage(message, model, customInstructions, images, permissionMode);
|
|
1748
1751
|
const response = {
|
|
1749
1752
|
success: true,
|
|
1750
1753
|
message: result.queued ? `Message queued at position ${result.position}` : "Message sent successfully",
|
|
@@ -1878,6 +1881,93 @@ claude.post("/reset", async (c) => {
|
|
|
1878
1881
|
});
|
|
1879
1882
|
var claude_default = claude;
|
|
1880
1883
|
|
|
1884
|
+
// src/routes/plans.ts
|
|
1885
|
+
import { Hono as Hono3 } from "hono";
|
|
1886
|
+
|
|
1887
|
+
// src/services/plans-service.ts
|
|
1888
|
+
import { readFile as readFile4, readdir as readdir2, mkdir as mkdir5 } from "fs/promises";
|
|
1889
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1890
|
+
import { join as join5, basename } from "path";
|
|
1891
|
+
import { homedir as homedir5 } from "os";
|
|
1892
|
+
var PLANS_DIR = join5(homedir5(), ".replicas", "plans");
|
|
1893
|
+
function isValidFilename(filename) {
|
|
1894
|
+
if (!filename.endsWith(".md")) {
|
|
1895
|
+
return false;
|
|
1896
|
+
}
|
|
1897
|
+
const safePattern = /^[a-zA-Z0-9_-]+\.md$/;
|
|
1898
|
+
return safePattern.test(filename);
|
|
1899
|
+
}
|
|
1900
|
+
async function ensurePlansDir() {
|
|
1901
|
+
if (!existsSync3(PLANS_DIR)) {
|
|
1902
|
+
await mkdir5(PLANS_DIR, { recursive: true });
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
async function listPlans() {
|
|
1906
|
+
await ensurePlansDir();
|
|
1907
|
+
try {
|
|
1908
|
+
const files = await readdir2(PLANS_DIR);
|
|
1909
|
+
return files.filter((file) => file.endsWith(".md")).sort((a, b) => a.localeCompare(b));
|
|
1910
|
+
} catch {
|
|
1911
|
+
return [];
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
async function getPlanContent(filename) {
|
|
1915
|
+
if (!isValidFilename(filename)) {
|
|
1916
|
+
throw new Error("Invalid filename");
|
|
1917
|
+
}
|
|
1918
|
+
await ensurePlansDir();
|
|
1919
|
+
const filePath = join5(PLANS_DIR, basename(filename));
|
|
1920
|
+
if (!existsSync3(filePath)) {
|
|
1921
|
+
throw new Error("Plan not found");
|
|
1922
|
+
}
|
|
1923
|
+
const content = await readFile4(filePath, "utf-8");
|
|
1924
|
+
return content;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
// src/routes/plans.ts
|
|
1928
|
+
var plans = new Hono3();
|
|
1929
|
+
plans.get("/", async (c) => {
|
|
1930
|
+
try {
|
|
1931
|
+
const planFiles = await listPlans();
|
|
1932
|
+
return c.json({ plans: planFiles });
|
|
1933
|
+
} catch (error) {
|
|
1934
|
+
return c.json(
|
|
1935
|
+
{
|
|
1936
|
+
error: "Failed to list plans",
|
|
1937
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
1938
|
+
},
|
|
1939
|
+
500
|
|
1940
|
+
);
|
|
1941
|
+
}
|
|
1942
|
+
});
|
|
1943
|
+
plans.get("/:filename", async (c) => {
|
|
1944
|
+
const filename = c.req.param("filename");
|
|
1945
|
+
if (!filename) {
|
|
1946
|
+
return c.json({ error: "Filename is required" }, 400);
|
|
1947
|
+
}
|
|
1948
|
+
try {
|
|
1949
|
+
const content = await getPlanContent(filename);
|
|
1950
|
+
return c.json({ content, filename });
|
|
1951
|
+
} catch (error) {
|
|
1952
|
+
if (error instanceof Error) {
|
|
1953
|
+
if (error.message === "Invalid filename") {
|
|
1954
|
+
return c.json({ error: "Invalid filename" }, 400);
|
|
1955
|
+
}
|
|
1956
|
+
if (error.message === "Plan not found") {
|
|
1957
|
+
return c.json({ error: "Plan not found" }, 404);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
return c.json(
|
|
1961
|
+
{
|
|
1962
|
+
error: "Failed to read plan",
|
|
1963
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
1964
|
+
},
|
|
1965
|
+
500
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
});
|
|
1969
|
+
var plans_default = plans;
|
|
1970
|
+
|
|
1881
1971
|
// src/services/github-token-manager.ts
|
|
1882
1972
|
import { promises as fs } from "fs";
|
|
1883
1973
|
import path from "path";
|
|
@@ -2136,7 +2226,7 @@ var CodexTokenManager = class {
|
|
|
2136
2226
|
var codexTokenManager = new CodexTokenManager();
|
|
2137
2227
|
|
|
2138
2228
|
// src/services/git-init.ts
|
|
2139
|
-
import { existsSync as
|
|
2229
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2140
2230
|
import path4 from "path";
|
|
2141
2231
|
var initializedBranch = null;
|
|
2142
2232
|
function findAvailableBranchName(baseName, cwd) {
|
|
@@ -2172,7 +2262,7 @@ async function initializeGitRepository() {
|
|
|
2172
2262
|
};
|
|
2173
2263
|
}
|
|
2174
2264
|
const repoPath = path4.join(workspaceHome, "workspaces", repoName);
|
|
2175
|
-
if (!
|
|
2265
|
+
if (!existsSync4(repoPath)) {
|
|
2176
2266
|
console.log(`[GitInit] Repository directory does not exist: ${repoPath}`);
|
|
2177
2267
|
console.log("[GitInit] Waiting for initializer to clone the repository...");
|
|
2178
2268
|
return {
|
|
@@ -2180,7 +2270,7 @@ async function initializeGitRepository() {
|
|
|
2180
2270
|
branch: null
|
|
2181
2271
|
};
|
|
2182
2272
|
}
|
|
2183
|
-
if (!
|
|
2273
|
+
if (!existsSync4(path4.join(repoPath, ".git"))) {
|
|
2184
2274
|
return {
|
|
2185
2275
|
success: false,
|
|
2186
2276
|
branch: null,
|
|
@@ -2260,10 +2350,10 @@ function checkActiveSSHSessions() {
|
|
|
2260
2350
|
return false;
|
|
2261
2351
|
}
|
|
2262
2352
|
}
|
|
2263
|
-
var app = new
|
|
2353
|
+
var app = new Hono4();
|
|
2264
2354
|
app.get("/health", async (c) => {
|
|
2265
2355
|
try {
|
|
2266
|
-
const logContent = await
|
|
2356
|
+
const logContent = await readFile5("/var/log/cloud-init-output.log", "utf-8");
|
|
2267
2357
|
let status;
|
|
2268
2358
|
if (logContent.includes(COMPLETION_MESSAGE)) {
|
|
2269
2359
|
status = "active";
|
|
@@ -2311,6 +2401,7 @@ app.get("/status", async (c) => {
|
|
|
2311
2401
|
app.use("*", authMiddleware);
|
|
2312
2402
|
app.route("/codex", codex_default);
|
|
2313
2403
|
app.route("/claude", claude_default);
|
|
2404
|
+
app.route("/plans", plans_default);
|
|
2314
2405
|
var port = Number(process.env.PORT) || 3737;
|
|
2315
2406
|
serve(
|
|
2316
2407
|
{
|