replicas-engine 0.1.51 → 0.1.52
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 +190 -165
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -44,35 +44,36 @@ function requireValidURL(value, name) {
|
|
|
44
44
|
throw new Error(`Invalid engine environment: ${name} must be a valid URL`);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
var IS_WARMING_MODE = process.argv.includes("--warming");
|
|
47
48
|
function loadEngineEnv() {
|
|
48
|
-
const HOME_DIR =
|
|
49
|
-
|
|
50
|
-
//
|
|
49
|
+
const HOME_DIR = homedir();
|
|
50
|
+
const env = {
|
|
51
|
+
// Defined: always available
|
|
51
52
|
REPLICAS_ENGINE_SECRET: requireDefined(readEnv("REPLICAS_ENGINE_SECRET"), "REPLICAS_ENGINE_SECRET"),
|
|
52
|
-
WORKSPACE_ID: requireDefined(readEnv("WORKSPACE_ID"), "WORKSPACE_ID"),
|
|
53
|
-
MONOLITH_URL: requireValidURL(requireDefined(readEnv("MONOLITH_URL"), "MONOLITH_URL"), "MONOLITH_URL"),
|
|
54
|
-
// Engine config defaults.
|
|
55
53
|
REPLICAS_ENGINE_PORT: parsePort(readEnv("REPLICAS_ENGINE_PORT")),
|
|
54
|
+
MONOLITH_URL: requireValidURL(requireDefined(readEnv("MONOLITH_URL"), "MONOLITH_URL"), "MONOLITH_URL"),
|
|
55
|
+
GH_TOKEN: readEnv("GH_TOKEN"),
|
|
56
56
|
HOME_DIR,
|
|
57
57
|
WORKSPACE_ROOT: join(HOME_DIR, "workspaces"),
|
|
58
|
-
//
|
|
58
|
+
// Runtime: may not be set during warming
|
|
59
|
+
WORKSPACE_ID: readEnv("WORKSPACE_ID"),
|
|
59
60
|
WORKSPACE_NAME: readEnv("WORKSPACE_NAME"),
|
|
60
61
|
LINEAR_SESSION_ID: readEnv("LINEAR_SESSION_ID"),
|
|
61
62
|
LINEAR_ACCESS_TOKEN: readEnv("LINEAR_ACCESS_TOKEN"),
|
|
62
63
|
SLACK_BOT_TOKEN: readEnv("SLACK_BOT_TOKEN"),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// not directly used by the engine code, but are required in the VM
|
|
66
|
-
// for use by the agent or SDKs (e.g claude/codex)
|
|
64
|
+
SLACK_CHANNEL_ID: readEnv("SLACK_CHANNEL_ID"),
|
|
65
|
+
SLACK_THREAD_TS: readEnv("SLACK_THREAD_TS"),
|
|
67
66
|
ANTHROPIC_API_KEY: readEnv("ANTHROPIC_API_KEY"),
|
|
68
67
|
OPENAI_API_KEY: readEnv("OPENAI_API_KEY"),
|
|
69
68
|
CLAUDE_CODE_USE_BEDROCK: readEnv("CLAUDE_CODE_USE_BEDROCK"),
|
|
70
69
|
AWS_ACCESS_KEY_ID: readEnv("AWS_ACCESS_KEY_ID"),
|
|
71
70
|
AWS_SECRET_ACCESS_KEY: readEnv("AWS_SECRET_ACCESS_KEY"),
|
|
72
|
-
AWS_REGION: readEnv("AWS_REGION")
|
|
73
|
-
SLACK_CHANNEL_ID: readEnv("SLACK_CHANNEL_ID"),
|
|
74
|
-
SLACK_THREAD_TS: readEnv("SLACK_THREAD_TS")
|
|
71
|
+
AWS_REGION: readEnv("AWS_REGION")
|
|
75
72
|
};
|
|
73
|
+
if (!IS_WARMING_MODE && !env.WORKSPACE_ID) {
|
|
74
|
+
console.error("WORKSPACE_ID is not set \u2014 this is required in normal (non-warming) mode");
|
|
75
|
+
}
|
|
76
|
+
return env;
|
|
76
77
|
}
|
|
77
78
|
var ENGINE_ENV = loadEngineEnv();
|
|
78
79
|
|
|
@@ -96,7 +97,7 @@ var BaseRefreshManager = class {
|
|
|
96
97
|
if (this.intervalHandle) {
|
|
97
98
|
return;
|
|
98
99
|
}
|
|
99
|
-
const skipReason = this.
|
|
100
|
+
const skipReason = this.getSkipReason();
|
|
100
101
|
if (skipReason) {
|
|
101
102
|
console.log(`[${this.managerName}] Skipping: ${skipReason}`);
|
|
102
103
|
return;
|
|
@@ -127,21 +128,17 @@ var BaseRefreshManager = class {
|
|
|
127
128
|
return null;
|
|
128
129
|
}
|
|
129
130
|
getRuntimeConfig() {
|
|
131
|
+
if (!ENGINE_ENV.WORKSPACE_ID) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
130
134
|
return {
|
|
131
135
|
monolithUrl: ENGINE_ENV.MONOLITH_URL,
|
|
132
136
|
workspaceId: ENGINE_ENV.WORKSPACE_ID,
|
|
133
137
|
engineSecret: ENGINE_ENV.REPLICAS_ENGINE_SECRET
|
|
134
138
|
};
|
|
135
139
|
}
|
|
136
|
-
getSkipReasonForRun() {
|
|
137
|
-
const skipReason = this.getSkipReason();
|
|
138
|
-
if (skipReason) {
|
|
139
|
-
return skipReason;
|
|
140
|
-
}
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
140
|
async refreshOnce() {
|
|
144
|
-
if (this.
|
|
141
|
+
if (this.getSkipReason()) {
|
|
145
142
|
return;
|
|
146
143
|
}
|
|
147
144
|
const config = this.getRuntimeConfig();
|
|
@@ -830,10 +827,12 @@ var EngineLogger = class {
|
|
|
830
827
|
var engineLogger = new EngineLogger();
|
|
831
828
|
|
|
832
829
|
// src/services/environment-details-service.ts
|
|
833
|
-
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
830
|
+
import { mkdir as mkdir3, readdir as readdir2, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
834
831
|
import { existsSync as existsSync3 } from "fs";
|
|
835
832
|
import { homedir as homedir4 } from "os";
|
|
836
833
|
import { join as join5 } from "path";
|
|
834
|
+
import { execFile } from "child_process";
|
|
835
|
+
import { promisify } from "util";
|
|
837
836
|
|
|
838
837
|
// ../shared/src/sandbox.ts
|
|
839
838
|
var SANDBOX_LIFECYCLE = {
|
|
@@ -842,6 +841,14 @@ var SANDBOX_LIFECYCLE = {
|
|
|
842
841
|
AUTO_DELETE_MINUTES: -1,
|
|
843
842
|
SSH_TOKEN_EXPIRATION_MINUTES: 3 * 60
|
|
844
843
|
};
|
|
844
|
+
var SANDBOX_PATHS = {
|
|
845
|
+
HOME_DIR: "/home/ubuntu",
|
|
846
|
+
WORKSPACES_DIR: "/home/ubuntu/workspaces",
|
|
847
|
+
REPLICAS_DIR: "/home/ubuntu/.replicas",
|
|
848
|
+
REPLICAS_FILES_DIR: "/home/ubuntu/.replicas/files",
|
|
849
|
+
REPLICAS_FILES_DISPLAY_DIR: "~/.replicas/files",
|
|
850
|
+
REPLICAS_RUNTIME_ENV_FILE: "/home/ubuntu/.replicas/runtime-env.sh"
|
|
851
|
+
};
|
|
845
852
|
|
|
846
853
|
// ../shared/src/warm-hooks.ts
|
|
847
854
|
var DEFAULT_WARM_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
@@ -911,34 +918,14 @@ var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
|
|
|
911
918
|
var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
912
919
|
|
|
913
920
|
// src/services/environment-details-service.ts
|
|
914
|
-
var
|
|
915
|
-
var
|
|
921
|
+
var execFileAsync = promisify(execFile);
|
|
922
|
+
var REPLICAS_DIR = SANDBOX_PATHS.REPLICAS_DIR;
|
|
923
|
+
var RUNTIME_ENV_FILE = SANDBOX_PATHS.REPLICAS_RUNTIME_ENV_FILE;
|
|
924
|
+
var FILES_DIR = SANDBOX_PATHS.REPLICAS_FILES_DIR;
|
|
925
|
+
var PERSISTED_STATE_FILE = join5(REPLICAS_DIR, "environment-details.json");
|
|
916
926
|
var CLAUDE_CREDENTIALS_PATH = join5(homedir4(), ".claude", ".credentials.json");
|
|
917
927
|
var CODEX_AUTH_PATH = join5(homedir4(), ".codex", "auth.json");
|
|
918
|
-
function
|
|
919
|
-
return { status, details: details ?? null };
|
|
920
|
-
}
|
|
921
|
-
function createDefaultDetails() {
|
|
922
|
-
return {
|
|
923
|
-
engineVersion: REPLICAS_ENGINE_VERSION,
|
|
924
|
-
globalWarmHookCompleted: createExecutionItem("n/a"),
|
|
925
|
-
repositories: [],
|
|
926
|
-
filesUploaded: [],
|
|
927
|
-
envVarsSet: [],
|
|
928
|
-
skillsInstalled: [],
|
|
929
|
-
runtimeEnvVarsSet: [],
|
|
930
|
-
repositoriesCloned: [],
|
|
931
|
-
gitIdentityConfigured: false,
|
|
932
|
-
githubCredentialsConfigured: false,
|
|
933
|
-
linearAccessConfigured: false,
|
|
934
|
-
slackAccessConfigured: false,
|
|
935
|
-
githubAccessConfigured: false,
|
|
936
|
-
claudeAuthMethod: "none",
|
|
937
|
-
codexAuthMethod: "none",
|
|
938
|
-
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
function getClaudeAuthMethod() {
|
|
928
|
+
function detectClaudeAuthMethod() {
|
|
942
929
|
if (existsSync3(CLAUDE_CREDENTIALS_PATH)) {
|
|
943
930
|
return "oauth";
|
|
944
931
|
}
|
|
@@ -950,7 +937,7 @@ function getClaudeAuthMethod() {
|
|
|
950
937
|
}
|
|
951
938
|
return "none";
|
|
952
939
|
}
|
|
953
|
-
function
|
|
940
|
+
function detectCodexAuthMethod() {
|
|
954
941
|
if (existsSync3(CODEX_AUTH_PATH)) {
|
|
955
942
|
return "oauth";
|
|
956
943
|
}
|
|
@@ -959,121 +946,162 @@ function getCodexAuthMethod() {
|
|
|
959
946
|
}
|
|
960
947
|
return "none";
|
|
961
948
|
}
|
|
962
|
-
function
|
|
963
|
-
|
|
964
|
-
|
|
949
|
+
async function detectGitIdentityConfigured() {
|
|
950
|
+
try {
|
|
951
|
+
const { stdout } = await execFileAsync("git", ["config", "--global", "user.name"]);
|
|
952
|
+
return stdout.trim().length > 0;
|
|
953
|
+
} catch {
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
async function parseRuntimeEnvKeys() {
|
|
958
|
+
try {
|
|
959
|
+
if (!existsSync3(RUNTIME_ENV_FILE)) {
|
|
960
|
+
return [];
|
|
961
|
+
}
|
|
962
|
+
const content = await readFile2(RUNTIME_ENV_FILE, "utf-8");
|
|
963
|
+
const keys = [];
|
|
964
|
+
for (const line of content.split("\n")) {
|
|
965
|
+
const trimmed = line.trim();
|
|
966
|
+
if (!trimmed.startsWith("export ")) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
const assignment = trimmed.slice("export ".length);
|
|
970
|
+
const eqIndex = assignment.indexOf("=");
|
|
971
|
+
if (eqIndex > 0) {
|
|
972
|
+
const key = assignment.slice(0, eqIndex).trim();
|
|
973
|
+
if (key) {
|
|
974
|
+
keys.push(key);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return keys;
|
|
979
|
+
} catch {
|
|
980
|
+
return [];
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
async function detectStaticState() {
|
|
984
|
+
const [repositories, gitIdentityConfigured, runtimeEnvKeys] = await Promise.all([
|
|
985
|
+
gitService.listRepositories(),
|
|
986
|
+
detectGitIdentityConfigured(),
|
|
987
|
+
parseRuntimeEnvKeys()
|
|
988
|
+
]);
|
|
965
989
|
return {
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
990
|
+
repositoriesCloned: repositories.map((repo) => repo.name),
|
|
991
|
+
gitIdentityConfigured,
|
|
992
|
+
envVarsSet: runtimeEnvKeys,
|
|
993
|
+
runtimeEnvVarsSet: runtimeEnvKeys,
|
|
994
|
+
claudeAuthMethod: detectClaudeAuthMethod(),
|
|
995
|
+
codexAuthMethod: detectCodexAuthMethod(),
|
|
970
996
|
githubAccessConfigured: Boolean(ENGINE_ENV.GH_TOKEN),
|
|
971
997
|
githubCredentialsConfigured: Boolean(ENGINE_ENV.GH_TOKEN),
|
|
972
|
-
|
|
973
|
-
|
|
998
|
+
linearAccessConfigured: Boolean(ENGINE_ENV.LINEAR_SESSION_ID || ENGINE_ENV.LINEAR_ACCESS_TOKEN),
|
|
999
|
+
slackAccessConfigured: Boolean(ENGINE_ENV.SLACK_BOT_TOKEN)
|
|
974
1000
|
};
|
|
975
1001
|
}
|
|
976
|
-
function
|
|
977
|
-
|
|
978
|
-
|
|
1002
|
+
async function detectUploadedFiles() {
|
|
1003
|
+
try {
|
|
1004
|
+
if (!existsSync3(FILES_DIR)) {
|
|
1005
|
+
return [];
|
|
1006
|
+
}
|
|
1007
|
+
const entries = await readdir2(FILES_DIR);
|
|
1008
|
+
return entries.filter((entry) => !entry.startsWith(".")).map((entry) => `${SANDBOX_PATHS.REPLICAS_FILES_DISPLAY_DIR}/${entry}`);
|
|
1009
|
+
} catch {
|
|
1010
|
+
return [];
|
|
979
1011
|
}
|
|
980
|
-
|
|
1012
|
+
}
|
|
1013
|
+
function createDefaultPersistedState() {
|
|
1014
|
+
return {
|
|
1015
|
+
globalWarmHookCompleted: { status: "n/a", details: null },
|
|
1016
|
+
repositories: [],
|
|
1017
|
+
skillsInstalled: []
|
|
1018
|
+
};
|
|
981
1019
|
}
|
|
982
1020
|
function upsertRepositoryStatus(current, incoming) {
|
|
983
|
-
const byName = new Map(current.map((
|
|
984
|
-
for (const
|
|
985
|
-
const existing = byName.get(
|
|
986
|
-
byName.set(
|
|
987
|
-
repositoryName:
|
|
988
|
-
warmHookCompleted:
|
|
989
|
-
startHookCompleted:
|
|
1021
|
+
const byName = new Map(current.map((r) => [r.repositoryName, r]));
|
|
1022
|
+
for (const repo of incoming) {
|
|
1023
|
+
const existing = byName.get(repo.repositoryName);
|
|
1024
|
+
byName.set(repo.repositoryName, {
|
|
1025
|
+
repositoryName: repo.repositoryName,
|
|
1026
|
+
warmHookCompleted: repo.warmHookCompleted !== "n/a" ? repo.warmHookCompleted : existing?.warmHookCompleted ?? "n/a",
|
|
1027
|
+
startHookCompleted: repo.startHookCompleted !== "n/a" ? repo.startHookCompleted : existing?.startHookCompleted ?? "n/a"
|
|
990
1028
|
});
|
|
991
1029
|
}
|
|
992
1030
|
return [...byName.values()].sort((a, b) => a.repositoryName.localeCompare(b.repositoryName));
|
|
993
1031
|
}
|
|
1032
|
+
async function readPersistedState() {
|
|
1033
|
+
try {
|
|
1034
|
+
if (!existsSync3(PERSISTED_STATE_FILE)) {
|
|
1035
|
+
return createDefaultPersistedState();
|
|
1036
|
+
}
|
|
1037
|
+
const raw = await readFile2(PERSISTED_STATE_FILE, "utf-8");
|
|
1038
|
+
const parsed = JSON.parse(raw);
|
|
1039
|
+
return { ...createDefaultPersistedState(), ...parsed };
|
|
1040
|
+
} catch {
|
|
1041
|
+
return createDefaultPersistedState();
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
async function writePersistedState(state) {
|
|
1045
|
+
await mkdir3(REPLICAS_DIR, { recursive: true });
|
|
1046
|
+
await writeFile3(PERSISTED_STATE_FILE, `${JSON.stringify(state, null, 2)}
|
|
1047
|
+
`, "utf-8");
|
|
1048
|
+
}
|
|
994
1049
|
var EnvironmentDetailsService = class {
|
|
1050
|
+
staticState = null;
|
|
995
1051
|
async initialize() {
|
|
996
|
-
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
repositoryName:
|
|
1052
|
+
this.staticState = await detectStaticState();
|
|
1053
|
+
const persisted = await readPersistedState();
|
|
1054
|
+
persisted.repositories = upsertRepositoryStatus(
|
|
1055
|
+
persisted.repositories,
|
|
1056
|
+
this.staticState.repositoriesCloned.map((name) => ({
|
|
1057
|
+
repositoryName: name,
|
|
1002
1058
|
warmHookCompleted: "n/a",
|
|
1003
1059
|
startHookCompleted: "n/a"
|
|
1004
1060
|
}))
|
|
1005
1061
|
);
|
|
1006
|
-
await
|
|
1007
|
-
...current,
|
|
1008
|
-
engineVersion: REPLICAS_ENGINE_VERSION,
|
|
1009
|
-
repositories: merged,
|
|
1010
|
-
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1011
|
-
});
|
|
1062
|
+
await writePersistedState(persisted);
|
|
1012
1063
|
}
|
|
1013
1064
|
async getDetails() {
|
|
1014
|
-
const
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
...current,
|
|
1065
|
+
const staticState = this.staticState ?? await detectStaticState();
|
|
1066
|
+
const [persisted, filesUploaded] = await Promise.all([
|
|
1067
|
+
readPersistedState(),
|
|
1068
|
+
detectUploadedFiles()
|
|
1069
|
+
]);
|
|
1070
|
+
return {
|
|
1021
1071
|
engineVersion: REPLICAS_ENGINE_VERSION,
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
filesUploaded
|
|
1025
|
-
envVarsSet: mergeUnique(current.envVarsSet, update.envVarsSet),
|
|
1026
|
-
skillsInstalled: mergeUnique(current.skillsInstalled, update.skillsInstalled),
|
|
1027
|
-
runtimeEnvVarsSet: mergeUnique(current.runtimeEnvVarsSet, update.runtimeEnvVarsSet),
|
|
1028
|
-
repositoriesCloned: mergeUnique(current.repositoriesCloned, update.repositoriesCloned),
|
|
1029
|
-
gitIdentityConfigured: update.gitIdentityConfigured ?? current.gitIdentityConfigured,
|
|
1030
|
-
githubCredentialsConfigured: update.githubCredentialsConfigured ?? current.githubCredentialsConfigured,
|
|
1072
|
+
...staticState,
|
|
1073
|
+
...persisted,
|
|
1074
|
+
filesUploaded,
|
|
1031
1075
|
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1032
1076
|
};
|
|
1033
|
-
|
|
1034
|
-
|
|
1077
|
+
}
|
|
1078
|
+
async trackSkillsInstalled(skills) {
|
|
1079
|
+
const persisted = await readPersistedState();
|
|
1080
|
+
persisted.skillsInstalled = Array.from(/* @__PURE__ */ new Set([...persisted.skillsInstalled, ...skills]));
|
|
1081
|
+
await writePersistedState(persisted);
|
|
1035
1082
|
}
|
|
1036
1083
|
async setGlobalWarmHook(status, details) {
|
|
1037
|
-
await
|
|
1038
|
-
|
|
1039
|
-
|
|
1084
|
+
const persisted = await readPersistedState();
|
|
1085
|
+
persisted.globalWarmHookCompleted = { status, details: details ?? null };
|
|
1086
|
+
await writePersistedState(persisted);
|
|
1040
1087
|
}
|
|
1041
1088
|
async setRepositoryWarmHook(repositoryName, status) {
|
|
1042
|
-
await
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1089
|
+
const persisted = await readPersistedState();
|
|
1090
|
+
persisted.repositories = upsertRepositoryStatus(persisted.repositories, [{
|
|
1091
|
+
repositoryName,
|
|
1092
|
+
warmHookCompleted: status,
|
|
1093
|
+
startHookCompleted: "n/a"
|
|
1094
|
+
}]);
|
|
1095
|
+
await writePersistedState(persisted);
|
|
1049
1096
|
}
|
|
1050
1097
|
async setRepositoryStartHook(repositoryName, status) {
|
|
1051
|
-
await
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
}
|
|
1059
|
-
async readDetails() {
|
|
1060
|
-
try {
|
|
1061
|
-
if (!existsSync3(ENVIRONMENT_DETAILS_FILE)) {
|
|
1062
|
-
return createDefaultDetails();
|
|
1063
|
-
}
|
|
1064
|
-
const raw = await readFile2(ENVIRONMENT_DETAILS_FILE, "utf-8");
|
|
1065
|
-
return getLiveDetails({
|
|
1066
|
-
...createDefaultDetails(),
|
|
1067
|
-
...JSON.parse(raw)
|
|
1068
|
-
});
|
|
1069
|
-
} catch {
|
|
1070
|
-
return createDefaultDetails();
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
async writeDetails(details) {
|
|
1074
|
-
await mkdir3(REPLICAS_DIR, { recursive: true });
|
|
1075
|
-
await writeFile3(ENVIRONMENT_DETAILS_FILE, `${JSON.stringify(details, null, 2)}
|
|
1076
|
-
`, "utf-8");
|
|
1098
|
+
const persisted = await readPersistedState();
|
|
1099
|
+
persisted.repositories = upsertRepositoryStatus(persisted.repositories, [{
|
|
1100
|
+
repositoryName,
|
|
1101
|
+
warmHookCompleted: "n/a",
|
|
1102
|
+
startHookCompleted: status
|
|
1103
|
+
}]);
|
|
1104
|
+
await writePersistedState(persisted);
|
|
1077
1105
|
}
|
|
1078
1106
|
};
|
|
1079
1107
|
var environmentDetailsService = new EnvironmentDetailsService();
|
|
@@ -1084,8 +1112,8 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
1084
1112
|
import { join as join6 } from "path";
|
|
1085
1113
|
import { homedir as homedir5 } from "os";
|
|
1086
1114
|
import { exec } from "child_process";
|
|
1087
|
-
import { promisify } from "util";
|
|
1088
|
-
var execAsync =
|
|
1115
|
+
import { promisify as promisify2 } from "util";
|
|
1116
|
+
var execAsync = promisify2(exec);
|
|
1089
1117
|
var START_HOOKS_LOG = join6(homedir5(), ".replicas", "startHooks.log");
|
|
1090
1118
|
var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
|
|
1091
1119
|
Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
|
|
@@ -1430,6 +1458,9 @@ async function readJSONL(filePath) {
|
|
|
1430
1458
|
// src/services/monolith-service.ts
|
|
1431
1459
|
var MonolithService = class {
|
|
1432
1460
|
async sendEvent(event) {
|
|
1461
|
+
if (!ENGINE_ENV.WORKSPACE_ID) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1433
1464
|
try {
|
|
1434
1465
|
const response = await fetch(`${ENGINE_ENV.MONOLITH_URL}/v1/engine/webhook`, {
|
|
1435
1466
|
method: "POST",
|
|
@@ -2321,7 +2352,7 @@ var ClaudeManager = class extends CodingAgentManager {
|
|
|
2321
2352
|
// src/managers/codex-manager.ts
|
|
2322
2353
|
import { Codex } from "@openai/codex-sdk";
|
|
2323
2354
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2324
|
-
import { readdir as
|
|
2355
|
+
import { readdir as readdir3, stat as stat2, writeFile as writeFile5, mkdir as mkdir7, readFile as readFile5 } from "fs/promises";
|
|
2325
2356
|
import { existsSync as existsSync5 } from "fs";
|
|
2326
2357
|
import { join as join9 } from "path";
|
|
2327
2358
|
import { homedir as homedir8 } from "os";
|
|
@@ -2563,7 +2594,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
2563
2594
|
}
|
|
2564
2595
|
async findFileInDirectory(directory, threadId) {
|
|
2565
2596
|
try {
|
|
2566
|
-
const files = await
|
|
2597
|
+
const files = await readdir3(directory);
|
|
2567
2598
|
for (const file of files) {
|
|
2568
2599
|
if (file.endsWith(".jsonl") && file.includes(threadId)) {
|
|
2569
2600
|
const fullPath = join9(directory, file);
|
|
@@ -2965,7 +2996,7 @@ import { Hono } from "hono";
|
|
|
2965
2996
|
import { z } from "zod";
|
|
2966
2997
|
|
|
2967
2998
|
// src/services/plan-service.ts
|
|
2968
|
-
import { readdir as
|
|
2999
|
+
import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
|
|
2969
3000
|
import { homedir as homedir10 } from "os";
|
|
2970
3001
|
import { basename, join as join11 } from "path";
|
|
2971
3002
|
var PLAN_DIRECTORIES = [
|
|
@@ -2983,7 +3014,7 @@ var PlanService = class {
|
|
|
2983
3014
|
const planNames = /* @__PURE__ */ new Set();
|
|
2984
3015
|
for (const directory of PLAN_DIRECTORIES) {
|
|
2985
3016
|
try {
|
|
2986
|
-
const entries = await
|
|
3017
|
+
const entries = await readdir4(directory, { withFileTypes: true });
|
|
2987
3018
|
for (const entry of entries) {
|
|
2988
3019
|
if (!entry.isFile()) {
|
|
2989
3020
|
continue;
|
|
@@ -3017,15 +3048,15 @@ var PlanService = class {
|
|
|
3017
3048
|
var planService = new PlanService();
|
|
3018
3049
|
|
|
3019
3050
|
// src/services/warm-hooks-service.ts
|
|
3020
|
-
import { execFile } from "child_process";
|
|
3021
|
-
import { promisify as
|
|
3051
|
+
import { execFile as execFile2 } from "child_process";
|
|
3052
|
+
import { promisify as promisify3 } from "util";
|
|
3022
3053
|
import { readFile as readFile8 } from "fs/promises";
|
|
3023
3054
|
import { join as join12 } from "path";
|
|
3024
|
-
var
|
|
3055
|
+
var execFileAsync2 = promisify3(execFile2);
|
|
3025
3056
|
async function installSkill(params) {
|
|
3026
3057
|
const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
|
|
3027
3058
|
try {
|
|
3028
|
-
const { stdout, stderr } = await
|
|
3059
|
+
const { stdout, stderr } = await execFileAsync2(
|
|
3029
3060
|
"npx",
|
|
3030
3061
|
["skills", "add", params.source, "--all", "--global"],
|
|
3031
3062
|
{
|
|
@@ -3061,7 +3092,7 @@ async function installSkill(params) {
|
|
|
3061
3092
|
async function executeHookScript(params) {
|
|
3062
3093
|
const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
|
|
3063
3094
|
try {
|
|
3064
|
-
const { stdout, stderr } = await
|
|
3095
|
+
const { stdout, stderr } = await execFileAsync2("bash", ["-lc", params.content], {
|
|
3065
3096
|
cwd: params.cwd,
|
|
3066
3097
|
timeout,
|
|
3067
3098
|
maxBuffer: 1024 * 1024,
|
|
@@ -3132,7 +3163,7 @@ async function runWarmHooks(params) {
|
|
|
3132
3163
|
});
|
|
3133
3164
|
outputBlocks.push(installResult.output);
|
|
3134
3165
|
if (installResult.exitCode === 0) {
|
|
3135
|
-
await environmentDetailsService.
|
|
3166
|
+
await environmentDetailsService.trackSkillsInstalled([source]);
|
|
3136
3167
|
}
|
|
3137
3168
|
if (installResult.exitCode !== 0) {
|
|
3138
3169
|
await environmentDetailsService.setGlobalWarmHook("no", installResult.output);
|
|
@@ -3450,18 +3481,6 @@ function createV1Routes(deps) {
|
|
|
3450
3481
|
);
|
|
3451
3482
|
}
|
|
3452
3483
|
});
|
|
3453
|
-
app2.post("/environment/track", async (c) => {
|
|
3454
|
-
try {
|
|
3455
|
-
const body = await c.req.json();
|
|
3456
|
-
const details = await environmentDetailsService.track(body);
|
|
3457
|
-
return c.json(details);
|
|
3458
|
-
} catch (error) {
|
|
3459
|
-
return c.json(
|
|
3460
|
-
jsonError("Failed to track environment details", error instanceof Error ? error.message : "Unknown error"),
|
|
3461
|
-
500
|
|
3462
|
-
);
|
|
3463
|
-
}
|
|
3464
|
-
});
|
|
3465
3484
|
app2.post("/warm-hooks/run", async (c) => {
|
|
3466
3485
|
try {
|
|
3467
3486
|
const body = await c.req.json();
|
|
@@ -3674,7 +3693,11 @@ serve(
|
|
|
3674
3693
|
port
|
|
3675
3694
|
},
|
|
3676
3695
|
async (info) => {
|
|
3677
|
-
|
|
3696
|
+
if (IS_WARMING_MODE) {
|
|
3697
|
+
console.log(`Replicas Engine running on port ${info.port} (warming mode)`);
|
|
3698
|
+
} else {
|
|
3699
|
+
console.log(`Replicas Engine running on port ${info.port}`);
|
|
3700
|
+
}
|
|
3678
3701
|
const gitResult = await gitService.initializeGitRepository();
|
|
3679
3702
|
if (!gitResult.success) {
|
|
3680
3703
|
console.warn(`Git initialization warning: ${gitResult.error}`);
|
|
@@ -3682,9 +3705,11 @@ serve(
|
|
|
3682
3705
|
await replicasConfigService.initialize();
|
|
3683
3706
|
await chatService.initialize();
|
|
3684
3707
|
engineReady = true;
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3708
|
+
if (!IS_WARMING_MODE) {
|
|
3709
|
+
await githubTokenManager.start();
|
|
3710
|
+
await claudeTokenManager.start();
|
|
3711
|
+
await codexTokenManager.start();
|
|
3712
|
+
}
|
|
3688
3713
|
const repos = await gitService.listRepos();
|
|
3689
3714
|
await eventService.publish({
|
|
3690
3715
|
id: randomUUID4(),
|