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.
Files changed (2) hide show
  1. package/dist/src/index.js +190 -165
  2. 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 = readEnv("HOME") ?? homedir();
49
- return {
50
- // Required boot env.
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
- // Engine-consumed optional values.
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
- 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)
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.getSkipReasonForRun();
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.getSkipReasonForRun()) {
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 REPLICAS_DIR = join5(homedir4(), ".replicas");
915
- var ENVIRONMENT_DETAILS_FILE = join5(REPLICAS_DIR, "environment-details.json");
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 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() {
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 getCodexAuthMethod() {
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 getLiveDetails(current) {
963
- const claudeAuthMethod = getClaudeAuthMethod();
964
- const codexAuthMethod = getCodexAuthMethod();
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
- ...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),
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
- claudeAuthMethod,
973
- codexAuthMethod
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 mergeUnique(current, incoming) {
977
- if (!incoming || incoming.length === 0) {
978
- return current;
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
- return Array.from(/* @__PURE__ */ new Set([...current, ...incoming]));
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((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"
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
- 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,
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 this.writeDetails({
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 current = await this.readDetails();
1015
- return getLiveDetails(current);
1016
- }
1017
- async track(update) {
1018
- const current = await this.readDetails();
1019
- const next = {
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
- 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,
1072
+ ...staticState,
1073
+ ...persisted,
1074
+ filesUploaded,
1031
1075
  lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
1032
1076
  };
1033
- await this.writeDetails(next);
1034
- return next;
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 this.track({
1038
- globalWarmHookCompleted: createExecutionItem(status, details)
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 this.track({
1043
- repositories: [{
1044
- repositoryName,
1045
- warmHookCompleted: status,
1046
- startHookCompleted: "n/a"
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 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");
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 = promisify(exec);
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 readdir2, stat as stat2, writeFile as writeFile5, mkdir as mkdir7, readFile as readFile5 } from "fs/promises";
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 readdir2(directory);
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 readdir3, readFile as readFile7 } from "fs/promises";
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 readdir3(directory, { withFileTypes: true });
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 promisify2 } from "util";
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 execFileAsync = promisify2(execFile);
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 execFileAsync(
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 execFileAsync("bash", ["-lc", params.content], {
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.track({ skillsInstalled: [source] });
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
- console.log(`Replicas Engine running on port ${info.port}`);
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
- await githubTokenManager.start();
3686
- await claudeTokenManager.start();
3687
- await codexTokenManager.start();
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(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",