replicas-engine 0.1.18 → 0.1.20
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 +198 -5
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import "dotenv/config";
|
|
5
5
|
import { serve } from "@hono/node-server";
|
|
6
6
|
import { Hono as Hono3 } from "hono";
|
|
7
|
-
import { readFile as
|
|
7
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
8
8
|
import { execSync as execSync2 } from "child_process";
|
|
9
9
|
|
|
10
10
|
// src/middleware/auth.ts
|
|
@@ -608,6 +608,7 @@ var CodexManager = class {
|
|
|
608
608
|
currentThread = null;
|
|
609
609
|
workingDirectory;
|
|
610
610
|
messageQueue;
|
|
611
|
+
baseSystemPrompt;
|
|
611
612
|
constructor(workingDirectory) {
|
|
612
613
|
this.codex = new Codex();
|
|
613
614
|
if (workingDirectory) {
|
|
@@ -640,6 +641,19 @@ var CodexManager = class {
|
|
|
640
641
|
getQueueStatus() {
|
|
641
642
|
return this.messageQueue.getStatus();
|
|
642
643
|
}
|
|
644
|
+
/**
|
|
645
|
+
* Set the base system prompt from replicas.json
|
|
646
|
+
* This will be combined with any custom instructions passed to individual messages
|
|
647
|
+
*/
|
|
648
|
+
setBaseSystemPrompt(prompt) {
|
|
649
|
+
this.baseSystemPrompt = prompt;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Get the current base system prompt
|
|
653
|
+
*/
|
|
654
|
+
getBaseSystemPrompt() {
|
|
655
|
+
return this.baseSystemPrompt;
|
|
656
|
+
}
|
|
643
657
|
/**
|
|
644
658
|
* Legacy sendMessage method - now uses the queue internally
|
|
645
659
|
* @deprecated Use enqueueMessage for better control over queue status
|
|
@@ -668,8 +682,16 @@ var CodexManager = class {
|
|
|
668
682
|
sandboxMode: "danger-full-access",
|
|
669
683
|
model: model || "gpt-5.1-codex"
|
|
670
684
|
});
|
|
671
|
-
|
|
672
|
-
|
|
685
|
+
let combinedInstructions;
|
|
686
|
+
if (this.baseSystemPrompt && customInstructions) {
|
|
687
|
+
combinedInstructions = `${this.baseSystemPrompt}
|
|
688
|
+
|
|
689
|
+
${customInstructions}`;
|
|
690
|
+
} else {
|
|
691
|
+
combinedInstructions = this.baseSystemPrompt || customInstructions;
|
|
692
|
+
}
|
|
693
|
+
if (combinedInstructions) {
|
|
694
|
+
message = combinedInstructions + "\n" + message;
|
|
673
695
|
}
|
|
674
696
|
const { events: events2 } = await this.currentThread.runStreamed("Hello");
|
|
675
697
|
for await (const event of events2) {
|
|
@@ -945,6 +967,7 @@ var ClaudeManager = class {
|
|
|
945
967
|
sessionId = null;
|
|
946
968
|
initialized;
|
|
947
969
|
messageQueue;
|
|
970
|
+
baseSystemPrompt;
|
|
948
971
|
constructor(workingDirectory) {
|
|
949
972
|
if (workingDirectory) {
|
|
950
973
|
this.workingDirectory = workingDirectory;
|
|
@@ -979,6 +1002,19 @@ var ClaudeManager = class {
|
|
|
979
1002
|
getQueueStatus() {
|
|
980
1003
|
return this.messageQueue.getStatus();
|
|
981
1004
|
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Set the base system prompt from replicas.json
|
|
1007
|
+
* This will be combined with any custom instructions passed to individual messages
|
|
1008
|
+
*/
|
|
1009
|
+
setBaseSystemPrompt(prompt) {
|
|
1010
|
+
this.baseSystemPrompt = prompt;
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Get the current base system prompt
|
|
1014
|
+
*/
|
|
1015
|
+
getBaseSystemPrompt() {
|
|
1016
|
+
return this.baseSystemPrompt;
|
|
1017
|
+
}
|
|
982
1018
|
/**
|
|
983
1019
|
* Legacy sendMessage method - now uses the queue internally
|
|
984
1020
|
* @deprecated Use enqueueMessage for better control over queue status
|
|
@@ -1014,6 +1050,14 @@ var ClaudeManager = class {
|
|
|
1014
1050
|
const promptIterable = (async function* () {
|
|
1015
1051
|
yield userMessage;
|
|
1016
1052
|
})();
|
|
1053
|
+
let combinedInstructions;
|
|
1054
|
+
if (this.baseSystemPrompt && customInstructions) {
|
|
1055
|
+
combinedInstructions = `${this.baseSystemPrompt}
|
|
1056
|
+
|
|
1057
|
+
${customInstructions}`;
|
|
1058
|
+
} else {
|
|
1059
|
+
combinedInstructions = this.baseSystemPrompt || customInstructions;
|
|
1060
|
+
}
|
|
1017
1061
|
const response = query({
|
|
1018
1062
|
prompt: promptIterable,
|
|
1019
1063
|
options: {
|
|
@@ -1025,7 +1069,7 @@ var ClaudeManager = class {
|
|
|
1025
1069
|
systemPrompt: {
|
|
1026
1070
|
type: "preset",
|
|
1027
1071
|
preset: "claude_code",
|
|
1028
|
-
append:
|
|
1072
|
+
append: combinedInstructions
|
|
1029
1073
|
},
|
|
1030
1074
|
env: process.env,
|
|
1031
1075
|
model: model || "opus"
|
|
@@ -1568,6 +1612,148 @@ async function initializeGitRepository() {
|
|
|
1568
1612
|
}
|
|
1569
1613
|
}
|
|
1570
1614
|
|
|
1615
|
+
// src/services/replicas-config.ts
|
|
1616
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1617
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1618
|
+
import { join as join3 } from "path";
|
|
1619
|
+
import { homedir as homedir3 } from "os";
|
|
1620
|
+
import { exec } from "child_process";
|
|
1621
|
+
import { promisify } from "util";
|
|
1622
|
+
var execAsync = promisify(exec);
|
|
1623
|
+
var ReplicasConfigService = class {
|
|
1624
|
+
config = null;
|
|
1625
|
+
workingDirectory;
|
|
1626
|
+
hooksExecuted = false;
|
|
1627
|
+
constructor() {
|
|
1628
|
+
const repoName = process.env.REPLICAS_REPO_NAME;
|
|
1629
|
+
const workspaceHome = process.env.WORKSPACE_HOME || process.env.HOME || homedir3();
|
|
1630
|
+
if (repoName) {
|
|
1631
|
+
this.workingDirectory = join3(workspaceHome, "workspaces", repoName);
|
|
1632
|
+
} else {
|
|
1633
|
+
this.workingDirectory = workspaceHome;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Initialize the service by reading replicas.json and executing start hooks
|
|
1638
|
+
*/
|
|
1639
|
+
async initialize() {
|
|
1640
|
+
await this.loadConfig();
|
|
1641
|
+
await this.executeStartHooks();
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Load and parse the replicas.json config file
|
|
1645
|
+
*/
|
|
1646
|
+
async loadConfig() {
|
|
1647
|
+
const configPath = join3(this.workingDirectory, "replicas.json");
|
|
1648
|
+
if (!existsSync2(configPath)) {
|
|
1649
|
+
console.log("No replicas.json found in workspace directory");
|
|
1650
|
+
this.config = null;
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
try {
|
|
1654
|
+
const data = await readFile2(configPath, "utf-8");
|
|
1655
|
+
const config = JSON.parse(data);
|
|
1656
|
+
if (config.copy && !Array.isArray(config.copy)) {
|
|
1657
|
+
throw new Error('Invalid replicas.json: "copy" must be an array of file paths');
|
|
1658
|
+
}
|
|
1659
|
+
if (config.ports && !Array.isArray(config.ports)) {
|
|
1660
|
+
throw new Error('Invalid replicas.json: "ports" must be an array of port numbers');
|
|
1661
|
+
}
|
|
1662
|
+
if (config.ports && !config.ports.every((p) => typeof p === "number")) {
|
|
1663
|
+
throw new Error("Invalid replicas.json: all ports must be numbers");
|
|
1664
|
+
}
|
|
1665
|
+
if (config.systemPrompt && typeof config.systemPrompt !== "string") {
|
|
1666
|
+
throw new Error('Invalid replicas.json: "systemPrompt" must be a string');
|
|
1667
|
+
}
|
|
1668
|
+
if (config.startHook) {
|
|
1669
|
+
if (typeof config.startHook !== "object" || Array.isArray(config.startHook)) {
|
|
1670
|
+
throw new Error('Invalid replicas.json: "startHook" must be an object with "commands" array');
|
|
1671
|
+
}
|
|
1672
|
+
if (!Array.isArray(config.startHook.commands)) {
|
|
1673
|
+
throw new Error('Invalid replicas.json: "startHook.commands" must be an array of shell commands');
|
|
1674
|
+
}
|
|
1675
|
+
if (!config.startHook.commands.every((cmd) => typeof cmd === "string")) {
|
|
1676
|
+
throw new Error("Invalid replicas.json: all startHook.commands entries must be strings");
|
|
1677
|
+
}
|
|
1678
|
+
if (config.startHook.timeout !== void 0 && (typeof config.startHook.timeout !== "number" || config.startHook.timeout <= 0)) {
|
|
1679
|
+
throw new Error('Invalid replicas.json: "startHook.timeout" must be a positive number');
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
this.config = config;
|
|
1683
|
+
console.log("Loaded replicas.json config:", {
|
|
1684
|
+
hasSystemPrompt: !!config.systemPrompt,
|
|
1685
|
+
startHookCount: config.startHook?.commands.length ?? 0
|
|
1686
|
+
});
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
if (error instanceof SyntaxError) {
|
|
1689
|
+
console.error("Failed to parse replicas.json:", error.message);
|
|
1690
|
+
} else if (error instanceof Error) {
|
|
1691
|
+
console.error("Error loading replicas.json:", error.message);
|
|
1692
|
+
}
|
|
1693
|
+
this.config = null;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Execute all start hooks defined in replicas.json
|
|
1698
|
+
*/
|
|
1699
|
+
async executeStartHooks() {
|
|
1700
|
+
if (this.hooksExecuted) {
|
|
1701
|
+
console.log("Start hooks already executed, skipping");
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
const startHookConfig = this.config?.startHook;
|
|
1705
|
+
if (!startHookConfig || startHookConfig.commands.length === 0) {
|
|
1706
|
+
this.hooksExecuted = true;
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
const timeout = startHookConfig.timeout ?? 3e5;
|
|
1710
|
+
const hooks = startHookConfig.commands;
|
|
1711
|
+
console.log(`Executing ${hooks.length} start hook(s) with timeout ${timeout}ms...`);
|
|
1712
|
+
for (const hook of hooks) {
|
|
1713
|
+
try {
|
|
1714
|
+
console.log(`Running start hook: ${hook}`);
|
|
1715
|
+
const { stdout, stderr } = await execAsync(hook, {
|
|
1716
|
+
cwd: this.workingDirectory,
|
|
1717
|
+
timeout,
|
|
1718
|
+
env: process.env
|
|
1719
|
+
});
|
|
1720
|
+
if (stdout) {
|
|
1721
|
+
console.log(`[${hook}] stdout:`, stdout);
|
|
1722
|
+
}
|
|
1723
|
+
if (stderr) {
|
|
1724
|
+
console.warn(`[${hook}] stderr:`, stderr);
|
|
1725
|
+
}
|
|
1726
|
+
console.log(`Start hook completed: ${hook}`);
|
|
1727
|
+
} catch (error) {
|
|
1728
|
+
if (error instanceof Error) {
|
|
1729
|
+
console.error(`Start hook failed: ${hook}`, error.message);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
this.hooksExecuted = true;
|
|
1734
|
+
console.log("All start hooks completed");
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* Get the system prompt from replicas.json
|
|
1738
|
+
*/
|
|
1739
|
+
getSystemPrompt() {
|
|
1740
|
+
return this.config?.systemPrompt;
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Get the full config object
|
|
1744
|
+
*/
|
|
1745
|
+
getConfig() {
|
|
1746
|
+
return this.config;
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Check if start hooks have been executed
|
|
1750
|
+
*/
|
|
1751
|
+
hasExecutedHooks() {
|
|
1752
|
+
return this.hooksExecuted;
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1755
|
+
var replicasConfigService = new ReplicasConfigService();
|
|
1756
|
+
|
|
1571
1757
|
// src/index.ts
|
|
1572
1758
|
var READY_MESSAGE = "========= REPLICAS WORKSPACE READY ==========";
|
|
1573
1759
|
var COMPLETION_MESSAGE = "========= REPLICAS WORKSPACE INITIALIZATION COMPLETE ==========";
|
|
@@ -1583,7 +1769,7 @@ function checkActiveSSHSessions() {
|
|
|
1583
1769
|
var app = new Hono3();
|
|
1584
1770
|
app.get("/health", async (c) => {
|
|
1585
1771
|
try {
|
|
1586
|
-
const logContent = await
|
|
1772
|
+
const logContent = await readFile3("/var/log/cloud-init-output.log", "utf-8");
|
|
1587
1773
|
let status;
|
|
1588
1774
|
if (logContent.includes(COMPLETION_MESSAGE)) {
|
|
1589
1775
|
status = "active";
|
|
@@ -1648,6 +1834,13 @@ serve(
|
|
|
1648
1834
|
} else {
|
|
1649
1835
|
console.warn(`Git initialization warning: ${gitResult.error}`);
|
|
1650
1836
|
}
|
|
1837
|
+
await replicasConfigService.initialize();
|
|
1838
|
+
const systemPrompt = replicasConfigService.getSystemPrompt();
|
|
1839
|
+
if (systemPrompt) {
|
|
1840
|
+
claudeManager.setBaseSystemPrompt(systemPrompt);
|
|
1841
|
+
codexManager.setBaseSystemPrompt(systemPrompt);
|
|
1842
|
+
console.log("Applied system prompt from replicas.json to Claude and Codex managers");
|
|
1843
|
+
}
|
|
1651
1844
|
await githubTokenManager.start();
|
|
1652
1845
|
await claudeTokenManager.start();
|
|
1653
1846
|
await codexTokenManager.start();
|