replicas-engine 0.1.51 → 0.1.53

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.
Files changed (2) hide show
  1. package/dist/src/index.js +169 -231
  2. package/package.json +1 -2
package/dist/src/index.js CHANGED
@@ -2,7 +2,6 @@
2
2
  import "./chunk-ZXMDA7VB.js";
3
3
 
4
4
  // src/index.ts
5
- import "dotenv/config";
6
5
  import { serve } from "@hono/node-server";
7
6
  import { Hono as Hono2 } from "hono";
8
7
  import { readFile as readFile9 } from "fs/promises";
@@ -44,35 +43,36 @@ function requireValidURL(value, name) {
44
43
  throw new Error(`Invalid engine environment: ${name} must be a valid URL`);
45
44
  }
46
45
  }
46
+ var IS_WARMING_MODE = process.argv.includes("--warming");
47
47
  function loadEngineEnv() {
48
- const HOME_DIR = readEnv("HOME") ?? homedir();
49
- return {
50
- // Required boot env.
48
+ const HOME_DIR = homedir();
49
+ const env = {
50
+ // Defined: always available
51
51
  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
52
  REPLICAS_ENGINE_PORT: parsePort(readEnv("REPLICAS_ENGINE_PORT")),
53
+ MONOLITH_URL: requireValidURL(requireDefined(readEnv("MONOLITH_URL"), "MONOLITH_URL"), "MONOLITH_URL"),
54
+ GH_TOKEN: readEnv("GH_TOKEN"),
56
55
  HOME_DIR,
57
56
  WORKSPACE_ROOT: join(HOME_DIR, "workspaces"),
58
- // Engine-consumed optional values.
57
+ // Runtime: may not be set during warming
58
+ WORKSPACE_ID: readEnv("WORKSPACE_ID"),
59
59
  WORKSPACE_NAME: readEnv("WORKSPACE_NAME"),
60
60
  LINEAR_SESSION_ID: readEnv("LINEAR_SESSION_ID"),
61
61
  LINEAR_ACCESS_TOKEN: readEnv("LINEAR_ACCESS_TOKEN"),
62
62
  SLACK_BOT_TOKEN: readEnv("SLACK_BOT_TOKEN"),
63
- GH_TOKEN: readEnv("GH_TOKEN"),
64
- // Ambient runtime values
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)
63
+ SLACK_CHANNEL_ID: readEnv("SLACK_CHANNEL_ID"),
64
+ SLACK_THREAD_TS: readEnv("SLACK_THREAD_TS"),
67
65
  ANTHROPIC_API_KEY: readEnv("ANTHROPIC_API_KEY"),
68
66
  OPENAI_API_KEY: readEnv("OPENAI_API_KEY"),
69
67
  CLAUDE_CODE_USE_BEDROCK: readEnv("CLAUDE_CODE_USE_BEDROCK"),
70
68
  AWS_ACCESS_KEY_ID: readEnv("AWS_ACCESS_KEY_ID"),
71
69
  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")
70
+ AWS_REGION: readEnv("AWS_REGION")
75
71
  };
72
+ if (!IS_WARMING_MODE && !env.WORKSPACE_ID) {
73
+ console.error("WORKSPACE_ID is not set \u2014 this is required in normal (non-warming) mode");
74
+ }
75
+ return env;
76
76
  }
77
77
  var ENGINE_ENV = loadEngineEnv();
78
78
 
@@ -96,7 +96,7 @@ var BaseRefreshManager = class {
96
96
  if (this.intervalHandle) {
97
97
  return;
98
98
  }
99
- const skipReason = this.getSkipReasonForRun();
99
+ const skipReason = this.getSkipReason();
100
100
  if (skipReason) {
101
101
  console.log(`[${this.managerName}] Skipping: ${skipReason}`);
102
102
  return;
@@ -127,21 +127,17 @@ var BaseRefreshManager = class {
127
127
  return null;
128
128
  }
129
129
  getRuntimeConfig() {
130
+ if (!ENGINE_ENV.WORKSPACE_ID) {
131
+ return null;
132
+ }
130
133
  return {
131
134
  monolithUrl: ENGINE_ENV.MONOLITH_URL,
132
135
  workspaceId: ENGINE_ENV.WORKSPACE_ID,
133
136
  engineSecret: ENGINE_ENV.REPLICAS_ENGINE_SECRET
134
137
  };
135
138
  }
136
- getSkipReasonForRun() {
137
- const skipReason = this.getSkipReason();
138
- if (skipReason) {
139
- return skipReason;
140
- }
141
- return null;
142
- }
143
139
  async refreshOnce() {
144
- if (this.getSkipReasonForRun()) {
140
+ if (this.getSkipReason()) {
145
141
  return;
146
142
  }
147
143
  const config = this.getRuntimeConfig();
@@ -829,11 +825,13 @@ var EngineLogger = class {
829
825
  };
830
826
  var engineLogger = new EngineLogger();
831
827
 
832
- // src/services/environment-details-service.ts
833
- import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
834
- import { existsSync as existsSync3 } from "fs";
835
- import { homedir as homedir4 } from "os";
836
- import { join as join5 } from "path";
828
+ // src/services/replicas-config-service.ts
829
+ import { readFile as readFile3, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
830
+ import { existsSync as existsSync4 } from "fs";
831
+ import { join as join6 } from "path";
832
+ import { homedir as homedir5 } from "os";
833
+ import { exec } from "child_process";
834
+ import { promisify as promisify2 } from "util";
837
835
 
838
836
  // ../shared/src/sandbox.ts
839
837
  var SANDBOX_LIFECYCLE = {
@@ -842,6 +840,14 @@ var SANDBOX_LIFECYCLE = {
842
840
  AUTO_DELETE_MINUTES: -1,
843
841
  SSH_TOKEN_EXPIRATION_MINUTES: 3 * 60
844
842
  };
843
+ var SANDBOX_PATHS = {
844
+ HOME_DIR: "/home/ubuntu",
845
+ WORKSPACES_DIR: "/home/ubuntu/workspaces",
846
+ REPLICAS_DIR: "/home/ubuntu/.replicas",
847
+ REPLICAS_FILES_DIR: "/home/ubuntu/.replicas/files",
848
+ REPLICAS_FILES_DISPLAY_DIR: "~/.replicas/files",
849
+ REPLICAS_RUNTIME_ENV_FILE: "/home/ubuntu/.replicas/runtime-env.sh"
850
+ };
845
851
 
846
852
  // ../shared/src/warm-hooks.ts
847
853
  var DEFAULT_WARM_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
@@ -911,34 +917,18 @@ var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
911
917
  var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
912
918
 
913
919
  // src/services/environment-details-service.ts
914
- var REPLICAS_DIR = join5(homedir4(), ".replicas");
915
- var ENVIRONMENT_DETAILS_FILE = join5(REPLICAS_DIR, "environment-details.json");
920
+ import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
921
+ import { existsSync as existsSync3 } from "fs";
922
+ import { homedir as homedir4 } from "os";
923
+ import { join as join5 } from "path";
924
+ import { execFile } from "child_process";
925
+ import { promisify } from "util";
926
+ var execFileAsync = promisify(execFile);
927
+ var REPLICAS_DIR = SANDBOX_PATHS.REPLICAS_DIR;
928
+ var DETAILS_FILE = join5(REPLICAS_DIR, "environment-details.json");
916
929
  var CLAUDE_CREDENTIALS_PATH = join5(homedir4(), ".claude", ".credentials.json");
917
930
  var CODEX_AUTH_PATH = join5(homedir4(), ".codex", "auth.json");
918
- function createExecutionItem(status, details) {
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() {
931
+ function detectClaudeAuthMethod() {
942
932
  if (existsSync3(CLAUDE_CREDENTIALS_PATH)) {
943
933
  return "oauth";
944
934
  }
@@ -950,7 +940,7 @@ function getClaudeAuthMethod() {
950
940
  }
951
941
  return "none";
952
942
  }
953
- function getCodexAuthMethod() {
943
+ function detectCodexAuthMethod() {
954
944
  if (existsSync3(CODEX_AUTH_PATH)) {
955
945
  return "oauth";
956
946
  }
@@ -959,133 +949,129 @@ function getCodexAuthMethod() {
959
949
  }
960
950
  return "none";
961
951
  }
962
- function getLiveDetails(current) {
963
- const claudeAuthMethod = getClaudeAuthMethod();
964
- const codexAuthMethod = getCodexAuthMethod();
965
- return {
966
- ...current,
967
- engineVersion: REPLICAS_ENGINE_VERSION,
968
- linearAccessConfigured: Boolean(ENGINE_ENV.LINEAR_SESSION_ID || ENGINE_ENV.LINEAR_ACCESS_TOKEN),
969
- slackAccessConfigured: Boolean(ENGINE_ENV.SLACK_BOT_TOKEN),
970
- githubAccessConfigured: Boolean(ENGINE_ENV.GH_TOKEN),
971
- githubCredentialsConfigured: Boolean(ENGINE_ENV.GH_TOKEN),
972
- claudeAuthMethod,
973
- codexAuthMethod
974
- };
975
- }
976
- function mergeUnique(current, incoming) {
977
- if (!incoming || incoming.length === 0) {
978
- return current;
952
+ async function detectGitIdentityConfigured() {
953
+ try {
954
+ const { stdout } = await execFileAsync("git", ["config", "--global", "user.name"]);
955
+ return stdout.trim().length > 0;
956
+ } catch {
957
+ return false;
979
958
  }
980
- return Array.from(/* @__PURE__ */ new Set([...current, ...incoming]));
981
959
  }
982
960
  function upsertRepositoryStatus(current, incoming) {
983
- const byName = new Map(current.map((repository) => [repository.repositoryName, repository]));
984
- for (const repository of incoming) {
985
- const existing = byName.get(repository.repositoryName);
986
- byName.set(repository.repositoryName, {
987
- repositoryName: repository.repositoryName,
988
- warmHookCompleted: repository.warmHookCompleted !== "n/a" ? repository.warmHookCompleted : existing?.warmHookCompleted ?? "n/a",
989
- startHookCompleted: repository.startHookCompleted !== "n/a" ? repository.startHookCompleted : existing?.startHookCompleted ?? "n/a"
961
+ const byName = new Map(current.map((r) => [r.repositoryName, r]));
962
+ for (const repo of incoming) {
963
+ const existing = byName.get(repo.repositoryName);
964
+ byName.set(repo.repositoryName, {
965
+ repositoryName: repo.repositoryName,
966
+ warmHookCompleted: repo.warmHookCompleted !== "n/a" ? repo.warmHookCompleted : existing?.warmHookCompleted ?? "n/a",
967
+ startHookCompleted: repo.startHookCompleted !== "n/a" ? repo.startHookCompleted : existing?.startHookCompleted ?? "n/a"
990
968
  });
991
969
  }
992
970
  return [...byName.values()].sort((a, b) => a.repositoryName.localeCompare(b.repositoryName));
993
971
  }
994
- var EnvironmentDetailsService = class {
995
- async initialize() {
996
- const current = await this.readDetails();
997
- const repositories = await gitService.listRepositories();
998
- const merged = upsertRepositoryStatus(
999
- current.repositories,
1000
- repositories.map((repository) => ({
1001
- repositoryName: repository.name,
1002
- warmHookCompleted: "n/a",
1003
- startHookCompleted: "n/a"
1004
- }))
1005
- );
1006
- await this.writeDetails({
1007
- ...current,
1008
- engineVersion: REPLICAS_ENGINE_VERSION,
1009
- repositories: merged,
1010
- lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
1011
- });
972
+ function createDefaultDetails() {
973
+ return {
974
+ engineVersion: REPLICAS_ENGINE_VERSION,
975
+ globalWarmHookCompleted: { status: "n/a", details: null },
976
+ repositories: [],
977
+ filesUploaded: [],
978
+ envVarsSet: [],
979
+ skillsInstalled: [],
980
+ gitIdentityConfigured: false,
981
+ githubCredentialsConfigured: false,
982
+ linearAccessConfigured: false,
983
+ slackAccessConfigured: false,
984
+ githubAccessConfigured: false,
985
+ claudeAuthMethod: "none",
986
+ codexAuthMethod: "none",
987
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
988
+ };
989
+ }
990
+ async function readDetails() {
991
+ try {
992
+ if (!existsSync3(DETAILS_FILE)) {
993
+ return createDefaultDetails();
994
+ }
995
+ const raw = await readFile2(DETAILS_FILE, "utf-8");
996
+ const parsed = JSON.parse(raw);
997
+ return { ...createDefaultDetails(), ...parsed };
998
+ } catch {
999
+ return createDefaultDetails();
1012
1000
  }
1001
+ }
1002
+ async function writeDetails(details) {
1003
+ await mkdir3(REPLICAS_DIR, { recursive: true });
1004
+ await writeFile3(DETAILS_FILE, `${JSON.stringify(details, null, 2)}
1005
+ `, "utf-8");
1006
+ }
1007
+ var EnvironmentDetailsService = class {
1013
1008
  async getDetails() {
1014
- const current = await this.readDetails();
1015
- return getLiveDetails(current);
1016
- }
1017
- async track(update) {
1018
- const current = await this.readDetails();
1019
- const next = {
1020
- ...current,
1021
- engineVersion: REPLICAS_ENGINE_VERSION,
1022
- globalWarmHookCompleted: update.globalWarmHookCompleted ?? current.globalWarmHookCompleted,
1023
- repositories: update.repositories ? upsertRepositoryStatus(current.repositories, update.repositories) : current.repositories,
1024
- filesUploaded: mergeUnique(current.filesUploaded, update.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,
1031
- lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
1032
- };
1033
- await this.writeDetails(next);
1034
- return next;
1009
+ const [details, repositories, gitIdentityConfigured] = await Promise.all([
1010
+ readDetails(),
1011
+ gitService.listRepositories(),
1012
+ detectGitIdentityConfigured()
1013
+ ]);
1014
+ details.engineVersion = REPLICAS_ENGINE_VERSION;
1015
+ details.claudeAuthMethod = detectClaudeAuthMethod();
1016
+ details.codexAuthMethod = detectCodexAuthMethod();
1017
+ details.gitIdentityConfigured = gitIdentityConfigured;
1018
+ details.githubAccessConfigured = Boolean(ENGINE_ENV.GH_TOKEN);
1019
+ details.githubCredentialsConfigured = Boolean(ENGINE_ENV.GH_TOKEN);
1020
+ details.linearAccessConfigured = Boolean(ENGINE_ENV.LINEAR_SESSION_ID || ENGINE_ENV.LINEAR_ACCESS_TOKEN);
1021
+ details.slackAccessConfigured = Boolean(ENGINE_ENV.SLACK_BOT_TOKEN);
1022
+ const freshRepos = repositories.map((repo) => ({
1023
+ repositoryName: repo.name,
1024
+ warmHookCompleted: "n/a",
1025
+ startHookCompleted: "n/a"
1026
+ }));
1027
+ details.repositories = upsertRepositoryStatus(details.repositories, freshRepos);
1028
+ return details;
1029
+ }
1030
+ async trackEnvironment(req) {
1031
+ const details = await readDetails();
1032
+ if (req.filesUploaded) {
1033
+ details.filesUploaded = req.filesUploaded;
1034
+ }
1035
+ if (req.envVarsSet) {
1036
+ details.envVarsSet = req.envVarsSet;
1037
+ }
1038
+ if (req.skillsInstalled) {
1039
+ details.skillsInstalled = req.skillsInstalled;
1040
+ }
1041
+ details.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
1042
+ await writeDetails(details);
1035
1043
  }
1036
1044
  async setGlobalWarmHook(status, details) {
1037
- await this.track({
1038
- globalWarmHookCompleted: createExecutionItem(status, details)
1039
- });
1045
+ const current = await readDetails();
1046
+ current.globalWarmHookCompleted = { status, details: details ?? null };
1047
+ current.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
1048
+ await writeDetails(current);
1040
1049
  }
1041
1050
  async setRepositoryWarmHook(repositoryName, status) {
1042
- await this.track({
1043
- repositories: [{
1044
- repositoryName,
1045
- warmHookCompleted: status,
1046
- startHookCompleted: "n/a"
1047
- }]
1048
- });
1051
+ const current = await readDetails();
1052
+ current.repositories = upsertRepositoryStatus(current.repositories, [{
1053
+ repositoryName,
1054
+ warmHookCompleted: status,
1055
+ startHookCompleted: "n/a"
1056
+ }]);
1057
+ current.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
1058
+ await writeDetails(current);
1049
1059
  }
1050
1060
  async setRepositoryStartHook(repositoryName, status) {
1051
- await this.track({
1052
- repositories: [{
1053
- repositoryName,
1054
- warmHookCompleted: "n/a",
1055
- startHookCompleted: status
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");
1061
+ const current = await readDetails();
1062
+ current.repositories = upsertRepositoryStatus(current.repositories, [{
1063
+ repositoryName,
1064
+ warmHookCompleted: "n/a",
1065
+ startHookCompleted: status
1066
+ }]);
1067
+ current.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
1068
+ await writeDetails(current);
1077
1069
  }
1078
1070
  };
1079
1071
  var environmentDetailsService = new EnvironmentDetailsService();
1080
1072
 
1081
1073
  // src/services/replicas-config-service.ts
1082
- import { readFile as readFile3, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
1083
- import { existsSync as existsSync4 } from "fs";
1084
- import { join as join6 } from "path";
1085
- import { homedir as homedir5 } from "os";
1086
- import { exec } from "child_process";
1087
- import { promisify } from "util";
1088
- var execAsync = promisify(exec);
1074
+ var execAsync = promisify2(exec);
1089
1075
  var START_HOOKS_LOG = join6(homedir5(), ".replicas", "startHooks.log");
1090
1076
  var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
1091
1077
  Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
@@ -1430,6 +1416,9 @@ async function readJSONL(filePath) {
1430
1416
  // src/services/monolith-service.ts
1431
1417
  var MonolithService = class {
1432
1418
  async sendEvent(event) {
1419
+ if (!ENGINE_ENV.WORKSPACE_ID) {
1420
+ return;
1421
+ }
1433
1422
  try {
1434
1423
  const response = await fetch(`${ENGINE_ENV.MONOLITH_URL}/v1/engine/webhook`, {
1435
1424
  method: "POST",
@@ -3017,51 +3006,15 @@ var PlanService = class {
3017
3006
  var planService = new PlanService();
3018
3007
 
3019
3008
  // src/services/warm-hooks-service.ts
3020
- import { execFile } from "child_process";
3021
- import { promisify as promisify2 } from "util";
3009
+ import { execFile as execFile2 } from "child_process";
3010
+ import { promisify as promisify3 } from "util";
3022
3011
  import { readFile as readFile8 } from "fs/promises";
3023
3012
  import { join as join12 } from "path";
3024
- var execFileAsync = promisify2(execFile);
3025
- async function installSkill(params) {
3026
- const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
3027
- try {
3028
- const { stdout, stderr } = await execFileAsync(
3029
- "npx",
3030
- ["skills", "add", params.source, "--all", "--global"],
3031
- {
3032
- cwd: params.cwd,
3033
- timeout,
3034
- maxBuffer: 1024 * 1024,
3035
- env: process.env
3036
- }
3037
- );
3038
- const combined = [`$ npx skills add ${params.source} --all --global`, stdout ?? "", stderr ?? ""].filter(Boolean).join("\n");
3039
- return {
3040
- exitCode: 0,
3041
- output: truncateWarmHookOutput(combined),
3042
- timedOut: false
3043
- };
3044
- } catch (error) {
3045
- const execError = error;
3046
- const timedOut = execError.signal === "SIGTERM" || execError.killed === true;
3047
- const exitCode = typeof execError.code === "number" ? execError.code : 1;
3048
- const combined = [
3049
- `$ npx skills add ${params.source} --all --global`,
3050
- execError.stdout ?? "",
3051
- execError.stderr ?? "",
3052
- execError.message
3053
- ].filter(Boolean).join("\n");
3054
- return {
3055
- exitCode,
3056
- output: truncateWarmHookOutput(combined),
3057
- timedOut
3058
- };
3059
- }
3060
- }
3013
+ var execFileAsync2 = promisify3(execFile2);
3061
3014
  async function executeHookScript(params) {
3062
3015
  const timeout = clampWarmHookTimeoutMs(params.timeoutMs);
3063
3016
  try {
3064
- const { stdout, stderr } = await execFileAsync("bash", ["-lc", params.content], {
3017
+ const { stdout, stderr } = await execFileAsync2("bash", ["-lc", params.content], {
3065
3018
  cwd: params.cwd,
3066
3019
  timeout,
3067
3020
  maxBuffer: 1024 * 1024,
@@ -3122,27 +3075,7 @@ async function collectRepoWarmHooks() {
3122
3075
  }
3123
3076
  async function runWarmHooks(params) {
3124
3077
  const outputBlocks = [];
3125
- const skills = params.skills ?? [];
3126
3078
  const orgHook = params.organizationWarmHook?.trim();
3127
- for (const source of skills) {
3128
- const installResult = await installSkill({
3129
- source,
3130
- cwd: gitService.getWorkspaceRoot(),
3131
- timeoutMs: params.timeoutMs
3132
- });
3133
- outputBlocks.push(installResult.output);
3134
- if (installResult.exitCode === 0) {
3135
- await environmentDetailsService.track({ skillsInstalled: [source] });
3136
- }
3137
- if (installResult.exitCode !== 0) {
3138
- await environmentDetailsService.setGlobalWarmHook("no", installResult.output);
3139
- return {
3140
- exitCode: installResult.exitCode,
3141
- output: outputBlocks.join("\n\n"),
3142
- timedOut: installResult.timedOut
3143
- };
3144
- }
3145
- }
3146
3079
  if (orgHook) {
3147
3080
  const orgResult = await executeHookScript({
3148
3081
  label: "org-warm-hook",
@@ -3453,11 +3386,11 @@ function createV1Routes(deps) {
3453
3386
  app2.post("/environment/track", async (c) => {
3454
3387
  try {
3455
3388
  const body = await c.req.json();
3456
- const details = await environmentDetailsService.track(body);
3457
- return c.json(details);
3389
+ await environmentDetailsService.trackEnvironment(body);
3390
+ return c.json({ success: true });
3458
3391
  } catch (error) {
3459
3392
  return c.json(
3460
- jsonError("Failed to track environment details", error instanceof Error ? error.message : "Unknown error"),
3393
+ jsonError("Failed to track environment", error instanceof Error ? error.message : "Unknown error"),
3461
3394
  500
3462
3395
  );
3463
3396
  }
@@ -3489,7 +3422,6 @@ function createV1Routes(deps) {
3489
3422
  // src/index.ts
3490
3423
  await engineLogger.initialize();
3491
3424
  await eventService.initialize();
3492
- await environmentDetailsService.initialize();
3493
3425
  var READY_MESSAGE = "========= REPLICAS WORKSPACE READY ==========";
3494
3426
  var COMPLETION_MESSAGE = "========= REPLICAS WORKSPACE INITIALIZATION COMPLETE ==========";
3495
3427
  function checkActiveSSHSessions() {
@@ -3674,7 +3606,11 @@ serve(
3674
3606
  port
3675
3607
  },
3676
3608
  async (info) => {
3677
- console.log(`Replicas Engine running on port ${info.port}`);
3609
+ if (IS_WARMING_MODE) {
3610
+ console.log(`Replicas Engine running on port ${info.port} (warming mode)`);
3611
+ } else {
3612
+ console.log(`Replicas Engine running on port ${info.port}`);
3613
+ }
3678
3614
  const gitResult = await gitService.initializeGitRepository();
3679
3615
  if (!gitResult.success) {
3680
3616
  console.warn(`Git initialization warning: ${gitResult.error}`);
@@ -3682,9 +3618,11 @@ serve(
3682
3618
  await replicasConfigService.initialize();
3683
3619
  await chatService.initialize();
3684
3620
  engineReady = true;
3685
- await githubTokenManager.start();
3686
- await claudeTokenManager.start();
3687
- await codexTokenManager.start();
3621
+ if (!IS_WARMING_MODE) {
3622
+ await githubTokenManager.start();
3623
+ await claudeTokenManager.start();
3624
+ await codexTokenManager.start();
3625
+ }
3688
3626
  const repos = await gitService.listRepos();
3689
3627
  await eventService.publish({
3690
3628
  id: randomUUID4(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -30,7 +30,6 @@
30
30
  "@anthropic-ai/claude-agent-sdk": "^0.2.41",
31
31
  "@hono/node-server": "^1.19.5",
32
32
  "@openai/codex-sdk": "^0.111.0",
33
- "dotenv": "^17.2.3",
34
33
  "hono": "^4.10.3",
35
34
  "smol-toml": "^1.6.0",
36
35
  "zod": "^4.0.0"