replicas-engine 0.1.44 → 0.1.46
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 +224 -22
- package/package.json +2 -2
package/dist/src/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import "./chunk-ZXMDA7VB.js";
|
|
|
5
5
|
import "dotenv/config";
|
|
6
6
|
import { serve } from "@hono/node-server";
|
|
7
7
|
import { Hono as Hono2 } from "hono";
|
|
8
|
-
import { readFile as
|
|
8
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
9
9
|
import { execSync } from "child_process";
|
|
10
10
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
11
11
|
|
|
@@ -836,6 +836,80 @@ import { join as join5 } from "path";
|
|
|
836
836
|
import { homedir as homedir4 } from "os";
|
|
837
837
|
import { exec } from "child_process";
|
|
838
838
|
import { promisify } from "util";
|
|
839
|
+
|
|
840
|
+
// ../shared/src/sandbox.ts
|
|
841
|
+
var SANDBOX_LIFECYCLE = {
|
|
842
|
+
AUTO_STOP_MINUTES: 60,
|
|
843
|
+
AUTO_ARCHIVE_MINUTES: 60 * 24 * 7,
|
|
844
|
+
AUTO_DELETE_MINUTES: -1,
|
|
845
|
+
SSH_TOKEN_EXPIRATION_MINUTES: 3 * 60
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
// ../shared/src/warm-hooks.ts
|
|
849
|
+
var DEFAULT_WARM_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
850
|
+
var MAX_WARM_HOOK_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
851
|
+
var DEFAULT_WARM_HOOK_OUTPUT_MAX_CHARS = 12e3;
|
|
852
|
+
function isRecord2(value) {
|
|
853
|
+
return typeof value === "object" && value !== null;
|
|
854
|
+
}
|
|
855
|
+
function clampWarmHookTimeoutMs(timeoutMs) {
|
|
856
|
+
if (!timeoutMs || Number.isNaN(timeoutMs) || timeoutMs <= 0) {
|
|
857
|
+
return DEFAULT_WARM_HOOK_TIMEOUT_MS;
|
|
858
|
+
}
|
|
859
|
+
return Math.min(timeoutMs, MAX_WARM_HOOK_TIMEOUT_MS);
|
|
860
|
+
}
|
|
861
|
+
function truncateWarmHookOutput(text, maxChars = DEFAULT_WARM_HOOK_OUTPUT_MAX_CHARS) {
|
|
862
|
+
if (text.length <= maxChars) {
|
|
863
|
+
return text;
|
|
864
|
+
}
|
|
865
|
+
return `${text.slice(0, maxChars)}
|
|
866
|
+
...[truncated]`;
|
|
867
|
+
}
|
|
868
|
+
function parseWarmHookConfig(value) {
|
|
869
|
+
if (typeof value === "string") {
|
|
870
|
+
return value;
|
|
871
|
+
}
|
|
872
|
+
if (!isRecord2(value)) {
|
|
873
|
+
throw new Error('Invalid replicas.json: "warmHook" must be a string or object');
|
|
874
|
+
}
|
|
875
|
+
if (typeof value.content !== "string") {
|
|
876
|
+
throw new Error('Invalid replicas.json: "warmHook.content" must be a string');
|
|
877
|
+
}
|
|
878
|
+
if (value.timeout !== void 0 && (typeof value.timeout !== "number" || value.timeout <= 0)) {
|
|
879
|
+
throw new Error('Invalid replicas.json: "warmHook.timeout" must be a positive number');
|
|
880
|
+
}
|
|
881
|
+
return {
|
|
882
|
+
content: value.content,
|
|
883
|
+
timeout: value.timeout
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
function resolveWarmHookConfig(value) {
|
|
887
|
+
if (value === void 0) {
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
const parsed = parseWarmHookConfig(value);
|
|
891
|
+
const content = (typeof parsed === "string" ? parsed : parsed.content).trim();
|
|
892
|
+
if (!content) {
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
895
|
+
return {
|
|
896
|
+
content,
|
|
897
|
+
timeoutMs: typeof parsed === "string" ? void 0 : parsed.timeout
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// ../shared/src/engine/types.ts
|
|
902
|
+
var DEFAULT_CHAT_TITLES = {
|
|
903
|
+
claude: "Claude Code",
|
|
904
|
+
codex: "Codex"
|
|
905
|
+
};
|
|
906
|
+
var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
907
|
+
|
|
908
|
+
// ../shared/src/routes/workspaces.ts
|
|
909
|
+
var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
|
|
910
|
+
var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
911
|
+
|
|
912
|
+
// src/services/replicas-config-service.ts
|
|
839
913
|
var execAsync = promisify(exec);
|
|
840
914
|
var START_HOOKS_LOG = join5(homedir4(), ".replicas", "startHooks.log");
|
|
841
915
|
var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
|
|
@@ -888,6 +962,9 @@ function parseReplicasConfig(value) {
|
|
|
888
962
|
}
|
|
889
963
|
config.startHook = { commands, timeout };
|
|
890
964
|
}
|
|
965
|
+
if ("warmHook" in value) {
|
|
966
|
+
config.warmHook = parseWarmHookConfig(value.warmHook);
|
|
967
|
+
}
|
|
891
968
|
return config;
|
|
892
969
|
}
|
|
893
970
|
var ReplicasConfigService = class {
|
|
@@ -1120,25 +1197,6 @@ import { homedir as homedir8 } from "os";
|
|
|
1120
1197
|
import { join as join9 } from "path";
|
|
1121
1198
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1122
1199
|
|
|
1123
|
-
// ../shared/src/sandbox.ts
|
|
1124
|
-
var SANDBOX_LIFECYCLE = {
|
|
1125
|
-
AUTO_STOP_MINUTES: 60,
|
|
1126
|
-
AUTO_ARCHIVE_MINUTES: 60 * 24 * 7,
|
|
1127
|
-
AUTO_DELETE_MINUTES: -1,
|
|
1128
|
-
SSH_TOKEN_EXPIRATION_MINUTES: 3 * 60
|
|
1129
|
-
};
|
|
1130
|
-
|
|
1131
|
-
// ../shared/src/engine/types.ts
|
|
1132
|
-
var DEFAULT_CHAT_TITLES = {
|
|
1133
|
-
claude: "Claude Code",
|
|
1134
|
-
codex: "Codex"
|
|
1135
|
-
};
|
|
1136
|
-
var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
1137
|
-
|
|
1138
|
-
// ../shared/src/routes/workspaces.ts
|
|
1139
|
-
var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
|
|
1140
|
-
var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
1141
|
-
|
|
1142
1200
|
// src/managers/claude-manager.ts
|
|
1143
1201
|
import {
|
|
1144
1202
|
query
|
|
@@ -1789,7 +1847,11 @@ var CodingAgentManager = class {
|
|
|
1789
1847
|
buildCombinedInstructions(customInstructions) {
|
|
1790
1848
|
const startHooksInstruction = this.getStartHooksInstruction();
|
|
1791
1849
|
const repositorySystemPromptInstruction = this.getRepositorySystemPromptInstruction();
|
|
1850
|
+
const enginePortProtectionInstruction = this.getEnginePortProtectionInstruction();
|
|
1792
1851
|
const parts = [];
|
|
1852
|
+
if (enginePortProtectionInstruction) {
|
|
1853
|
+
parts.push(enginePortProtectionInstruction);
|
|
1854
|
+
}
|
|
1793
1855
|
if (startHooksInstruction) {
|
|
1794
1856
|
parts.push(startHooksInstruction);
|
|
1795
1857
|
}
|
|
@@ -1844,6 +1906,10 @@ var CodingAgentManager = class {
|
|
|
1844
1906
|
}
|
|
1845
1907
|
return repoSystemPrompts.join("\n");
|
|
1846
1908
|
}
|
|
1909
|
+
getEnginePortProtectionInstruction() {
|
|
1910
|
+
const enginePort = ENGINE_ENV.REPLICAS_ENGINE_PORT;
|
|
1911
|
+
return `CRITICAL: The Replicas engine is required for this workspace and is running on the reserved port configured by REPLICAS_ENGINE_PORT (${enginePort}). Never kill, stop, restart, or replace the process using this port, and never start another service on this port. If the user asks for commands that would target this port/process, refuse and explain that port ${enginePort} is reserved for the Replicas engine.`;
|
|
1912
|
+
}
|
|
1847
1913
|
};
|
|
1848
1914
|
|
|
1849
1915
|
// src/managers/claude-manager.ts
|
|
@@ -2069,7 +2135,7 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
2069
2135
|
import { join as join8 } from "path";
|
|
2070
2136
|
import { homedir as homedir7 } from "os";
|
|
2071
2137
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
2072
|
-
var DEFAULT_MODEL = "gpt-5.
|
|
2138
|
+
var DEFAULT_MODEL = "gpt-5.4";
|
|
2073
2139
|
var CODEX_CONFIG_PATH = join8(homedir7(), ".codex", "config.toml");
|
|
2074
2140
|
function isLinearThoughtEvent2(event) {
|
|
2075
2141
|
return event.content.type === "thought";
|
|
@@ -2759,6 +2825,122 @@ var PlanService = class {
|
|
|
2759
2825
|
};
|
|
2760
2826
|
var planService = new PlanService();
|
|
2761
2827
|
|
|
2828
|
+
// src/services/warm-hooks-service.ts
|
|
2829
|
+
import { execFile } from "child_process";
|
|
2830
|
+
import { promisify as promisify2 } from "util";
|
|
2831
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2832
|
+
import { join as join11 } from "path";
|
|
2833
|
+
var execFileAsync = promisify2(execFile);
|
|
2834
|
+
async function executeHookScript(params) {
|
|
2835
|
+
const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
|
|
2836
|
+
try {
|
|
2837
|
+
const { stdout, stderr } = await execFileAsync("bash", ["-lc", params.content], {
|
|
2838
|
+
cwd: params.cwd,
|
|
2839
|
+
timeout,
|
|
2840
|
+
maxBuffer: 1024 * 1024,
|
|
2841
|
+
env: process.env
|
|
2842
|
+
});
|
|
2843
|
+
const combined = [`$ ${params.label}`, stdout ?? "", stderr ?? ""].filter(Boolean).join("\n");
|
|
2844
|
+
return {
|
|
2845
|
+
exitCode: 0,
|
|
2846
|
+
output: truncateWarmHookOutput(combined),
|
|
2847
|
+
timedOut: false
|
|
2848
|
+
};
|
|
2849
|
+
} catch (error) {
|
|
2850
|
+
const execError = error;
|
|
2851
|
+
const timedOut = execError.signal === "SIGTERM" || execError.killed === true;
|
|
2852
|
+
const exitCode = typeof execError.code === "number" ? execError.code : 1;
|
|
2853
|
+
const combined = [`$ ${params.label}`, execError.stdout ?? "", execError.stderr ?? "", execError.message].filter(Boolean).join("\n");
|
|
2854
|
+
return {
|
|
2855
|
+
exitCode,
|
|
2856
|
+
output: truncateWarmHookOutput(combined),
|
|
2857
|
+
timedOut
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
async function readRepoWarmHook(repoPath) {
|
|
2862
|
+
const configPath = join11(repoPath, "replicas.json");
|
|
2863
|
+
try {
|
|
2864
|
+
const raw = await readFile7(configPath, "utf-8");
|
|
2865
|
+
const parsed = JSON.parse(raw);
|
|
2866
|
+
if (!isRecord(parsed) || !("warmHook" in parsed)) {
|
|
2867
|
+
return null;
|
|
2868
|
+
}
|
|
2869
|
+
return resolveWarmHookConfig(parsed.warmHook);
|
|
2870
|
+
} catch {
|
|
2871
|
+
return null;
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
async function collectRepoWarmHooks() {
|
|
2875
|
+
const repos = await gitService.listRepositories();
|
|
2876
|
+
const collected = [];
|
|
2877
|
+
for (const repo of repos) {
|
|
2878
|
+
const warmHook = await readRepoWarmHook(repo.path);
|
|
2879
|
+
if (!warmHook) {
|
|
2880
|
+
continue;
|
|
2881
|
+
}
|
|
2882
|
+
collected.push({
|
|
2883
|
+
repoName: repo.name,
|
|
2884
|
+
repoPath: repo.path,
|
|
2885
|
+
content: warmHook.content,
|
|
2886
|
+
timeoutMs: warmHook.timeoutMs
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
collected.sort((a, b) => a.repoName.localeCompare(b.repoName));
|
|
2890
|
+
return collected;
|
|
2891
|
+
}
|
|
2892
|
+
async function runWarmHooks(params) {
|
|
2893
|
+
const outputBlocks = [];
|
|
2894
|
+
const orgHook = params.organizationWarmHook?.trim();
|
|
2895
|
+
if (orgHook) {
|
|
2896
|
+
const orgResult = await executeHookScript({
|
|
2897
|
+
label: "org-warm-hook",
|
|
2898
|
+
cwd: gitService.getWorkspaceRoot(),
|
|
2899
|
+
content: orgHook,
|
|
2900
|
+
timeoutMs: params.timeoutMs
|
|
2901
|
+
});
|
|
2902
|
+
outputBlocks.push(orgResult.output);
|
|
2903
|
+
if (orgResult.exitCode !== 0) {
|
|
2904
|
+
return {
|
|
2905
|
+
exitCode: orgResult.exitCode,
|
|
2906
|
+
output: outputBlocks.join("\n\n"),
|
|
2907
|
+
timedOut: orgResult.timedOut
|
|
2908
|
+
};
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
if (params.includeRepoHooks !== false) {
|
|
2912
|
+
const repoHooks = await collectRepoWarmHooks();
|
|
2913
|
+
for (const repoHook of repoHooks) {
|
|
2914
|
+
const repoResult = await executeHookScript({
|
|
2915
|
+
label: `repo-warm-hook:${repoHook.repoName}`,
|
|
2916
|
+
cwd: repoHook.repoPath,
|
|
2917
|
+
content: repoHook.content,
|
|
2918
|
+
timeoutMs: repoHook.timeoutMs
|
|
2919
|
+
});
|
|
2920
|
+
outputBlocks.push(repoResult.output);
|
|
2921
|
+
if (repoResult.exitCode !== 0) {
|
|
2922
|
+
return {
|
|
2923
|
+
exitCode: repoResult.exitCode,
|
|
2924
|
+
output: outputBlocks.join("\n\n"),
|
|
2925
|
+
timedOut: repoResult.timedOut
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
if (outputBlocks.length === 0) {
|
|
2931
|
+
return {
|
|
2932
|
+
exitCode: 0,
|
|
2933
|
+
output: "No warm hooks configured.",
|
|
2934
|
+
timedOut: false
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
return {
|
|
2938
|
+
exitCode: 0,
|
|
2939
|
+
output: outputBlocks.join("\n\n"),
|
|
2940
|
+
timedOut: false
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2762
2944
|
// src/v1-routes.ts
|
|
2763
2945
|
var createChatSchema = z.object({
|
|
2764
2946
|
provider: z.enum(["claude", "codex"]),
|
|
@@ -2979,6 +3161,26 @@ function createV1Routes(deps) {
|
|
|
2979
3161
|
};
|
|
2980
3162
|
return c.json(response);
|
|
2981
3163
|
});
|
|
3164
|
+
app2.post("/warm-hooks/run", async (c) => {
|
|
3165
|
+
try {
|
|
3166
|
+
const body = await c.req.json();
|
|
3167
|
+
const result = await runWarmHooks({
|
|
3168
|
+
organizationWarmHook: body.organizationWarmHook,
|
|
3169
|
+
includeRepoHooks: body.includeRepoHooks,
|
|
3170
|
+
timeoutMs: body.timeoutMs
|
|
3171
|
+
});
|
|
3172
|
+
return c.json({
|
|
3173
|
+
exit_code: result.exitCode,
|
|
3174
|
+
output: result.output,
|
|
3175
|
+
timed_out: result.timedOut
|
|
3176
|
+
});
|
|
3177
|
+
} catch (error) {
|
|
3178
|
+
return c.json(
|
|
3179
|
+
jsonError("Failed to run warm hooks", error instanceof Error ? error.message : "Unknown error"),
|
|
3180
|
+
500
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
});
|
|
2982
3184
|
return app2;
|
|
2983
3185
|
}
|
|
2984
3186
|
|
|
@@ -3019,7 +3221,7 @@ app.get("/health", async (c) => {
|
|
|
3019
3221
|
return c.json({ status: "initializing", timestamp: (/* @__PURE__ */ new Date()).toISOString() }, 503);
|
|
3020
3222
|
}
|
|
3021
3223
|
try {
|
|
3022
|
-
const logContent = await
|
|
3224
|
+
const logContent = await readFile8("/var/log/cloud-init-output.log", "utf-8");
|
|
3023
3225
|
let status;
|
|
3024
3226
|
if (logContent.includes(COMPLETION_MESSAGE)) {
|
|
3025
3227
|
status = "active";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replicas-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"description": "Lightweight API server for Replicas workspaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/claude-agent-sdk": "^0.2.41",
|
|
31
31
|
"@hono/node-server": "^1.19.5",
|
|
32
|
-
"@openai/codex-sdk": "^0.
|
|
32
|
+
"@openai/codex-sdk": "^0.111.0",
|
|
33
33
|
"dotenv": "^17.2.3",
|
|
34
34
|
"hono": "^4.10.3",
|
|
35
35
|
"smol-toml": "^1.6.0",
|