replicas-engine 0.1.49 → 0.1.50
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 +283 -71
- package/package.json +1 -1
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 readFile9 } from "fs/promises";
|
|
9
9
|
import { execSync } from "child_process";
|
|
10
10
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
11
11
|
|
|
@@ -829,13 +829,11 @@ var EngineLogger = class {
|
|
|
829
829
|
};
|
|
830
830
|
var engineLogger = new EngineLogger();
|
|
831
831
|
|
|
832
|
-
// src/services/
|
|
833
|
-
import {
|
|
832
|
+
// src/services/environment-details-service.ts
|
|
833
|
+
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
834
834
|
import { existsSync as existsSync3 } from "fs";
|
|
835
|
-
import { join as join5 } from "path";
|
|
836
835
|
import { homedir as homedir4 } from "os";
|
|
837
|
-
import {
|
|
838
|
-
import { promisify } from "util";
|
|
836
|
+
import { join as join5 } from "path";
|
|
839
837
|
|
|
840
838
|
// ../shared/src/sandbox.ts
|
|
841
839
|
var SANDBOX_LIFECYCLE = {
|
|
@@ -898,6 +896,9 @@ function resolveWarmHookConfig(value) {
|
|
|
898
896
|
};
|
|
899
897
|
}
|
|
900
898
|
|
|
899
|
+
// ../shared/src/engine/environment.ts
|
|
900
|
+
var REPLICAS_ENGINE_VERSION = "09-03-2026-kipling";
|
|
901
|
+
|
|
901
902
|
// ../shared/src/engine/types.ts
|
|
902
903
|
var DEFAULT_CHAT_TITLES = {
|
|
903
904
|
claude: "Claude Code",
|
|
@@ -909,9 +910,151 @@ var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
|
909
910
|
var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
|
|
910
911
|
var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
911
912
|
|
|
913
|
+
// src/services/environment-details-service.ts
|
|
914
|
+
var REPLICAS_DIR = join5(homedir4(), ".replicas");
|
|
915
|
+
var ENVIRONMENT_DETAILS_FILE = join5(REPLICAS_DIR, "environment-details.json");
|
|
916
|
+
function createExecutionItem(status, details) {
|
|
917
|
+
return { status, details: details ?? null };
|
|
918
|
+
}
|
|
919
|
+
function createDefaultDetails() {
|
|
920
|
+
return {
|
|
921
|
+
engineVersion: REPLICAS_ENGINE_VERSION,
|
|
922
|
+
globalWarmHookCompleted: createExecutionItem("n/a"),
|
|
923
|
+
repositories: [],
|
|
924
|
+
filesUploaded: [],
|
|
925
|
+
envVarsSet: [],
|
|
926
|
+
skillsInstalled: [],
|
|
927
|
+
runtimeEnvVarsSet: [],
|
|
928
|
+
repositoriesCloned: [],
|
|
929
|
+
gitIdentityConfigured: false,
|
|
930
|
+
githubCredentialsConfigured: false,
|
|
931
|
+
codexAuthConfigured: false,
|
|
932
|
+
claudeAuthConfigured: false,
|
|
933
|
+
bedrockAuthConfigured: false,
|
|
934
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
function mergeUnique(current, incoming) {
|
|
938
|
+
if (!incoming || incoming.length === 0) {
|
|
939
|
+
return current;
|
|
940
|
+
}
|
|
941
|
+
return Array.from(/* @__PURE__ */ new Set([...current, ...incoming]));
|
|
942
|
+
}
|
|
943
|
+
function upsertRepositoryStatus(current, incoming) {
|
|
944
|
+
const byName = new Map(current.map((repository) => [repository.repositoryName, repository]));
|
|
945
|
+
for (const repository of incoming) {
|
|
946
|
+
const existing = byName.get(repository.repositoryName);
|
|
947
|
+
byName.set(repository.repositoryName, {
|
|
948
|
+
repositoryName: repository.repositoryName,
|
|
949
|
+
warmHookCompleted: repository.warmHookCompleted !== "n/a" ? repository.warmHookCompleted : existing?.warmHookCompleted ?? "n/a",
|
|
950
|
+
startHookCompleted: repository.startHookCompleted !== "n/a" ? repository.startHookCompleted : existing?.startHookCompleted ?? "n/a"
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
return [...byName.values()].sort((a, b) => a.repositoryName.localeCompare(b.repositoryName));
|
|
954
|
+
}
|
|
955
|
+
var EnvironmentDetailsService = class {
|
|
956
|
+
async initialize() {
|
|
957
|
+
const current = await this.readDetails();
|
|
958
|
+
const repositories = await gitService.listRepositories();
|
|
959
|
+
const merged = upsertRepositoryStatus(
|
|
960
|
+
current.repositories,
|
|
961
|
+
repositories.map((repository) => ({
|
|
962
|
+
repositoryName: repository.name,
|
|
963
|
+
warmHookCompleted: "n/a",
|
|
964
|
+
startHookCompleted: "n/a"
|
|
965
|
+
}))
|
|
966
|
+
);
|
|
967
|
+
await this.writeDetails({
|
|
968
|
+
...current,
|
|
969
|
+
engineVersion: REPLICAS_ENGINE_VERSION,
|
|
970
|
+
repositories: merged,
|
|
971
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
async getDetails() {
|
|
975
|
+
const current = await this.readDetails();
|
|
976
|
+
return {
|
|
977
|
+
...current,
|
|
978
|
+
engineVersion: REPLICAS_ENGINE_VERSION
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
async track(update) {
|
|
982
|
+
const current = await this.readDetails();
|
|
983
|
+
const next = {
|
|
984
|
+
...current,
|
|
985
|
+
engineVersion: REPLICAS_ENGINE_VERSION,
|
|
986
|
+
globalWarmHookCompleted: update.globalWarmHookCompleted ?? current.globalWarmHookCompleted,
|
|
987
|
+
repositories: update.repositories ? upsertRepositoryStatus(current.repositories, update.repositories) : current.repositories,
|
|
988
|
+
filesUploaded: mergeUnique(current.filesUploaded, update.filesUploaded),
|
|
989
|
+
envVarsSet: mergeUnique(current.envVarsSet, update.envVarsSet),
|
|
990
|
+
skillsInstalled: mergeUnique(current.skillsInstalled, update.skillsInstalled),
|
|
991
|
+
runtimeEnvVarsSet: mergeUnique(current.runtimeEnvVarsSet, update.runtimeEnvVarsSet),
|
|
992
|
+
repositoriesCloned: mergeUnique(current.repositoriesCloned, update.repositoriesCloned),
|
|
993
|
+
gitIdentityConfigured: update.gitIdentityConfigured ?? current.gitIdentityConfigured,
|
|
994
|
+
githubCredentialsConfigured: update.githubCredentialsConfigured ?? current.githubCredentialsConfigured,
|
|
995
|
+
codexAuthConfigured: update.codexAuthConfigured ?? current.codexAuthConfigured,
|
|
996
|
+
claudeAuthConfigured: update.claudeAuthConfigured ?? current.claudeAuthConfigured,
|
|
997
|
+
bedrockAuthConfigured: update.bedrockAuthConfigured ?? current.bedrockAuthConfigured,
|
|
998
|
+
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
999
|
+
};
|
|
1000
|
+
await this.writeDetails(next);
|
|
1001
|
+
return next;
|
|
1002
|
+
}
|
|
1003
|
+
async setGlobalWarmHook(status, details) {
|
|
1004
|
+
await this.track({
|
|
1005
|
+
globalWarmHookCompleted: createExecutionItem(status, details)
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
async setRepositoryWarmHook(repositoryName, status) {
|
|
1009
|
+
await this.track({
|
|
1010
|
+
repositories: [{
|
|
1011
|
+
repositoryName,
|
|
1012
|
+
warmHookCompleted: status,
|
|
1013
|
+
startHookCompleted: "n/a"
|
|
1014
|
+
}]
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
async setRepositoryStartHook(repositoryName, status) {
|
|
1018
|
+
await this.track({
|
|
1019
|
+
repositories: [{
|
|
1020
|
+
repositoryName,
|
|
1021
|
+
warmHookCompleted: "n/a",
|
|
1022
|
+
startHookCompleted: status
|
|
1023
|
+
}]
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
async readDetails() {
|
|
1027
|
+
try {
|
|
1028
|
+
if (!existsSync3(ENVIRONMENT_DETAILS_FILE)) {
|
|
1029
|
+
return createDefaultDetails();
|
|
1030
|
+
}
|
|
1031
|
+
const raw = await readFile2(ENVIRONMENT_DETAILS_FILE, "utf-8");
|
|
1032
|
+
return {
|
|
1033
|
+
...createDefaultDetails(),
|
|
1034
|
+
...JSON.parse(raw),
|
|
1035
|
+
engineVersion: REPLICAS_ENGINE_VERSION
|
|
1036
|
+
};
|
|
1037
|
+
} catch {
|
|
1038
|
+
return createDefaultDetails();
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
async writeDetails(details) {
|
|
1042
|
+
await mkdir3(REPLICAS_DIR, { recursive: true });
|
|
1043
|
+
await writeFile3(ENVIRONMENT_DETAILS_FILE, `${JSON.stringify(details, null, 2)}
|
|
1044
|
+
`, "utf-8");
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
var environmentDetailsService = new EnvironmentDetailsService();
|
|
1048
|
+
|
|
912
1049
|
// src/services/replicas-config-service.ts
|
|
1050
|
+
import { readFile as readFile3, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1051
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1052
|
+
import { join as join6 } from "path";
|
|
1053
|
+
import { homedir as homedir5 } from "os";
|
|
1054
|
+
import { exec } from "child_process";
|
|
1055
|
+
import { promisify } from "util";
|
|
913
1056
|
var execAsync = promisify(exec);
|
|
914
|
-
var START_HOOKS_LOG =
|
|
1057
|
+
var START_HOOKS_LOG = join6(homedir5(), ".replicas", "startHooks.log");
|
|
915
1058
|
var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
|
|
916
1059
|
Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
|
|
917
1060
|
These hooks are currently executing in the background. You can:
|
|
@@ -991,12 +1134,12 @@ var ReplicasConfigService = class {
|
|
|
991
1134
|
const repos = await gitService.listRepositories();
|
|
992
1135
|
const configs = [];
|
|
993
1136
|
for (const repo of repos) {
|
|
994
|
-
const configPath =
|
|
995
|
-
if (!
|
|
1137
|
+
const configPath = join6(repo.path, "replicas.json");
|
|
1138
|
+
if (!existsSync4(configPath)) {
|
|
996
1139
|
continue;
|
|
997
1140
|
}
|
|
998
1141
|
try {
|
|
999
|
-
const data = await
|
|
1142
|
+
const data = await readFile3(configPath, "utf-8");
|
|
1000
1143
|
const config = parseReplicasConfig(JSON.parse(data));
|
|
1001
1144
|
configs.push({
|
|
1002
1145
|
repoName: repo.name,
|
|
@@ -1022,7 +1165,7 @@ var ReplicasConfigService = class {
|
|
|
1022
1165
|
const logLine = `[${timestamp}] ${message}
|
|
1023
1166
|
`;
|
|
1024
1167
|
try {
|
|
1025
|
-
await
|
|
1168
|
+
await mkdir4(join6(homedir5(), ".replicas"), { recursive: true });
|
|
1026
1169
|
await appendFile2(START_HOOKS_LOG, logLine, "utf-8");
|
|
1027
1170
|
} catch (error) {
|
|
1028
1171
|
console.error("Failed to write to start hooks log:", error);
|
|
@@ -1037,13 +1180,17 @@ var ReplicasConfigService = class {
|
|
|
1037
1180
|
this.hooksRunning = false;
|
|
1038
1181
|
this.hooksCompleted = true;
|
|
1039
1182
|
this.hooksFailed = false;
|
|
1183
|
+
const repos = await gitService.listRepositories();
|
|
1184
|
+
for (const repo of repos) {
|
|
1185
|
+
await environmentDetailsService.setRepositoryStartHook(repo.name, "n/a");
|
|
1186
|
+
}
|
|
1040
1187
|
return;
|
|
1041
1188
|
}
|
|
1042
1189
|
this.hooksRunning = true;
|
|
1043
1190
|
this.hooksCompleted = false;
|
|
1044
1191
|
try {
|
|
1045
|
-
await
|
|
1046
|
-
await
|
|
1192
|
+
await mkdir4(join6(homedir5(), ".replicas"), { recursive: true });
|
|
1193
|
+
await writeFile4(
|
|
1047
1194
|
START_HOOKS_LOG,
|
|
1048
1195
|
`=== Start Hooks Execution Log ===
|
|
1049
1196
|
Started: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
@@ -1052,6 +1199,13 @@ Repositories: ${hookEntries.length}
|
|
|
1052
1199
|
`,
|
|
1053
1200
|
"utf-8"
|
|
1054
1201
|
);
|
|
1202
|
+
const repos = await gitService.listRepositories();
|
|
1203
|
+
const reposWithHooks = new Set(hookEntries.map((entry) => entry.repoName));
|
|
1204
|
+
for (const repo of repos) {
|
|
1205
|
+
if (!reposWithHooks.has(repo.name)) {
|
|
1206
|
+
await environmentDetailsService.setRepositoryStartHook(repo.name, "n/a");
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1055
1209
|
for (const entry of hookEntries) {
|
|
1056
1210
|
const startHookConfig = entry.config.startHook;
|
|
1057
1211
|
if (!startHookConfig) {
|
|
@@ -1060,9 +1214,11 @@ Repositories: ${hookEntries.length}
|
|
|
1060
1214
|
const persistedRepoState = await loadRepoState(entry.repoName);
|
|
1061
1215
|
if (persistedRepoState?.startHooksCompleted) {
|
|
1062
1216
|
await this.logToFile(`[${entry.repoName}] Start hooks already completed in this workspace lifecycle, skipping`);
|
|
1217
|
+
await environmentDetailsService.setRepositoryStartHook(entry.repoName, "yes");
|
|
1063
1218
|
continue;
|
|
1064
1219
|
}
|
|
1065
1220
|
const timeout = startHookConfig.timeout ?? 3e5;
|
|
1221
|
+
let repoFailed = false;
|
|
1066
1222
|
await this.logToFile(`[${entry.repoName}] Executing ${startHookConfig.commands.length} hook(s) with timeout ${timeout}ms`);
|
|
1067
1223
|
for (const hook of startHookConfig.commands) {
|
|
1068
1224
|
try {
|
|
@@ -1082,7 +1238,9 @@ Repositories: ${hookEntries.length}
|
|
|
1082
1238
|
} catch (error) {
|
|
1083
1239
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1084
1240
|
this.hooksFailed = true;
|
|
1241
|
+
repoFailed = true;
|
|
1085
1242
|
await this.logToFile(`[${entry.repoName}] [ERROR] ${hook} failed: ${errorMessage}`);
|
|
1243
|
+
await environmentDetailsService.setRepositoryStartHook(entry.repoName, "no");
|
|
1086
1244
|
}
|
|
1087
1245
|
}
|
|
1088
1246
|
const fallbackRepoState = persistedRepoState ?? {
|
|
@@ -1095,6 +1253,7 @@ Repositories: ${hookEntries.length}
|
|
|
1095
1253
|
startHooksCompleted: false
|
|
1096
1254
|
};
|
|
1097
1255
|
await saveRepoState(entry.repoName, { startHooksCompleted: true }, fallbackRepoState);
|
|
1256
|
+
await environmentDetailsService.setRepositoryStartHook(entry.repoName, repoFailed ? "no" : "yes");
|
|
1098
1257
|
}
|
|
1099
1258
|
this.hooksCompleted = true;
|
|
1100
1259
|
await this.logToFile(`=== All start hooks completed at ${(/* @__PURE__ */ new Date()).toISOString()} ===`);
|
|
@@ -1152,17 +1311,17 @@ Repositories: ${hookEntries.length}
|
|
|
1152
1311
|
var replicasConfigService = new ReplicasConfigService();
|
|
1153
1312
|
|
|
1154
1313
|
// src/services/event-service.ts
|
|
1155
|
-
import { appendFile as appendFile3, mkdir as
|
|
1156
|
-
import { homedir as
|
|
1157
|
-
import { join as
|
|
1314
|
+
import { appendFile as appendFile3, mkdir as mkdir5 } from "fs/promises";
|
|
1315
|
+
import { homedir as homedir6 } from "os";
|
|
1316
|
+
import { join as join7 } from "path";
|
|
1158
1317
|
import { randomUUID } from "crypto";
|
|
1159
|
-
var ENGINE_DIR =
|
|
1160
|
-
var EVENTS_FILE =
|
|
1318
|
+
var ENGINE_DIR = join7(homedir6(), ".replicas", "engine");
|
|
1319
|
+
var EVENTS_FILE = join7(ENGINE_DIR, "events.jsonl");
|
|
1161
1320
|
var EventService = class {
|
|
1162
1321
|
subscribers = /* @__PURE__ */ new Map();
|
|
1163
1322
|
writeChain = Promise.resolve();
|
|
1164
1323
|
async initialize() {
|
|
1165
|
-
await
|
|
1324
|
+
await mkdir5(ENGINE_DIR, { recursive: true });
|
|
1166
1325
|
}
|
|
1167
1326
|
subscribe(subscriber) {
|
|
1168
1327
|
const id = randomUUID();
|
|
@@ -1192,21 +1351,21 @@ var EventService = class {
|
|
|
1192
1351
|
var eventService = new EventService();
|
|
1193
1352
|
|
|
1194
1353
|
// src/services/chat/chat-service.ts
|
|
1195
|
-
import { mkdir as
|
|
1196
|
-
import { homedir as
|
|
1197
|
-
import { join as
|
|
1354
|
+
import { mkdir as mkdir8, readFile as readFile6, rm, writeFile as writeFile6 } from "fs/promises";
|
|
1355
|
+
import { homedir as homedir9 } from "os";
|
|
1356
|
+
import { join as join10 } from "path";
|
|
1198
1357
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1199
1358
|
|
|
1200
1359
|
// src/managers/claude-manager.ts
|
|
1201
1360
|
import {
|
|
1202
1361
|
query
|
|
1203
1362
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
1204
|
-
import { join as
|
|
1205
|
-
import { mkdir as
|
|
1206
|
-
import { homedir as
|
|
1363
|
+
import { join as join8 } from "path";
|
|
1364
|
+
import { mkdir as mkdir6, appendFile as appendFile4 } from "fs/promises";
|
|
1365
|
+
import { homedir as homedir7 } from "os";
|
|
1207
1366
|
|
|
1208
1367
|
// src/utils/jsonl-reader.ts
|
|
1209
|
-
import { readFile as
|
|
1368
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1210
1369
|
function isJsonlEvent(value) {
|
|
1211
1370
|
if (!isRecord(value)) {
|
|
1212
1371
|
return false;
|
|
@@ -1228,7 +1387,7 @@ function parseJsonlEvents(lines) {
|
|
|
1228
1387
|
}
|
|
1229
1388
|
async function readJSONL(filePath) {
|
|
1230
1389
|
try {
|
|
1231
|
-
const content = await
|
|
1390
|
+
const content = await readFile4(filePath, "utf-8");
|
|
1232
1391
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
1233
1392
|
return parseJsonlEvents(lines);
|
|
1234
1393
|
} catch (error) {
|
|
@@ -1962,7 +2121,7 @@ var ClaudeManager = class extends CodingAgentManager {
|
|
|
1962
2121
|
pendingInterrupt = false;
|
|
1963
2122
|
constructor(options) {
|
|
1964
2123
|
super(options);
|
|
1965
|
-
this.historyFile = options.historyFilePath ??
|
|
2124
|
+
this.historyFile = options.historyFilePath ?? join8(homedir7(), ".replicas", "claude", "history.jsonl");
|
|
1966
2125
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
1967
2126
|
}
|
|
1968
2127
|
async interruptActiveTurn() {
|
|
@@ -2100,8 +2259,8 @@ var ClaudeManager = class extends CodingAgentManager {
|
|
|
2100
2259
|
};
|
|
2101
2260
|
}
|
|
2102
2261
|
async initialize() {
|
|
2103
|
-
const historyDir =
|
|
2104
|
-
await
|
|
2262
|
+
const historyDir = join8(homedir7(), ".replicas", "claude");
|
|
2263
|
+
await mkdir6(historyDir, { recursive: true });
|
|
2105
2264
|
if (this.initialSessionId) {
|
|
2106
2265
|
this.sessionId = this.initialSessionId;
|
|
2107
2266
|
console.log(`[ClaudeManager] Restored session ID from persisted state: ${this.sessionId}`);
|
|
@@ -2130,13 +2289,13 @@ var ClaudeManager = class extends CodingAgentManager {
|
|
|
2130
2289
|
// src/managers/codex-manager.ts
|
|
2131
2290
|
import { Codex } from "@openai/codex-sdk";
|
|
2132
2291
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2133
|
-
import { readdir as readdir2, stat as stat2, writeFile as
|
|
2134
|
-
import { existsSync as
|
|
2135
|
-
import { join as
|
|
2136
|
-
import { homedir as
|
|
2292
|
+
import { readdir as readdir2, stat as stat2, writeFile as writeFile5, mkdir as mkdir7, readFile as readFile5 } from "fs/promises";
|
|
2293
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2294
|
+
import { join as join9 } from "path";
|
|
2295
|
+
import { homedir as homedir8 } from "os";
|
|
2137
2296
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
2138
2297
|
var DEFAULT_MODEL = "gpt-5.4";
|
|
2139
|
-
var CODEX_CONFIG_PATH =
|
|
2298
|
+
var CODEX_CONFIG_PATH = join9(homedir8(), ".codex", "config.toml");
|
|
2140
2299
|
function isLinearThoughtEvent2(event) {
|
|
2141
2300
|
return event.content.type === "thought";
|
|
2142
2301
|
}
|
|
@@ -2158,7 +2317,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2158
2317
|
constructor(options) {
|
|
2159
2318
|
super(options);
|
|
2160
2319
|
this.codex = new Codex();
|
|
2161
|
-
this.tempImageDir =
|
|
2320
|
+
this.tempImageDir = join9(homedir8(), ".replicas", "codex", "temp-images");
|
|
2162
2321
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
2163
2322
|
}
|
|
2164
2323
|
async initialize() {
|
|
@@ -2178,12 +2337,12 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2178
2337
|
*/
|
|
2179
2338
|
async updateCodexConfig(developerInstructions) {
|
|
2180
2339
|
try {
|
|
2181
|
-
const codexDir =
|
|
2182
|
-
await
|
|
2340
|
+
const codexDir = join9(homedir8(), ".codex");
|
|
2341
|
+
await mkdir7(codexDir, { recursive: true });
|
|
2183
2342
|
let config = {};
|
|
2184
|
-
if (
|
|
2343
|
+
if (existsSync5(CODEX_CONFIG_PATH)) {
|
|
2185
2344
|
try {
|
|
2186
|
-
const existingContent = await
|
|
2345
|
+
const existingContent = await readFile5(CODEX_CONFIG_PATH, "utf-8");
|
|
2187
2346
|
const parsed = parseToml(existingContent);
|
|
2188
2347
|
if (isRecord(parsed)) {
|
|
2189
2348
|
config = parsed;
|
|
@@ -2198,7 +2357,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2198
2357
|
delete config.developer_instructions;
|
|
2199
2358
|
}
|
|
2200
2359
|
const tomlContent = stringifyToml(config);
|
|
2201
|
-
await
|
|
2360
|
+
await writeFile5(CODEX_CONFIG_PATH, tomlContent, "utf-8");
|
|
2202
2361
|
console.log("[CodexManager] Updated config.toml with developer_instructions");
|
|
2203
2362
|
} catch (error) {
|
|
2204
2363
|
console.error("[CodexManager] Failed to update config.toml:", error);
|
|
@@ -2209,14 +2368,14 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2209
2368
|
* @returns Array of temp file paths
|
|
2210
2369
|
*/
|
|
2211
2370
|
async saveImagesToTempFiles(images) {
|
|
2212
|
-
await
|
|
2371
|
+
await mkdir7(this.tempImageDir, { recursive: true });
|
|
2213
2372
|
const tempPaths = [];
|
|
2214
2373
|
for (const image of images) {
|
|
2215
2374
|
const ext = image.source.media_type.split("/")[1] || "png";
|
|
2216
2375
|
const filename = `img_${randomUUID2()}.${ext}`;
|
|
2217
|
-
const filepath =
|
|
2376
|
+
const filepath = join9(this.tempImageDir, filename);
|
|
2218
2377
|
const buffer = Buffer.from(image.source.data, "base64");
|
|
2219
|
-
await
|
|
2378
|
+
await writeFile5(filepath, buffer);
|
|
2220
2379
|
tempPaths.push(filepath);
|
|
2221
2380
|
}
|
|
2222
2381
|
return tempPaths;
|
|
@@ -2346,13 +2505,13 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2346
2505
|
}
|
|
2347
2506
|
// Helper methods for finding session files
|
|
2348
2507
|
async findSessionFile(threadId) {
|
|
2349
|
-
const sessionsDir =
|
|
2508
|
+
const sessionsDir = join9(homedir8(), ".codex", "sessions");
|
|
2350
2509
|
try {
|
|
2351
2510
|
const now = /* @__PURE__ */ new Date();
|
|
2352
2511
|
const year = now.getFullYear();
|
|
2353
2512
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
2354
2513
|
const day = String(now.getDate()).padStart(2, "0");
|
|
2355
|
-
const todayDir =
|
|
2514
|
+
const todayDir = join9(sessionsDir, String(year), month, day);
|
|
2356
2515
|
const file = await this.findFileInDirectory(todayDir, threadId);
|
|
2357
2516
|
if (file) return file;
|
|
2358
2517
|
for (let daysAgo = 1; daysAgo <= 7; daysAgo++) {
|
|
@@ -2361,7 +2520,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2361
2520
|
const searchYear = date.getFullYear();
|
|
2362
2521
|
const searchMonth = String(date.getMonth() + 1).padStart(2, "0");
|
|
2363
2522
|
const searchDay = String(date.getDate()).padStart(2, "0");
|
|
2364
|
-
const searchDir =
|
|
2523
|
+
const searchDir = join9(sessionsDir, String(searchYear), searchMonth, searchDay);
|
|
2365
2524
|
const file2 = await this.findFileInDirectory(searchDir, threadId);
|
|
2366
2525
|
if (file2) return file2;
|
|
2367
2526
|
}
|
|
@@ -2375,7 +2534,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2375
2534
|
const files = await readdir2(directory);
|
|
2376
2535
|
for (const file of files) {
|
|
2377
2536
|
if (file.endsWith(".jsonl") && file.includes(threadId)) {
|
|
2378
|
-
const fullPath =
|
|
2537
|
+
const fullPath = join9(directory, file);
|
|
2379
2538
|
const stats = await stat2(fullPath);
|
|
2380
2539
|
if (stats.isFile()) {
|
|
2381
2540
|
return fullPath;
|
|
@@ -2408,7 +2567,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2408
2567
|
const seenLines = /* @__PURE__ */ new Set();
|
|
2409
2568
|
const seedSeenLines = async () => {
|
|
2410
2569
|
try {
|
|
2411
|
-
const content = await
|
|
2570
|
+
const content = await readFile5(sessionFile, "utf-8");
|
|
2412
2571
|
const lines = content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
2413
2572
|
for (const line of lines) {
|
|
2414
2573
|
seenLines.add(line);
|
|
@@ -2420,7 +2579,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2420
2579
|
const pump = async () => {
|
|
2421
2580
|
let emitted = 0;
|
|
2422
2581
|
try {
|
|
2423
|
-
const content = await
|
|
2582
|
+
const content = await readFile5(sessionFile, "utf-8");
|
|
2424
2583
|
const lines = content.split("\n");
|
|
2425
2584
|
const completeLines = content.endsWith("\n") ? lines : lines.slice(0, -1);
|
|
2426
2585
|
for (const line of completeLines) {
|
|
@@ -2491,9 +2650,9 @@ var DuplicateDefaultChatError = class extends Error {
|
|
|
2491
2650
|
};
|
|
2492
2651
|
|
|
2493
2652
|
// src/services/chat/chat-service.ts
|
|
2494
|
-
var ENGINE_DIR2 =
|
|
2495
|
-
var CHATS_FILE =
|
|
2496
|
-
var CLAUDE_HISTORY_DIR =
|
|
2653
|
+
var ENGINE_DIR2 = join10(homedir9(), ".replicas", "engine");
|
|
2654
|
+
var CHATS_FILE = join10(ENGINE_DIR2, "chats.json");
|
|
2655
|
+
var CLAUDE_HISTORY_DIR = join10(ENGINE_DIR2, "claude-histories");
|
|
2497
2656
|
function isPersistedChat(value) {
|
|
2498
2657
|
if (!isRecord(value)) {
|
|
2499
2658
|
return false;
|
|
@@ -2508,8 +2667,8 @@ var ChatService = class {
|
|
|
2508
2667
|
chats = /* @__PURE__ */ new Map();
|
|
2509
2668
|
writeChain = Promise.resolve();
|
|
2510
2669
|
async initialize() {
|
|
2511
|
-
await
|
|
2512
|
-
await
|
|
2670
|
+
await mkdir8(ENGINE_DIR2, { recursive: true });
|
|
2671
|
+
await mkdir8(CLAUDE_HISTORY_DIR, { recursive: true });
|
|
2513
2672
|
const persisted = await this.loadChats();
|
|
2514
2673
|
for (const chat of persisted) {
|
|
2515
2674
|
const runtime = this.createRuntimeChat(chat);
|
|
@@ -2593,7 +2752,7 @@ var ChatService = class {
|
|
|
2593
2752
|
this.chats.delete(chatId);
|
|
2594
2753
|
await this.persistAllChats();
|
|
2595
2754
|
if (chat.persisted.provider === "claude") {
|
|
2596
|
-
const historyFilePath =
|
|
2755
|
+
const historyFilePath = join10(CLAUDE_HISTORY_DIR, `${chatId}.jsonl`);
|
|
2597
2756
|
await rm(historyFilePath, { force: true });
|
|
2598
2757
|
}
|
|
2599
2758
|
await this.publish({
|
|
@@ -2636,7 +2795,7 @@ var ChatService = class {
|
|
|
2636
2795
|
};
|
|
2637
2796
|
const provider = persisted.provider === "claude" ? new ClaudeManager({
|
|
2638
2797
|
workingDirectory: this.workingDirectory,
|
|
2639
|
-
historyFilePath:
|
|
2798
|
+
historyFilePath: join10(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
2640
2799
|
initialSessionId: persisted.providerSessionId,
|
|
2641
2800
|
onSaveSessionId: saveSession,
|
|
2642
2801
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -2732,7 +2891,7 @@ var ChatService = class {
|
|
|
2732
2891
|
}
|
|
2733
2892
|
async loadChats() {
|
|
2734
2893
|
try {
|
|
2735
|
-
const content = await
|
|
2894
|
+
const content = await readFile6(CHATS_FILE, "utf-8");
|
|
2736
2895
|
const parsed = JSON.parse(content);
|
|
2737
2896
|
if (!Array.isArray(parsed)) {
|
|
2738
2897
|
return [];
|
|
@@ -2749,7 +2908,7 @@ var ChatService = class {
|
|
|
2749
2908
|
this.writeChain = this.writeChain.catch(() => {
|
|
2750
2909
|
}).then(async () => {
|
|
2751
2910
|
const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
|
|
2752
|
-
await
|
|
2911
|
+
await writeFile6(CHATS_FILE, JSON.stringify(payload, null, 2), "utf-8");
|
|
2753
2912
|
});
|
|
2754
2913
|
await this.writeChain;
|
|
2755
2914
|
}
|
|
@@ -2774,12 +2933,12 @@ import { Hono } from "hono";
|
|
|
2774
2933
|
import { z } from "zod";
|
|
2775
2934
|
|
|
2776
2935
|
// src/services/plan-service.ts
|
|
2777
|
-
import { readdir as readdir3, readFile as
|
|
2778
|
-
import { homedir as
|
|
2779
|
-
import { basename, join as
|
|
2936
|
+
import { readdir as readdir3, readFile as readFile7 } from "fs/promises";
|
|
2937
|
+
import { homedir as homedir10 } from "os";
|
|
2938
|
+
import { basename, join as join11 } from "path";
|
|
2780
2939
|
var PLAN_DIRECTORIES = [
|
|
2781
|
-
|
|
2782
|
-
|
|
2940
|
+
join11(homedir10(), ".claude", "plans"),
|
|
2941
|
+
join11(homedir10(), ".replicas", "plans")
|
|
2783
2942
|
];
|
|
2784
2943
|
function isMarkdownFile(filename) {
|
|
2785
2944
|
return filename.toLowerCase().endsWith(".md");
|
|
@@ -2813,9 +2972,9 @@ var PlanService = class {
|
|
|
2813
2972
|
return null;
|
|
2814
2973
|
}
|
|
2815
2974
|
for (const directory of PLAN_DIRECTORIES) {
|
|
2816
|
-
const filePath =
|
|
2975
|
+
const filePath = join11(directory, safeFilename);
|
|
2817
2976
|
try {
|
|
2818
|
-
const content = await
|
|
2977
|
+
const content = await readFile7(filePath, "utf-8");
|
|
2819
2978
|
return { filename: safeFilename, content };
|
|
2820
2979
|
} catch {
|
|
2821
2980
|
}
|
|
@@ -2828,8 +2987,8 @@ var planService = new PlanService();
|
|
|
2828
2987
|
// src/services/warm-hooks-service.ts
|
|
2829
2988
|
import { execFile } from "child_process";
|
|
2830
2989
|
import { promisify as promisify2 } from "util";
|
|
2831
|
-
import { readFile as
|
|
2832
|
-
import { join as
|
|
2990
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2991
|
+
import { join as join12 } from "path";
|
|
2833
2992
|
var execFileAsync = promisify2(execFile);
|
|
2834
2993
|
async function installSkill(params) {
|
|
2835
2994
|
const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
|
|
@@ -2895,9 +3054,9 @@ async function executeHookScript(params) {
|
|
|
2895
3054
|
}
|
|
2896
3055
|
}
|
|
2897
3056
|
async function readRepoWarmHook(repoPath) {
|
|
2898
|
-
const configPath =
|
|
3057
|
+
const configPath = join12(repoPath, "replicas.json");
|
|
2899
3058
|
try {
|
|
2900
|
-
const raw = await
|
|
3059
|
+
const raw = await readFile8(configPath, "utf-8");
|
|
2901
3060
|
const parsed = JSON.parse(raw);
|
|
2902
3061
|
if (!isRecord(parsed) || !("warmHook" in parsed)) {
|
|
2903
3062
|
return null;
|
|
@@ -2940,7 +3099,11 @@ async function runWarmHooks(params) {
|
|
|
2940
3099
|
timeoutMs: params.timeoutMs
|
|
2941
3100
|
});
|
|
2942
3101
|
outputBlocks.push(installResult.output);
|
|
3102
|
+
if (installResult.exitCode === 0) {
|
|
3103
|
+
await environmentDetailsService.track({ skillsInstalled: [source] });
|
|
3104
|
+
}
|
|
2943
3105
|
if (installResult.exitCode !== 0) {
|
|
3106
|
+
await environmentDetailsService.setGlobalWarmHook("no", installResult.output);
|
|
2944
3107
|
return {
|
|
2945
3108
|
exitCode: installResult.exitCode,
|
|
2946
3109
|
output: outputBlocks.join("\n\n"),
|
|
@@ -2956,6 +3119,7 @@ async function runWarmHooks(params) {
|
|
|
2956
3119
|
timeoutMs: params.timeoutMs
|
|
2957
3120
|
});
|
|
2958
3121
|
outputBlocks.push(orgResult.output);
|
|
3122
|
+
await environmentDetailsService.setGlobalWarmHook(orgResult.exitCode === 0 ? "yes" : "no", orgResult.output);
|
|
2959
3123
|
if (orgResult.exitCode !== 0) {
|
|
2960
3124
|
return {
|
|
2961
3125
|
exitCode: orgResult.exitCode,
|
|
@@ -2963,9 +3127,18 @@ async function runWarmHooks(params) {
|
|
|
2963
3127
|
timedOut: orgResult.timedOut
|
|
2964
3128
|
};
|
|
2965
3129
|
}
|
|
3130
|
+
} else {
|
|
3131
|
+
await environmentDetailsService.setGlobalWarmHook("n/a");
|
|
2966
3132
|
}
|
|
2967
3133
|
if (params.includeRepoHooks !== false) {
|
|
2968
3134
|
const repoHooks = await collectRepoWarmHooks();
|
|
3135
|
+
const repoHookNames = new Set(repoHooks.map((repoHook) => repoHook.repoName));
|
|
3136
|
+
const discoveredRepos = await gitService.listRepositories();
|
|
3137
|
+
for (const repo of discoveredRepos) {
|
|
3138
|
+
if (!repoHookNames.has(repo.name)) {
|
|
3139
|
+
await environmentDetailsService.setRepositoryWarmHook(repo.name, "n/a");
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
2969
3142
|
for (const repoHook of repoHooks) {
|
|
2970
3143
|
for (const command of repoHook.commands) {
|
|
2971
3144
|
const repoResult = await executeHookScript({
|
|
@@ -2975,6 +3148,10 @@ async function runWarmHooks(params) {
|
|
|
2975
3148
|
timeoutMs: repoHook.timeoutMs
|
|
2976
3149
|
});
|
|
2977
3150
|
outputBlocks.push(repoResult.output);
|
|
3151
|
+
await environmentDetailsService.setRepositoryWarmHook(
|
|
3152
|
+
repoHook.repoName,
|
|
3153
|
+
repoResult.exitCode === 0 ? "yes" : "no"
|
|
3154
|
+
);
|
|
2978
3155
|
if (repoResult.exitCode !== 0) {
|
|
2979
3156
|
return {
|
|
2980
3157
|
exitCode: repoResult.exitCode,
|
|
@@ -2984,6 +3161,11 @@ async function runWarmHooks(params) {
|
|
|
2984
3161
|
}
|
|
2985
3162
|
}
|
|
2986
3163
|
}
|
|
3164
|
+
} else {
|
|
3165
|
+
const repos = await gitService.listRepositories();
|
|
3166
|
+
for (const repo of repos) {
|
|
3167
|
+
await environmentDetailsService.setRepositoryWarmHook(repo.name, "n/a");
|
|
3168
|
+
}
|
|
2987
3169
|
}
|
|
2988
3170
|
if (outputBlocks.length === 0) {
|
|
2989
3171
|
return {
|
|
@@ -3219,6 +3401,35 @@ function createV1Routes(deps) {
|
|
|
3219
3401
|
};
|
|
3220
3402
|
return c.json(response);
|
|
3221
3403
|
});
|
|
3404
|
+
app2.get("/version", (c) => {
|
|
3405
|
+
const response = {
|
|
3406
|
+
version: REPLICAS_ENGINE_VERSION
|
|
3407
|
+
};
|
|
3408
|
+
return c.json(response);
|
|
3409
|
+
});
|
|
3410
|
+
app2.get("/environment", async (c) => {
|
|
3411
|
+
try {
|
|
3412
|
+
const details = await environmentDetailsService.getDetails();
|
|
3413
|
+
return c.json(details);
|
|
3414
|
+
} catch (error) {
|
|
3415
|
+
return c.json(
|
|
3416
|
+
jsonError("Failed to load environment details", error instanceof Error ? error.message : "Unknown error"),
|
|
3417
|
+
500
|
|
3418
|
+
);
|
|
3419
|
+
}
|
|
3420
|
+
});
|
|
3421
|
+
app2.post("/environment/track", async (c) => {
|
|
3422
|
+
try {
|
|
3423
|
+
const body = await c.req.json();
|
|
3424
|
+
const details = await environmentDetailsService.track(body);
|
|
3425
|
+
return c.json(details);
|
|
3426
|
+
} catch (error) {
|
|
3427
|
+
return c.json(
|
|
3428
|
+
jsonError("Failed to track environment details", error instanceof Error ? error.message : "Unknown error"),
|
|
3429
|
+
500
|
|
3430
|
+
);
|
|
3431
|
+
}
|
|
3432
|
+
});
|
|
3222
3433
|
app2.post("/warm-hooks/run", async (c) => {
|
|
3223
3434
|
try {
|
|
3224
3435
|
const body = await c.req.json();
|
|
@@ -3246,6 +3457,7 @@ function createV1Routes(deps) {
|
|
|
3246
3457
|
// src/index.ts
|
|
3247
3458
|
await engineLogger.initialize();
|
|
3248
3459
|
await eventService.initialize();
|
|
3460
|
+
await environmentDetailsService.initialize();
|
|
3249
3461
|
var READY_MESSAGE = "========= REPLICAS WORKSPACE READY ==========";
|
|
3250
3462
|
var COMPLETION_MESSAGE = "========= REPLICAS WORKSPACE INITIALIZATION COMPLETE ==========";
|
|
3251
3463
|
function checkActiveSSHSessions() {
|
|
@@ -3280,7 +3492,7 @@ app.get("/health", async (c) => {
|
|
|
3280
3492
|
return c.json({ status: "initializing", timestamp: (/* @__PURE__ */ new Date()).toISOString() }, 503);
|
|
3281
3493
|
}
|
|
3282
3494
|
try {
|
|
3283
|
-
const logContent = await
|
|
3495
|
+
const logContent = await readFile9("/var/log/cloud-init-output.log", "utf-8");
|
|
3284
3496
|
let status;
|
|
3285
3497
|
if (logContent.includes(COMPLETION_MESSAGE)) {
|
|
3286
3498
|
status = "active";
|